]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
keys: Fix UAF in key_put()
authorDavid Howells <dhowells@redhat.com>
Wed, 19 Mar 2025 15:57:46 +0000 (15:57 +0000)
committerJarkko Sakkinen <jarkko@kernel.org>
Sat, 22 Mar 2025 13:36:49 +0000 (15:36 +0200)
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
Closes: https://lore.kernel.org/all/673b6aec.050a0220.87769.004a.GAE@google.com/
Signed-off-by: David Howells <dhowells@redhat.com>
Tested-by: syzbot+6105ffc1ded71d194d6d@syzkaller.appspotmail.com
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
include/linux/key.h
security/keys/gc.c
security/keys/key.c

index 074dca3222b967179e5c0f344e8cb1d624a736eb..ba05de8579ecc5ca31a428ef963bd4256403f5ea 100644 (file)
@@ -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
index 7d687b0962b14678ae1a1edc31973e7021700740..f27223ea4578f1dcee16a68d51357e337e522a0a 100644 (file)
@@ -218,8 +218,10 @@ continue_scanning:
                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) {
index 3d7d185019d30a8befd20dd8717f4350a949798c..7198cd2ac3a3a57dd17a34f4d174e54cf2b74558 100644 (file)
@@ -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);
                }
        }