lib/vsprintf.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-)
On RT, we can't call get_random_bytes() from inside of the raw locks
that callers of vsprintf might take, because get_random_bytes() takes
normal spinlocks. So on those RT systems, defer the siphash key
generation to a worker.
Also, avoid using a static_branch, as this isn't the fast path.
Using static_branch_likely() to signal that ptr_key has been filled is a
bit much given that it is not a fast path.
Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Reported-by: Mike Galbraith <efault@gmx.de>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
---
Sebastian - feel free to take this and tweak it as needed. Sending this
mostly as something illustrative of what the "simpler" thing would be
that I had in mind. -Jason
lib/vsprintf.c | 35 ++++++++++++++++++++---------------
1 file changed, 20 insertions(+), 15 deletions(-)
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 3c1853a9d1c0..5a67f6f65ddc 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -750,37 +750,42 @@ static int __init debug_boot_weak_hash_enable(char *str)
}
early_param("debug_boot_weak_hash", debug_boot_weak_hash_enable);
-static DEFINE_STATIC_KEY_FALSE(filled_random_ptr_key);
+static bool filled_ptr_key __read_mostly;
+static siphash_key_t ptr_key __read_mostly;
-static void enable_ptr_key_workfn(struct work_struct *work)
+static void fill_ptr_key_workfn(struct work_struct *work)
{
- static_branch_enable(&filled_random_ptr_key);
+ if (READ_ONCE(filled_ptr_key))
+ return;
+ get_random_bytes(&ptr_key, sizeof(ptr_key));
+ /* Pairs with smp_rmb() before reading ptr_key. */
+ smp_wmb();
+ WRITE_ONCE(filled_ptr_key, true);
}
/* Maps a pointer to a 32 bit unique identifier. */
static inline int __ptr_to_hashval(const void *ptr, unsigned long *hashval_out)
{
- static siphash_key_t ptr_key __read_mostly;
unsigned long hashval;
- if (!static_branch_likely(&filled_random_ptr_key)) {
- static bool filled = false;
+ if (!READ_ONCE(filled_ptr_key)) {
static DEFINE_SPINLOCK(filling);
- static DECLARE_WORK(enable_ptr_key_work, enable_ptr_key_workfn);
unsigned long flags;
- if (!system_unbound_wq || !rng_is_initialized() ||
- !spin_trylock_irqsave(&filling, flags))
+ if (IS_ENABLED(CONFIG_PREEMPT_RT) && rng_is_initialized()) {
+ static DECLARE_WORK(fill_ptr_key_work, fill_ptr_key_workfn);
+ queue_work(system_unbound_wq, &fill_ptr_key_work);
return -EAGAIN;
-
- if (!filled) {
- get_random_bytes(&ptr_key, sizeof(ptr_key));
- queue_work(system_unbound_wq, &enable_ptr_key_work);
- filled = true;
}
+
+ if (!rng_is_initialized() || !spin_trylock_irqsave(&filling, flags))
+ return -EAGAIN;
+
+ fill_ptr_key_workfn(NULL);
spin_unlock_irqrestore(&filling, flags);
}
-
+ /* Pairs with smp_wmb() after writing ptr_key. */
+ smp_rmb();
#ifdef CONFIG_64BIT
hashval = (unsigned long)siphash_1u64((u64)ptr, &ptr_key);
--
2.35.1
On 2022-08-01 14:39:46 [+0200], Jason A. Donenfeld wrote: > On RT, we can't call get_random_bytes() from inside of the raw locks > that callers of vsprintf might take, because get_random_bytes() takes > normal spinlocks. So on those RT systems, defer the siphash key > generation to a worker. > > Also, avoid using a static_branch, as this isn't the fast path. > Using static_branch_likely() to signal that ptr_key has been filled is a > bit much given that it is not a fast path. > > Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de> > Reported-by: Mike Galbraith <efault@gmx.de> > Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com> > --- > Sebastian - feel free to take this and tweak it as needed. Sending this > mostly as something illustrative of what the "simpler" thing would be > that I had in mind. -Jason Can have the same behaviour regardless of CONFIG_PREEMPT_RT? Here lockdep _may_ yell with !RT because it is broken for RT. If we agree that we drop the first %p print here, can we do this on both (regardless of CONFIG_PREEMPT_RT)? Sebastian
Hi Sebastian, On Mon, Aug 01, 2022 at 02:46:35PM +0200, Sebastian Andrzej Siewior wrote: > On 2022-08-01 14:39:46 [+0200], Jason A. Donenfeld wrote: > > On RT, we can't call get_random_bytes() from inside of the raw locks > > that callers of vsprintf might take, because get_random_bytes() takes > > normal spinlocks. So on those RT systems, defer the siphash key > > generation to a worker. > > > > Also, avoid using a static_branch, as this isn't the fast path. > > Using static_branch_likely() to signal that ptr_key has been filled is a > > bit much given that it is not a fast path. > > > > Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de> > > Reported-by: Mike Galbraith <efault@gmx.de> > > Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com> > > --- > > Sebastian - feel free to take this and tweak it as needed. Sending this > > mostly as something illustrative of what the "simpler" thing would be > > that I had in mind. -Jason > > Can have the same behaviour regardless of CONFIG_PREEMPT_RT? Here > lockdep _may_ yell with !RT because it is broken for RT. > If we agree that we drop the first %p print here, can we do this on > both (regardless of CONFIG_PREEMPT_RT)? "Lockdep may yell" -- but this would be when lockdep is turned on to catch RT bugs, not to catch non-RT bugs. The actual bug only exists on RT. This is an RT problem. Stop pretending that this is a real issue outside of RT. It isn't. This is *only* an RT issue. So why would we make things worse for an issue that doesn't actually exist on non-RT? I too generally prefer having only one code path and not two. But the way this patch is written, the worker function just gets reused with a straight call on the non-RT case, so it doesn't actually require duplicating code. Jason
On 2022-08-01 15:36:32 [+0200], Jason A. Donenfeld wrote: > Hi Sebastian, Hi Jason, > > Can have the same behaviour regardless of CONFIG_PREEMPT_RT? Here > > lockdep _may_ yell with !RT because it is broken for RT. > > If we agree that we drop the first %p print here, can we do this on > > both (regardless of CONFIG_PREEMPT_RT)? > > "Lockdep may yell" -- but this would be when lockdep is turned on to > catch RT bugs, not to catch non-RT bugs. The actual bug only exists on > RT. This is an RT problem. Stop pretending that this is a real issue > outside of RT. It isn't. This is *only* an RT issue. So why would we > make things worse for an issue that doesn't actually exist on non-RT? You do remember the warning that poped up in random core with CONFIG_PROVE_RAW_LOCK_NESTING enabled? Where I said it does not affect !RT it just points out a RT problem in a !RT config? If you fix this with one code path for RT and another one for !RT then you will have this warning _if_ the caller has a raw_spinlock_t acquired. > I too generally prefer having only one code path and not two. But the > way this patch is written, the worker function just gets reused with a > straight call on the non-RT case, so it doesn't actually require > duplicating code. > Jason Sebastian
Hi Sebastian, On 8/1/22, Sebastian Andrzej Siewior <bigeasy@linutronix.de> wrote: > You do remember the warning that poped up in random core with > CONFIG_PROVE_RAW_LOCK_NESTING enabled? Where I said it does not affect > !RT it just points out a RT problem in a !RT config? > If you fix this with one code path for RT and another one for !RT then > you will have this warning _if_ the caller has a raw_spinlock_t > acquired. CONFIG_PROVE_RAW_LOCK_NESTING catches RT issues on non-RT. It doesn't catch non-RT issues. So lockdep isn't actually warning about something real as far as non-RT is concerned when CONFIG_PROVE_RAW_LOCK_NESTING is enabled. So probably this v4 patch should expand that condition to be: IS_ENABLED(CONFIG_PROVE_RAW_LOCK_NESTING) || IS_ENABLED(CONFIG_PREEMPT_RT) That's somewhat distasteful, I realize, but it *does* make the code actually match reality, which is maybe better. Jason
On RT, we can't call get_random_bytes() from inside of the raw locks
that callers of vsprintf might take, because get_random_bytes() takes
normal spinlocks. So on those RT systems, defer the siphash key
generation to a worker.
We also do the deferal for CONFIG_PROVE_RAW_LOCK_NESTING systems, which
catches RT issues on non-RT. Branching on CONFIG_PROVE_RAW_LOCK_NESTING
is partly awful, as it basically defeats the purpose of lockdep. But in
this case, it really generates incorrect splats.
Also, avoid using a static_branch, as this isn't the fast path.
Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Reported-by: Mike Galbraith <efault@gmx.de>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
---
Sebastian - again, feel free to take this and modify it as needed. Just
posting ideas... -Jason
lib/vsprintf.c | 36 +++++++++++++++++++++---------------
1 file changed, 21 insertions(+), 15 deletions(-)
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 3c1853a9d1c0..a2a61915eb6b 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -750,37 +750,43 @@ static int __init debug_boot_weak_hash_enable(char *str)
}
early_param("debug_boot_weak_hash", debug_boot_weak_hash_enable);
-static DEFINE_STATIC_KEY_FALSE(filled_random_ptr_key);
+static bool filled_ptr_key __read_mostly;
+static siphash_key_t ptr_key __read_mostly;
-static void enable_ptr_key_workfn(struct work_struct *work)
+static void fill_ptr_key_workfn(struct work_struct *work)
{
- static_branch_enable(&filled_random_ptr_key);
+ if (READ_ONCE(filled_ptr_key))
+ return;
+ get_random_bytes(&ptr_key, sizeof(ptr_key));
+ /* Pairs with smp_rmb() before reading ptr_key. */
+ smp_wmb();
+ WRITE_ONCE(filled_ptr_key, true);
}
/* Maps a pointer to a 32 bit unique identifier. */
static inline int __ptr_to_hashval(const void *ptr, unsigned long *hashval_out)
{
- static siphash_key_t ptr_key __read_mostly;
unsigned long hashval;
- if (!static_branch_likely(&filled_random_ptr_key)) {
- static bool filled = false;
+ if (!READ_ONCE(filled_ptr_key)) {
static DEFINE_SPINLOCK(filling);
- static DECLARE_WORK(enable_ptr_key_work, enable_ptr_key_workfn);
unsigned long flags;
- if (!system_unbound_wq || !rng_is_initialized() ||
- !spin_trylock_irqsave(&filling, flags))
+ if ((IS_ENABLED(CONFIG_PREEMPT_RT) || IS_ENABLED(CONFIG_PROVE_RAW_LOCK_NESTING)) &&
+ rng_is_initialized()) {
+ static DECLARE_WORK(fill_ptr_key_work, fill_ptr_key_workfn);
+ queue_work(system_unbound_wq, &fill_ptr_key_work);
return -EAGAIN;
-
- if (!filled) {
- get_random_bytes(&ptr_key, sizeof(ptr_key));
- queue_work(system_unbound_wq, &enable_ptr_key_work);
- filled = true;
}
+
+ if (!rng_is_initialized() || !spin_trylock_irqsave(&filling, flags))
+ return -EAGAIN;
+
+ fill_ptr_key_workfn(NULL);
spin_unlock_irqrestore(&filling, flags);
}
-
+ /* Pairs with smp_wmb() after writing ptr_key. */
+ smp_rmb();
#ifdef CONFIG_64BIT
hashval = (unsigned long)siphash_1u64((u64)ptr, &ptr_key);
--
2.35.1
On 2022-08-01 16:12:45 [+0200], Jason A. Donenfeld wrote: > Sebastian - again, feel free to take this and modify it as needed. Just > posting ideas... -Jason The plan is to make CONFIG_PROVE_RAW_LOCK_NESTING part of LOCKDEP once the issues have been sorted out. Sebastian
Hey again, On Mon, Aug 01, 2022 at 03:36:32PM +0200, Jason A. Donenfeld wrote: > Hi Sebastian, > > On Mon, Aug 01, 2022 at 02:46:35PM +0200, Sebastian Andrzej Siewior wrote: > > On 2022-08-01 14:39:46 [+0200], Jason A. Donenfeld wrote: > > > On RT, we can't call get_random_bytes() from inside of the raw locks > > > that callers of vsprintf might take, because get_random_bytes() takes > > > normal spinlocks. So on those RT systems, defer the siphash key > > > generation to a worker. > > > > > > Also, avoid using a static_branch, as this isn't the fast path. > > > Using static_branch_likely() to signal that ptr_key has been filled is a > > > bit much given that it is not a fast path. > > > > > > Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de> > > > Reported-by: Mike Galbraith <efault@gmx.de> > > > Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com> > > > --- > > > Sebastian - feel free to take this and tweak it as needed. Sending this > > > mostly as something illustrative of what the "simpler" thing would be > > > that I had in mind. -Jason > > > > Can have the same behaviour regardless of CONFIG_PREEMPT_RT? Here > > lockdep _may_ yell with !RT because it is broken for RT. > > If we agree that we drop the first %p print here, can we do this on > > both (regardless of CONFIG_PREEMPT_RT)? > > "Lockdep may yell" -- but this would be when lockdep is turned on to > catch RT bugs, not to catch non-RT bugs. The actual bug only exists on > RT. This is an RT problem. Stop pretending that this is a real issue > outside of RT. It isn't. This is *only* an RT issue. So why would we > make things worse for an issue that doesn't actually exist on non-RT? > > I too generally prefer having only one code path and not two. But the > way this patch is written, the worker function just gets reused with a > straight call on the non-RT case, so it doesn't actually require > duplicating code. > > Jason By the way, another option that would be fine with me would be to make random.c use all raw spinlocks. From a non-RT perspective, that wouldn't change the codegen at all, so it doesn't make a huge difference to me. From an RT perspective, it would presumably fix a lot of these issues, and enable randomness to be available in any context, which is maybe what we want anyway. From an RT-safety point of view, I suspect doing this might actually be okay, because the locks are only ever protecting operations that are fixed duration CPU-bound, like generating a chacha block or something, not waiting for some I/O. Thoughts on that? Jason
On 2022-08-01 15:44:12 [+0200], Jason A. Donenfeld wrote: > Hey again, Hi Jason, > By the way, another option that would be fine with me would be to make > random.c use all raw spinlocks. From a non-RT perspective, that wouldn't > change the codegen at all, so it doesn't make a huge difference to me. > From an RT perspective, it would presumably fix a lot of these issues, > and enable randomness to be available in any context, which is maybe > what we want anyway. From an RT-safety point of view, I suspect doing > this might actually be okay, because the locks are only ever protecting > operations that are fixed duration CPU-bound, like generating a chacha > block or something, not waiting for some I/O. > > Thoughts on that? That random-core change regarding random numbers broke lockdep, kasan (I think) and now printk's %p. Each one of them appears to be exceptional since we don't have _that_ many users asking for random numbers in atomic context. Making the locks raw would indeed solve all the issues at once. Last time I was looking into this, would include three locks and I tried to trigger the worst-case via "re-seed" and this was visible back then. After the rework you did back thinks looked good. > Jason Sebastian
Hi Sebastian, On Mon, Aug 1, 2022 at 4:25 PM Sebastian Andrzej Siewior <bigeasy@linutronix.de> wrote: > > On 2022-08-01 15:44:12 [+0200], Jason A. Donenfeld wrote: > > Hey again, > Hi Jason, > > > By the way, another option that would be fine with me would be to make > > random.c use all raw spinlocks. From a non-RT perspective, that wouldn't > > change the codegen at all, so it doesn't make a huge difference to me. > > From an RT perspective, it would presumably fix a lot of these issues, > > and enable randomness to be available in any context, which is maybe > > what we want anyway. From an RT-safety point of view, I suspect doing > > this might actually be okay, because the locks are only ever protecting > > operations that are fixed duration CPU-bound, like generating a chacha > > block or something, not waiting for some I/O. > > > > Thoughts on that? > > That random-core change regarding random numbers broke lockdep, kasan (I > think) and now printk's %p. Each one of them appears to be exceptional > since we don't have _that_ many users asking for random numbers in > atomic context. Actually, the printk %p case was caused by something different than the other. This used to be initialized with a clunky notifier callback mechanism, which I got rid of, replacing it with this direct thing. It's this direct thing that's now causing problems on RT. > Making the locks raw would indeed solve all the issues at once. Last > time I was looking into this, would include three locks and I tried to > trigger the worst-case via "re-seed" and this was visible back then. > After the rework you did back thinks looked good. I actually just sent a patch for that to you a second ago: https://lore.kernel.org/lkml/20220801142530.133007-1-Jason@zx2c4.com/ It's only two locks, and usage seems pretty constrained in a good way. So okay, if you're on board, let's just do that, and then printk can stay the same. Jason
© 2016 - 2025 Red Hat, Inc.