LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
From: "Jason A. Donenfeld" <Jason@zx2c4.com>
To: Dominik Brodowski <linux@dominikbrodowski.net>
Cc: Hsin-Yi Wang <hsinyi@chromium.org>,
	"Theodore Ts'o" <tytso@mit.edu>,
	"Ivan T. Ivanov" <iivanov@suse.de>,
	Ard Biesheuvel <ardb@kernel.org>,
	linux-efi@vger.kernel.org, LKML <linux-kernel@vger.kernel.org>
Subject: Re: [PATCH v5] random: fix crash on multiple early calls to add_bootloader_randomness()
Date: Tue, 7 Dec 2021 18:22:26 +0100	[thread overview]
Message-ID: <CAHmME9oonMxxfEq7sjSSYc7XPwzjW4e45JTbBCJ2hFEbL-tnyw@mail.gmail.com> (raw)
In-Reply-To: <Ya55SjgSkO+INcbb@light.dominikbrodowski.net>

Hi Dominik,

Thanks for the followup patch. Some comments below:

On Mon, Dec 6, 2021 at 9:58 PM Dominik Brodowski
<linux@dominikbrodowski.net> wrote:
> @@ -722,7 +722,8 @@ static void credit_entropy_bits(struct entropy_store *r, int nbits)
>         if (r == &input_pool) {
>                 int entropy_bits = entropy_count >> ENTROPY_SHIFT;
>
> -               if (crng_init < 2 && entropy_bits >= 128)
> +               if (crng_init < 2 && entropy_bits >= 128 &&
> +                   crng_global_init_time > 0)

crng_global_init_time is being used because it's set in
rand_initialize(), and rand_initialize() is called after workqueues
have been set up. Fair enough. But:
a) It's still set to `jiffies - 1` afterwards at runtime, via
ioctl(RNDRESEEDCRNG). I'm not sure if we want to mess around with
having a 1:2**32 chance of getting this at the unlucky 0 wraparound.
It seems like that kind of strategy generally works if unlikely
failure is tolerable, but it seems like that's not a game to play
here. There are a few other options:
b) Use another proxy for the state, like
rand_initialize()->primary_crng.state (ugly) or something better.
c) Add another static global state variable or static branch to random.c.
d) Actually conditionalize this on whether or not workqueues have been
init'd, which is *actually* the thing you care about, not whether
rand_initialize() has fired.

I think that of these, (d) seems the most correct. The thing you're
*actually* trying to prevent is that
`schedule_work(&numa_crng_init_work)` call being triggered before
workqueues are up. It looks like system_wq is made non-NULL by
workqueue_init_early(); perhaps you could get away with
conditionalizing it on that instead?

This also seems like a distinct state machine derp from the rest of
the patch, so I wonder if it could be separated out into a more
trivial patch, in case it does prove problematic somehow.

> +#ifdef CONFIG_RANDOM_TRUST_BOOTLOADER

Can you use IF_ENABLED() like the code that this replaces? Easier to
read and ensures syntax doesn't regress on less-used configurations.

>  void add_bootloader_randomness(const void *buf, unsigned int size)
> +       unsigned long time = random_get_entropy() ^ jiffies;
> +       unsigned long flags;
> +
> +       if (!crng_ready() && size) {
> +#ifdef CONFIG_RANDOM_TRUST_BOOTLOADER
> +               crng_fast_load(buf, size);
> +#else
> +               crng_slow_load(buf, size);
> +#endif /* CONFIG_RANDOM_TRUST_BOOTLOADER */
> +       }
> +
> +       spin_lock_irqsave(&input_pool.lock, flags);
> +       _mix_pool_bytes(&input_pool, buf, size);
> +       _mix_pool_bytes(&input_pool, &time, sizeof(time));
> +       spin_unlock_irqrestore(&input_pool.lock, flags);
> +
> +#ifdef CONFIG_RANDOM_TRUST_BOOTLOADER
> +       credit_entropy_bits(&input_pool, size * 8);
> +#endif
>  }
>  EXPORT_SYMBOL_GPL(add_bootloader_randomness);


Trying to understand this and compare it to what was here before. Two
cases: trustbootloader and !trustbootloader.

trustbootloader looks like this:

       unsigned long time = random_get_entropy() ^ jiffies;
       unsigned long flags;
       if (!crng_ready() && size)
               crng_fast_load(buf, size);
       spin_lock_irqsave(&input_pool.lock, flags);
       _mix_pool_bytes(&input_pool, buf, size);
       _mix_pool_bytes(&input_pool, &time, sizeof(time));
       spin_unlock_irqrestore(&input_pool.lock, flags);
       credit_entropy_bits(&input_pool, size * 8);

!trustbooloader looks like this:

       unsigned long time = random_get_entropy() ^ jiffies;
       unsigned long flags;
       if (!crng_ready() && size)
               crng_slow_load(buf, size);
       spin_lock_irqsave(&input_pool.lock, flags);
       _mix_pool_bytes(&input_pool, buf, size);
       _mix_pool_bytes(&input_pool, &time, sizeof(time));
       spin_unlock_irqrestore(&input_pool.lock, flags);

So this means !trustbootloader is the same as add_device_randomness
(with the call to trace_add_device_randomness removed). It'd probably
make sense to continue just branching to add_device_randomness on an
IS_ENABLED(), then, so it's more clear what's up, rather than open
coding the distinct paths and copying code around.

But trustbootloader is a different case. Here the differences appear to be:

1) crng_fast_load is now called for crng_init==1||crng_init==0 [via
crng_ready()] instead of for crng_init==0;
2) The function now continues onward after calling crng_fast_load
instead of returning;
3) The useless call to wait_event_interruptible is now skipped;
4) _mix_pool_bytes(random_get_entropy() ^ jiffies) is now called in
addition to _mix_pool_bytes(buf).

I'd now like to map (1), (2), (3), and (4) back to the rationale in
your commit message.

(2) seems to be related to:

> On the first call to add_hwgenerator_randomness(), crng_fast_load() is
> executed, and if the seed is long enough, crng_init will be set to 1.
> However, no entropy is currently credited for that, even though the
> name and description of CONFIG_RANDOM_TRUST_BOOTLOADER states otherwise.

But it seems like rather than going from:
       if (!ready) {
               crng_fast_load(buf, size);
               return;
       }
to:
       if (!ready)
               crng_fast_load(buf, size);
the actually corresponding fix would be to go instead to:
       if (!ready)
               crng_fast_load(buf, size);
       if (!ready)
               return;

(3) seems to be related to:

> wait_event_interruptible() (which makes no sense for the init process)

which is fine.

(1) and (4) I don't see justification for. Perhaps it's a mistake?

Regards,
Jason

  parent reply	other threads:[~2021-12-07 17:22 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-10-12  8:27 [PATCH] Revert "efi/random: Treat EFI_RNG_PROTOCOL output as bootloader randomness" Ivan T. Ivanov
2021-10-12  8:40 ` Dominik Brodowski
2021-10-13  7:30   ` [RESEND] " Ivan T. Ivanov
2021-10-13  7:50     ` Ard Biesheuvel
2021-10-13  8:05       ` Ivan T. Ivanov
2021-10-13  9:51       ` [RESEND] " Ivan T. Ivanov
2021-10-13  9:53         ` Ivan T. Ivanov
2021-10-13 13:23           ` Ivan T. Ivanov
2021-10-31  6:30   ` [PATCH] random: fix crash on multiple early calls to add_bootloader_randomness() Dominik Brodowski
2021-10-31 12:33     ` Ard Biesheuvel
2021-11-03  7:14       ` Dominik Brodowski
2021-11-03  7:27         ` Ard Biesheuvel
2021-11-05  6:04           ` Dominik Brodowski
2021-11-03  7:17   ` [PATCH v2] " Dominik Brodowski
2021-11-05  6:04   ` [PATCH v3] " Dominik Brodowski
2021-11-24 12:32     ` Ivan T. Ivanov
2021-12-02 11:35   ` [PATCH v3, resend] " Dominik Brodowski
2021-12-02 16:55     ` Jason A. Donenfeld
2021-12-03  7:58       ` [PATCH v4] " Dominik Brodowski
2021-12-03 15:39         ` Jason A. Donenfeld
2021-12-03 16:47           ` Jason A. Donenfeld
2021-12-03 17:01             ` Dominik Brodowski
2021-12-06  8:14           ` Ivan T. Ivanov
2021-12-30 18:05             ` Jason A. Donenfeld
2022-01-04 15:06               ` Ivan T. Ivanov
2021-12-06  5:42         ` Hsin-Yi Wang
2021-12-06 20:57           ` [PATCH v5] " Dominik Brodowski
2021-12-07  7:09             ` Hsin-Yi Wang
2021-12-07  7:14               ` Dominik Brodowski
2021-12-07 17:22             ` Jason A. Donenfeld [this message]
2021-12-20 14:48               ` Jason A. Donenfeld

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAHmME9oonMxxfEq7sjSSYc7XPwzjW4e45JTbBCJ2hFEbL-tnyw@mail.gmail.com \
    --to=jason@zx2c4.com \
    --cc=ardb@kernel.org \
    --cc=hsinyi@chromium.org \
    --cc=iivanov@suse.de \
    --cc=linux-efi@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux@dominikbrodowski.net \
    --cc=tytso@mit.edu \
    --subject='Re: [PATCH v5] random: fix crash on multiple early calls to add_bootloader_randomness()' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).