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
Unified · Split
| @@ -160,6 +160,7 @@ struct ucounts *alloc_ucounts(struct user_namespace *ns, kuid_t uid) · 159 unmodified lines | ||
| 160 | 160 | { |
| 161 | 161 | struct hlist_head *hashent = ucounts_hashentry(ns, uid); |
| 162 | 162 | struct ucounts *ucounts, *new; |
| 163 | + long overflow; | |
| 163 | 164 | |
| 164 | 165 | spin_lock_irq(&ucounts_lock); |
| 165 | 166 | ucounts = find_ucounts(ns, uid, hashent); |
| @@ -184,8 +185,12 @@ struct ucounts *alloc_ucounts(struct user_namespace *ns, kuid_t uid) · 18 unmodified lines | ||
| 184 | 185 | return new; |
| 185 | 186 | } |
| 186 | 187 | } |
| 188 | + overflow = atomic_add_negative(1, &ucounts->count); | |
| 187 | 189 | spin_unlock_irq(&ucounts_lock); |
| 188 | − ucounts = get_ucounts(ucounts); | |
| 190 | + if (overflow) { | |
| 191 | + put_ucounts(ucounts); | |
| 192 | + return NULL; | |
| 193 | + } | |
| 189 | 194 | return ucounts; |
| 190 | 195 | } |
| 191 | 196 | |
| @@ -193,8 +198,7 @@ void put_ucounts(struct ucounts *ucounts) · 1 unmodified line | ||
| 193 | 198 | { |
| 194 | 199 | unsigned long flags; |
| 195 | 200 | |
| 196 | − if (atomic_dec_and_test(&ucounts->count)) { | |
| 197 | − spin_lock_irqsave(&ucounts_lock, flags); | |
| 201 | + if (atomic_dec_and_lock_irqsave(&ucounts->count, &ucounts_lock, flags)) { | |
| 198 | 202 | hlist_del_init(&ucounts->node); |
| 199 | 203 | spin_unlock_irqrestore(&ucounts_lock, flags); |
| 200 | 204 | kfree(ucounts); |