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