diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-08-05 12:00:00 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-08-05 12:00:00 -0700 |
commit | 6209049ecfc1894453d1fc850e60c58d4eccaf2a (patch) | |
tree | dffe5a652de0608937393e9695ab75799a5aaf8a /kernel | |
parent | 3c3e9027071c979cfa7e48d9c2a39a4d56829236 (diff) | |
parent | 345daff2e994ee844d6a609c37f085695fbb4c4d (diff) | |
download | linux-stable-6209049ecfc1894453d1fc850e60c58d4eccaf2a.tar.gz linux-stable-6209049ecfc1894453d1fc850e60c58d4eccaf2a.tar.bz2 linux-stable-6209049ecfc1894453d1fc850e60c58d4eccaf2a.zip |
Merge branch 'for-v5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace
Pull ucounts fix from Eric Biederman:
"Fix a subtle locking versus reference counting bug in the ucount
changes, found by syzbot"
* 'for-v5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace:
ucounts: Fix race condition between alloc_ucounts and put_ucounts
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/ucount.c | 10 |
1 files changed, 7 insertions, 3 deletions
diff --git a/kernel/ucount.c b/kernel/ucount.c index 87799e2379bd..77be3bbe3cc4 100644 --- a/kernel/ucount.c +++ b/kernel/ucount.c @@ -160,6 +160,7 @@ struct ucounts *alloc_ucounts(struct user_namespace *ns, kuid_t uid) { struct hlist_head *hashent = ucounts_hashentry(ns, uid); struct ucounts *ucounts, *new; + long overflow; spin_lock_irq(&ucounts_lock); ucounts = find_ucounts(ns, uid, hashent); @@ -184,8 +185,12 @@ struct ucounts *alloc_ucounts(struct user_namespace *ns, kuid_t uid) return new; } } + overflow = atomic_add_negative(1, &ucounts->count); spin_unlock_irq(&ucounts_lock); - ucounts = get_ucounts(ucounts); + if (overflow) { + put_ucounts(ucounts); + return NULL; + } return ucounts; } @@ -193,8 +198,7 @@ void put_ucounts(struct ucounts *ucounts) { unsigned long flags; - if (atomic_dec_and_test(&ucounts->count)) { - spin_lock_irqsave(&ucounts_lock, flags); + if (atomic_dec_and_lock_irqsave(&ucounts->count, &ucounts_lock, flags)) { hlist_del_init(&ucounts->node); spin_unlock_irqrestore(&ucounts_lock, flags); kfree(ucounts); |