| From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> |
| Date: Mon, 7 May 2018 17:09:42 +0200 |
| Subject: [PATCH] userns: use refcount_t for reference counting instead |
| atomic_t |
| |
| refcount_t type and corresponding API should be used instead of atomic_t when |
| the variable is used as a reference counter. This allows to avoid accidental |
| refcounter overflows that might lead to use-after-free situations. |
| |
| Suggested-by: Peter Zijlstra <peterz@infradead.org> |
| Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> |
| --- |
| include/linux/sched/user.h | 5 +++-- |
| kernel/user.c | 8 ++++---- |
| 2 files changed, 7 insertions(+), 6 deletions(-) |
| |
| --- a/include/linux/sched/user.h |
| +++ b/include/linux/sched/user.h |
| @@ -4,6 +4,7 @@ |
| |
| #include <linux/uidgid.h> |
| #include <linux/atomic.h> |
| +#include <linux/refcount.h> |
| #include <linux/ratelimit.h> |
| |
| struct key; |
| @@ -12,7 +13,7 @@ struct key; |
| * Some day this will be a full-fledged user tracking system.. |
| */ |
| struct user_struct { |
| - atomic_t __count; /* reference count */ |
| + refcount_t __count; /* reference count */ |
| atomic_t processes; /* How many processes does this user have? */ |
| atomic_t sigpending; /* How many pending signals does this user have? */ |
| #ifdef CONFIG_FANOTIFY |
| @@ -59,7 +60,7 @@ extern struct user_struct root_user; |
| extern struct user_struct * alloc_uid(kuid_t); |
| static inline struct user_struct *get_uid(struct user_struct *u) |
| { |
| - atomic_inc(&u->__count); |
| + refcount_inc(&u->__count); |
| return u; |
| } |
| extern void free_uid(struct user_struct *); |
| --- a/kernel/user.c |
| +++ b/kernel/user.c |
| @@ -96,7 +96,7 @@ static DEFINE_SPINLOCK(uidhash_lock); |
| |
| /* root_user.__count is 1, for init task cred */ |
| struct user_struct root_user = { |
| - .__count = ATOMIC_INIT(1), |
| + .__count = REFCOUNT_INIT(1), |
| .processes = ATOMIC_INIT(1), |
| .sigpending = ATOMIC_INIT(0), |
| .locked_shm = 0, |
| @@ -123,7 +123,7 @@ static struct user_struct *uid_hash_find |
| |
| hlist_for_each_entry(user, hashent, uidhash_node) { |
| if (uid_eq(user->uid, uid)) { |
| - atomic_inc(&user->__count); |
| + refcount_inc(&user->__count); |
| return user; |
| } |
| } |
| @@ -170,7 +170,7 @@ void free_uid(struct user_struct *up) |
| return; |
| |
| local_irq_save(flags); |
| - if (atomic_dec_and_lock(&up->__count, &uidhash_lock)) |
| + if (refcount_dec_and_lock(&up->__count, &uidhash_lock)) |
| free_user(up, flags); |
| else |
| local_irq_restore(flags); |
| @@ -191,7 +191,7 @@ struct user_struct *alloc_uid(kuid_t uid |
| goto out_unlock; |
| |
| new->uid = uid; |
| - atomic_set(&new->__count, 1); |
| + refcount_set(&new->__count, 1); |
| ratelimit_state_init(&new->ratelimit, HZ, 100); |
| ratelimit_set_flags(&new->ratelimit, RATELIMIT_MSG_ON_RELEASE); |
| |