* [PATCH v2] keys: Fix UAF in key_put()
@ 2025-03-19 15:57 David Howells
2025-03-19 16:30 ` Oleg Nesterov
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: David Howells @ 2025-03-19 15:57 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: dhowells, Kees Cook, Oleg Nesterov, Greg KH, Josh Drake,
Suraj Sonawane, keyrings, linux-security-module, security, stable,
linux-kernel
Once a key's reference count has been reduced to 0, the garbage collector
thread may destroy it at any time and so key_put() is not allowed to touch
the key after that point. The most key_put() is normally allowed to do is
to touch key_gc_work as that's a static global variable.
However, in an effort to speed up the reclamation of quota, this is now
done in key_put() once the key's usage is reduced to 0 - but now the code
is looking at the key after the deadline, which is forbidden.
Fix this by using a flag to indicate that a key can be gc'd now rather than
looking at the key's refcount in the garbage collector.
Fixes: 9578e327b2b4 ("keys: update key quotas in key_put()")
Reported-by: syzbot+6105ffc1ded71d194d6d@syzkaller.appspotmail.com
Signed-off-by: David Howells <dhowells@redhat.com>
Tested-by: syzbot+6105ffc1ded71d194d6d@syzkaller.appspotmail.com
cc: Jarkko Sakkinen <jarkko@kernel.org>
cc: Oleg Nesterov <oleg@redhat.com>
cc: Kees Cook <kees@kernel.org>
cc: Hillf Danton <hdanton@sina.com>,
cc: keyrings@vger.kernel.org
Cc: stable@vger.kernel.org # v6.10+
---
include/linux/key.h | 1 +
security/keys/gc.c | 4 +++-
security/keys/key.c | 2 ++
3 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/include/linux/key.h b/include/linux/key.h
index 074dca3222b9..ba05de8579ec 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -236,6 +236,7 @@ struct key {
#define KEY_FLAG_ROOT_CAN_INVAL 7 /* set if key can be invalidated by root without permission */
#define KEY_FLAG_KEEP 8 /* set if key should not be removed */
#define KEY_FLAG_UID_KEYRING 9 /* set if key is a user or user session keyring */
+#define KEY_FLAG_FINAL_PUT 10 /* set if final put has happened on key */
/* the key type and key description string
* - the desc is used to match a key against search criteria
diff --git a/security/keys/gc.c b/security/keys/gc.c
index 7d687b0962b1..f27223ea4578 100644
--- a/security/keys/gc.c
+++ b/security/keys/gc.c
@@ -218,8 +218,10 @@ static void key_garbage_collector(struct work_struct *work)
key = rb_entry(cursor, struct key, serial_node);
cursor = rb_next(cursor);
- if (refcount_read(&key->usage) == 0)
+ if (test_bit(KEY_FLAG_FINAL_PUT, &key->flags)) {
+ smp_mb(); /* Clobber key->user after FINAL_PUT seen. */
goto found_unreferenced_key;
+ }
if (unlikely(gc_state & KEY_GC_REAPING_DEAD_1)) {
if (key->type == key_gc_dead_keytype) {
diff --git a/security/keys/key.c b/security/keys/key.c
index 3d7d185019d3..7198cd2ac3a3 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -658,6 +658,8 @@ void key_put(struct key *key)
key->user->qnbytes -= key->quotalen;
spin_unlock_irqrestore(&key->user->lock, flags);
}
+ smp_mb(); /* key->user before FINAL_PUT set. */
+ set_bit(KEY_FLAG_FINAL_PUT, &key->flags);
schedule_work(&key_gc_work);
}
}
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v2] keys: Fix UAF in key_put()
2025-03-19 15:57 [PATCH v2] keys: Fix UAF in key_put() David Howells
@ 2025-03-19 16:30 ` Oleg Nesterov
2025-03-19 18:11 ` Linus Torvalds
2025-03-19 18:47 ` David Howells
2025-03-20 16:14 ` Jarkko Sakkinen
2025-03-20 16:39 ` David Howells
2 siblings, 2 replies; 9+ messages in thread
From: Oleg Nesterov @ 2025-03-19 16:30 UTC (permalink / raw)
To: David Howells
Cc: Jarkko Sakkinen, Kees Cook, Greg KH, Josh Drake, Suraj Sonawane,
keyrings, linux-security-module, security, stable, linux-kernel
On 03/19, David Howells wrote:
>
> --- a/security/keys/gc.c
> +++ b/security/keys/gc.c
> @@ -218,8 +218,10 @@ static void key_garbage_collector(struct work_struct *work)
> key = rb_entry(cursor, struct key, serial_node);
> cursor = rb_next(cursor);
>
> - if (refcount_read(&key->usage) == 0)
> + if (test_bit(KEY_FLAG_FINAL_PUT, &key->flags)) {
> + smp_mb(); /* Clobber key->user after FINAL_PUT seen. */
> goto found_unreferenced_key;
> + }
>
> if (unlikely(gc_state & KEY_GC_REAPING_DEAD_1)) {
> if (key->type == key_gc_dead_keytype) {
> diff --git a/security/keys/key.c b/security/keys/key.c
> index 3d7d185019d3..7198cd2ac3a3 100644
> --- a/security/keys/key.c
> +++ b/security/keys/key.c
> @@ -658,6 +658,8 @@ void key_put(struct key *key)
> key->user->qnbytes -= key->quotalen;
> spin_unlock_irqrestore(&key->user->lock, flags);
> }
> + smp_mb(); /* key->user before FINAL_PUT set. */
Can't resist, smp_mb__before_atomic() should equally work,
but this doesn't really matter, please forget.
> + set_bit(KEY_FLAG_FINAL_PUT, &key->flags);
> schedule_work(&key_gc_work);
I believe this patch is correct,
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] keys: Fix UAF in key_put()
2025-03-19 16:30 ` Oleg Nesterov
@ 2025-03-19 18:11 ` Linus Torvalds
2025-03-19 18:47 ` David Howells
1 sibling, 0 replies; 9+ messages in thread
From: Linus Torvalds @ 2025-03-19 18:11 UTC (permalink / raw)
To: Oleg Nesterov
Cc: David Howells, Jarkko Sakkinen, Kees Cook, Greg KH, Josh Drake,
Suraj Sonawane, keyrings, linux-security-module, security, stable,
linux-kernel
On Wed, 19 Mar 2025 at 09:31, Oleg Nesterov <oleg@redhat.com> wrote:
>
> Can't resist, smp_mb__before_atomic() should equally work,
> but this doesn't really matter, please forget.
We really should have "test_bit_acquire()" and "set_bit_release()".
Well, we do have the test_bit_acquire().
We just don't have the set_bit side, because we only have the bit
clearing version (and it's called "clear_bit_unlock()" for historical
reasons).
Annoying.
Linus
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] keys: Fix UAF in key_put()
2025-03-19 16:30 ` Oleg Nesterov
2025-03-19 18:11 ` Linus Torvalds
@ 2025-03-19 18:47 ` David Howells
1 sibling, 0 replies; 9+ messages in thread
From: David Howells @ 2025-03-19 18:47 UTC (permalink / raw)
To: Linus Torvalds
Cc: dhowells, Oleg Nesterov, Jarkko Sakkinen, Kees Cook, Greg KH,
Josh Drake, Suraj Sonawane, keyrings, linux-security-module,
security, stable, linux-kernel
Linus Torvalds <torvalds@linuxfoundation.org> wrote:
> We really should have "test_bit_acquire()" and "set_bit_release()".
I considered using test_bit_acquire() but, as you say, there's no
set_bit_release() as yet. I could switch things to initialise the flag to set
on key creation and clear the flag instead.
David
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] keys: Fix UAF in key_put()
2025-03-19 15:57 [PATCH v2] keys: Fix UAF in key_put() David Howells
2025-03-19 16:30 ` Oleg Nesterov
@ 2025-03-20 16:14 ` Jarkko Sakkinen
2025-03-20 16:39 ` David Howells
2 siblings, 0 replies; 9+ messages in thread
From: Jarkko Sakkinen @ 2025-03-20 16:14 UTC (permalink / raw)
To: David Howells
Cc: Kees Cook, Oleg Nesterov, Greg KH, Josh Drake, Suraj Sonawane,
keyrings, linux-security-module, security, stable, linux-kernel
On Wed, Mar 19, 2025 at 03:57:46PM +0000, David Howells wrote:
>
> Once a key's reference count has been reduced to 0, the garbage collector
> thread may destroy it at any time and so key_put() is not allowed to touch
> the key after that point. The most key_put() is normally allowed to do is
> to touch key_gc_work as that's a static global variable.
>
> However, in an effort to speed up the reclamation of quota, this is now
> done in key_put() once the key's usage is reduced to 0 - but now the code
> is looking at the key after the deadline, which is forbidden.
>
> Fix this by using a flag to indicate that a key can be gc'd now rather than
> looking at the key's refcount in the garbage collector.
>
> Fixes: 9578e327b2b4 ("keys: update key quotas in key_put()")
> Reported-by: syzbot+6105ffc1ded71d194d6d@syzkaller.appspotmail.com
> Signed-off-by: David Howells <dhowells@redhat.com>
> Tested-by: syzbot+6105ffc1ded71d194d6d@syzkaller.appspotmail.com
> cc: Jarkko Sakkinen <jarkko@kernel.org>
> cc: Oleg Nesterov <oleg@redhat.com>
> cc: Kees Cook <kees@kernel.org>
> cc: Hillf Danton <hdanton@sina.com>,
> cc: keyrings@vger.kernel.org
> Cc: stable@vger.kernel.org # v6.10+
> ---
> include/linux/key.h | 1 +
> security/keys/gc.c | 4 +++-
> security/keys/key.c | 2 ++
> 3 files changed, 6 insertions(+), 1 deletion(-)
>
> diff --git a/include/linux/key.h b/include/linux/key.h
> index 074dca3222b9..ba05de8579ec 100644
> --- a/include/linux/key.h
> +++ b/include/linux/key.h
> @@ -236,6 +236,7 @@ struct key {
> #define KEY_FLAG_ROOT_CAN_INVAL 7 /* set if key can be invalidated by root without permission */
> #define KEY_FLAG_KEEP 8 /* set if key should not be removed */
> #define KEY_FLAG_UID_KEYRING 9 /* set if key is a user or user session keyring */
> +#define KEY_FLAG_FINAL_PUT 10 /* set if final put has happened on key */
>
> /* the key type and key description string
> * - the desc is used to match a key against search criteria
> diff --git a/security/keys/gc.c b/security/keys/gc.c
> index 7d687b0962b1..f27223ea4578 100644
> --- a/security/keys/gc.c
> +++ b/security/keys/gc.c
> @@ -218,8 +218,10 @@ static void key_garbage_collector(struct work_struct *work)
> key = rb_entry(cursor, struct key, serial_node);
> cursor = rb_next(cursor);
>
> - if (refcount_read(&key->usage) == 0)
> + if (test_bit(KEY_FLAG_FINAL_PUT, &key->flags)) {
> + smp_mb(); /* Clobber key->user after FINAL_PUT seen. */
test_bit() is already atomic.
https://docs.kernel.org/core-api/wrappers/atomic_bitops.html
> goto found_unreferenced_key;
> + }
>
> if (unlikely(gc_state & KEY_GC_REAPING_DEAD_1)) {
> if (key->type == key_gc_dead_keytype) {
> diff --git a/security/keys/key.c b/security/keys/key.c
> index 3d7d185019d3..7198cd2ac3a3 100644
> --- a/security/keys/key.c
> +++ b/security/keys/key.c
> @@ -658,6 +658,8 @@ void key_put(struct key *key)
> key->user->qnbytes -= key->quotalen;
> spin_unlock_irqrestore(&key->user->lock, flags);
> }
> + smp_mb(); /* key->user before FINAL_PUT set. */
> + set_bit(KEY_FLAG_FINAL_PUT, &key->flags);
Ditto.
Nit: I'm just thinking should the name imply more like that "now
key_put() is actually done". E.g., even something like KEY_FLAG_PUT_DONE
would be more self-descriptive.
> schedule_work(&key_gc_work);
> }
> }
>
>
BR, Jarkko
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] keys: Fix UAF in key_put()
2025-03-19 15:57 [PATCH v2] keys: Fix UAF in key_put() David Howells
2025-03-19 16:30 ` Oleg Nesterov
2025-03-20 16:14 ` Jarkko Sakkinen
@ 2025-03-20 16:39 ` David Howells
2025-03-20 17:28 ` Jarkko Sakkinen
2 siblings, 1 reply; 9+ messages in thread
From: David Howells @ 2025-03-20 16:39 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: dhowells, Kees Cook, Oleg Nesterov, Greg KH, Josh Drake,
Suraj Sonawane, keyrings, linux-security-module, security, stable,
linux-kernel
Jarkko Sakkinen <jarkko@kernel.org> wrote:
> > + if (test_bit(KEY_FLAG_FINAL_PUT, &key->flags)) {
> > + smp_mb(); /* Clobber key->user after FINAL_PUT seen. */
>
> test_bit() is already atomic.
Atomiticity doesn't apply to test_bit() - it only matters when it does two (or
more) accesses that must be perceptually indivisible (e.g. set_bit doing RMW).
But atomiticity isn't the issue here, hence the barrier. You need to be
looking at memory-barriers.txt, not atomic_bitops.txt.
We have two things to correctly order and set_bit() does not imply sufficient
barriering; test_and_set_bit() does, but not set_bit(), hence Linus's comment
about really wanting a set_bit_release().
> > + smp_mb(); /* key->user before FINAL_PUT set. */
> > + set_bit(KEY_FLAG_FINAL_PUT, &key->flags);
>
> Ditto.
Ditto. ;-)
> Nit: I'm just thinking should the name imply more like that "now
> key_put() is actually done". E.g., even something like KEY_FLAG_PUT_DONE
> would be more self-descriptive.
KEY_FLAG_PUT_DONE isn't right. There can be lots of puts on a single key -
only the one that reduces it to 0 matters for this. You could call it
KEY_FLAG_CAN_NOW_GC or KEY_FLAG_GC_ABLE.
David
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] keys: Fix UAF in key_put()
2025-03-20 16:39 ` David Howells
@ 2025-03-20 17:28 ` Jarkko Sakkinen
2025-03-20 18:46 ` Jarkko Sakkinen
0 siblings, 1 reply; 9+ messages in thread
From: Jarkko Sakkinen @ 2025-03-20 17:28 UTC (permalink / raw)
To: David Howells
Cc: Kees Cook, Oleg Nesterov, Greg KH, Josh Drake, Suraj Sonawane,
keyrings, linux-security-module, security, stable, linux-kernel
On Thu, Mar 20, 2025 at 04:39:11PM +0000, David Howells wrote:
> Jarkko Sakkinen <jarkko@kernel.org> wrote:
>
> > > + if (test_bit(KEY_FLAG_FINAL_PUT, &key->flags)) {
> > > + smp_mb(); /* Clobber key->user after FINAL_PUT seen. */
> >
> > test_bit() is already atomic.
>
> Atomiticity doesn't apply to test_bit() - it only matters when it does two (or
> more) accesses that must be perceptually indivisible (e.g. set_bit doing RMW).
>
> But atomiticity isn't the issue here, hence the barrier. You need to be
> looking at memory-barriers.txt, not atomic_bitops.txt.
>
> We have two things to correctly order and set_bit() does not imply sufficient
> barriering; test_and_set_bit() does, but not set_bit(), hence Linus's comment
> about really wanting a set_bit_release().
Oops, I was hallucinating here. And yeah, test_and_set_bit() does
imply full mb as you said.
I was somehow remembering what I did in SGX driver incorrectly and
that led me into misconclusions, sorry.
if (test_and_set_bit(SGX_ENCL_IOCTL, &encl->flags))
return -EBUSY;
>
> > > + smp_mb(); /* key->user before FINAL_PUT set. */
> > > + set_bit(KEY_FLAG_FINAL_PUT, &key->flags);
> >
> > Ditto.
>
> Ditto. ;-)
Duh, no need poke with the stick further (or deeper) ;-)
>
> > Nit: I'm just thinking should the name imply more like that "now
> > key_put() is actually done". E.g., even something like KEY_FLAG_PUT_DONE
> > would be more self-descriptive.
>
> KEY_FLAG_PUT_DONE isn't right. There can be lots of puts on a single key -
> only the one that reduces it to 0 matters for this. You could call it
> KEY_FLAG_CAN_NOW_GC or KEY_FLAG_GC_ABLE.
Well all alternatives are fine but my thinking was that one that finally
zeros the refcount, "finalizes put" (pick whatever you want anyway).
>
> David
BR, Jarkko
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] keys: Fix UAF in key_put()
2025-03-20 17:28 ` Jarkko Sakkinen
@ 2025-03-20 18:46 ` Jarkko Sakkinen
2025-03-20 18:48 ` Jarkko Sakkinen
0 siblings, 1 reply; 9+ messages in thread
From: Jarkko Sakkinen @ 2025-03-20 18:46 UTC (permalink / raw)
To: David Howells
Cc: Kees Cook, Oleg Nesterov, Greg KH, Josh Drake, Suraj Sonawane,
keyrings, linux-security-module, security, stable, linux-kernel
On Thu, Mar 20, 2025 at 07:28:36PM +0200, Jarkko Sakkinen wrote:
> On Thu, Mar 20, 2025 at 04:39:11PM +0000, David Howells wrote:
> > Jarkko Sakkinen <jarkko@kernel.org> wrote:
> >
> > > > + if (test_bit(KEY_FLAG_FINAL_PUT, &key->flags)) {
> > > > + smp_mb(); /* Clobber key->user after FINAL_PUT seen. */
> > >
> > > test_bit() is already atomic.
> >
> > Atomiticity doesn't apply to test_bit() - it only matters when it does two (or
> > more) accesses that must be perceptually indivisible (e.g. set_bit doing RMW).
> >
> > But atomiticity isn't the issue here, hence the barrier. You need to be
> > looking at memory-barriers.txt, not atomic_bitops.txt.
> >
> > We have two things to correctly order and set_bit() does not imply sufficient
> > barriering; test_and_set_bit() does, but not set_bit(), hence Linus's comment
> > about really wanting a set_bit_release().
>
> Oops, I was hallucinating here. And yeah, test_and_set_bit() does
> imply full mb as you said.
>
> I was somehow remembering what I did in SGX driver incorrectly and
> that led me into misconclusions, sorry.
>
> if (test_and_set_bit(SGX_ENCL_IOCTL, &encl->flags))
> return -EBUSY;
>
> >
> > > > + smp_mb(); /* key->user before FINAL_PUT set. */
> > > > + set_bit(KEY_FLAG_FINAL_PUT, &key->flags);
> > >
> > > Ditto.
> >
> > Ditto. ;-)
>
> Duh, no need poke with the stick further (or deeper) ;-)
>
> >
> > > Nit: I'm just thinking should the name imply more like that "now
> > > key_put() is actually done". E.g., even something like KEY_FLAG_PUT_DONE
> > > would be more self-descriptive.
> >
> > KEY_FLAG_PUT_DONE isn't right. There can be lots of puts on a single key -
> > only the one that reduces it to 0 matters for this. You could call it
> > KEY_FLAG_CAN_NOW_GC or KEY_FLAG_GC_ABLE.
>
> Well all alternatives are fine but my thinking was that one that finally
> zeros the refcount, "finalizes put" (pick whatever you want anyway).
I'll pick this one up tomorrow and put a PR out within this week.
BR, Jarkko
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] keys: Fix UAF in key_put()
2025-03-20 18:46 ` Jarkko Sakkinen
@ 2025-03-20 18:48 ` Jarkko Sakkinen
0 siblings, 0 replies; 9+ messages in thread
From: Jarkko Sakkinen @ 2025-03-20 18:48 UTC (permalink / raw)
To: David Howells
Cc: Kees Cook, Oleg Nesterov, Greg KH, Josh Drake, Suraj Sonawane,
keyrings, linux-security-module, security, stable, linux-kernel
On Thu, Mar 20, 2025 at 08:46:41PM +0200, Jarkko Sakkinen wrote:
> On Thu, Mar 20, 2025 at 07:28:36PM +0200, Jarkko Sakkinen wrote:
> > On Thu, Mar 20, 2025 at 04:39:11PM +0000, David Howells wrote:
> > > Jarkko Sakkinen <jarkko@kernel.org> wrote:
> > >
> > > > > + if (test_bit(KEY_FLAG_FINAL_PUT, &key->flags)) {
> > > > > + smp_mb(); /* Clobber key->user after FINAL_PUT seen. */
> > > >
> > > > test_bit() is already atomic.
> > >
> > > Atomiticity doesn't apply to test_bit() - it only matters when it does two (or
> > > more) accesses that must be perceptually indivisible (e.g. set_bit doing RMW).
> > >
> > > But atomiticity isn't the issue here, hence the barrier. You need to be
> > > looking at memory-barriers.txt, not atomic_bitops.txt.
> > >
> > > We have two things to correctly order and set_bit() does not imply sufficient
> > > barriering; test_and_set_bit() does, but not set_bit(), hence Linus's comment
> > > about really wanting a set_bit_release().
> >
> > Oops, I was hallucinating here. And yeah, test_and_set_bit() does
> > imply full mb as you said.
> >
> > I was somehow remembering what I did in SGX driver incorrectly and
> > that led me into misconclusions, sorry.
> >
> > if (test_and_set_bit(SGX_ENCL_IOCTL, &encl->flags))
> > return -EBUSY;
> >
> > >
> > > > > + smp_mb(); /* key->user before FINAL_PUT set. */
> > > > > + set_bit(KEY_FLAG_FINAL_PUT, &key->flags);
> > > >
> > > > Ditto.
> > >
> > > Ditto. ;-)
> >
> > Duh, no need poke with the stick further (or deeper) ;-)
> >
> > >
> > > > Nit: I'm just thinking should the name imply more like that "now
> > > > key_put() is actually done". E.g., even something like KEY_FLAG_PUT_DONE
> > > > would be more self-descriptive.
> > >
> > > KEY_FLAG_PUT_DONE isn't right. There can be lots of puts on a single key -
> > > only the one that reduces it to 0 matters for this. You could call it
> > > KEY_FLAG_CAN_NOW_GC or KEY_FLAG_GC_ABLE.
> >
> > Well all alternatives are fine but my thinking was that one that finally
> > zeros the refcount, "finalizes put" (pick whatever you want anyway).
>
> I'll pick this one up tomorrow and put a PR out within this week.
I can try get this done tomorrow fully (with only one patch in the PR)
so that we would get it still to the ongoing release...
BR, Jarkko
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2025-03-20 18:48 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-03-19 15:57 [PATCH v2] keys: Fix UAF in key_put() David Howells
2025-03-19 16:30 ` Oleg Nesterov
2025-03-19 18:11 ` Linus Torvalds
2025-03-19 18:47 ` David Howells
2025-03-20 16:14 ` Jarkko Sakkinen
2025-03-20 16:39 ` David Howells
2025-03-20 17:28 ` Jarkko Sakkinen
2025-03-20 18:46 ` Jarkko Sakkinen
2025-03-20 18:48 ` Jarkko Sakkinen
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).