Count rlimits in each user namespace
Subsystems (11)
v11 cover Count rlimits in each user namespace
From: Alexey Gladkov <[email protected]>
Preface
-------
These patches are for binding the rlimit counters to a user in user namespace. This patch set can be applied on top of:
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git v5.12-rc4
Problem
-------
The RLIMIT_NPROC, RLIMIT_MEMLOCK, RLIMIT_SIGPENDING, RLIMIT_MSGQUEUE rlimits implementation places the counters in user_struct [1]. These limits are global between processes and persists for the lifetime of the process, even if processes are in different user namespaces.
To illustrate the impact of rlimits, let's say there is a program that does not fork. Some service-A wants to run this program as user X in multiple containers. Since the program never fork the service wants to set RLIMIT_NPROC=1.
service-A
\- program (uid=1000, container1, rlimit_nproc=1) \- program (uid=1000, container2, rlimit_nproc=1)
The service-A sets RLIMIT_NPROC=1 and runs the program in container1. When the service-A tries to run a program with RLIMIT_NPROC=1 in container2 it fails since user X already has one running process.
The problem is not that the limit from container1 affects container2. The problem is that limit is verified against the global counter that reflects the number of processes in all containers.
This problem can be worked around by using different users for each container but in this case we face a different problem of uid mapping when transferring files from one container to another.
Eric W. Biederman mentioned this issue [2][3].
Introduced changes
------------------
To address the problem, we bind rlimit counters to user namespace. Each counter reflects the number of processes in a given uid in a given user namespace. The result is a tree of rlimit counters with the biggest value at the root (aka init_user_ns). The limit is considered exceeded if it's exceeded up in the tree.
[1]: https://lore.kernel.org/containers/[email protected]/
[2]: https://lists.linuxfoundation.org/pipermail/containers/2020-August/042096.html
[3]: https://lists.linuxfoundation.org/pipermail/containers/2020-October/042524.html
Changelog
---------
v11:
* Revert most of changes in signal.c to fix performance issues and remove unnecessary memory allocations.
* Fixed issue found by lkp robot (again).
v10:
* Fixed memory leak in __sigqueue_alloc.
* Handled an unlikely situation when all consumers will return ucounts at once.
* Addressed other review comments from Eric W. Biederman.
v9:
* Used a negative value to check that the ucounts->count is close to overflow.
* Rebased onto v5.12-rc4.
v8:
* Used atomic_t for ucounts reference counting. Also added counter overflow check (thanks to Linus Torvalds for the idea).
* Fixed other issues found by lkp-tests project in the patch that Reimplements RLIMIT_MEMLOCK on top of ucounts.
v7:
* Fixed issues found by lkp-tests project in the patch that Reimplements RLIMIT_MEMLOCK on top of ucounts.
v6:
* Fixed issues found by lkp-tests project.
* Rebased onto v5.11.
v5:
* Split the first commit into two commits: change ucounts.count type to atomic_long_t and add ucounts to cred. These commits were merged by mistake during the rebase.
* The __get_ucounts() renamed to alloc_ucounts().
* The cred.ucounts update has been moved from commit_creds() as it did not allow to handle errors.
* Added error handling of set_cred_ucounts().
v4:
* Reverted the type change of ucounts.count to refcount_t.
* Fixed typo in the kernel/cred.c
v3:
* Added get_ucounts() function to increase the reference count. The existing get_counts() function renamed to __get_ucounts().
* The type of ucounts.count changed from atomic_t to refcount_t.
* Dropped 'const' from set_cred_ucounts() arguments.
* Fixed a bug with freeing the cred structure after calling cred_alloc_blank().
* Commit messages have been updated.
* Added selftest.
v2:
* RLIMIT_MEMLOCK, RLIMIT_SIGPENDING and RLIMIT_MSGQUEUE are migrated to ucounts.
* Added ucounts for pair uid and user namespace into cred.
* Added the ability to increase ucount by more than 1.
v1:
* After discussion with Eric W. Biederman, I increased the size of ucounts to atomic_long_t.
* Added ucount_max to avoid the fork bomb.
--
Alexey Gladkov (9):
Increase size of ucounts to atomic_long_t
Add a reference to ucounts for each cred
Use atomic_t for ucounts reference counting
Reimplement RLIMIT_NPROC on top of ucounts
Reimplement RLIMIT_MSGQUEUE on top of ucounts Reimplement RLIMIT_SIGPENDING on top of ucounts Reimplement RLIMIT_MEMLOCK on top of ucounts
kselftests: Add test to check for rlimit changes in different user
namespaces
ucounts: Set ucount_max to the largest positive value the type can
hold
fs/exec.c | 6 +- fs/hugetlbfs/inode.c | 16 +- fs/proc/array.c | 2 +- include/linux/cred.h | 4 + include/linux/hugetlb.h | 4 +- include/linux/mm.h | 4 +- include/linux/sched/user.h | 7 - include/linux/shmem_fs.h | 2 +- include/linux/signal_types.h | 4 +- include/linux/user_namespace.h | 31 +++- ipc/mqueue.c | 40 ++--- ipc/shm.c | 26 +-- kernel/cred.c | 50 +++++- kernel/exit.c | 2 +- kernel/fork.c | 18 +- kernel/signal.c | 25 +-- kernel/sys.c | 14 +- kernel/ucount.c | 116 ++++++++++--- kernel/user.c | 3 - kernel/user_namespace.c | 9 +- mm/memfd.c | 4 +- mm/mlock.c | 22 ++- mm/mmap.c | 4 +- mm/shmem.c | 10 +- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/rlimits/.gitignore | 2 + tools/testing/selftests/rlimits/Makefile | 6 + tools/testing/selftests/rlimits/config | 1 + .../selftests/rlimits/rlimits-per-userns.c | 161 ++++++++++++++++++ 29 files changed, 467 insertions(+), 127 deletions(-) create mode 100644 tools/testing/selftests/rlimits/.gitignore create mode 100644 tools/testing/selftests/rlimits/Makefile create mode 100644 tools/testing/selftests/rlimits/config create mode 100644 tools/testing/selftests/rlimits/rlimits-per-userns.c
Signature · 1 line
2.29.3
PATCH v11 1/9 Increase size of ucounts to atomic_long_t
From: Alexey Gladkov <[email protected]>
RLIMIT_MSGQUEUE and RLIMIT_MEMLOCK use unsigned long to store their counters. As a preparation for moving rlimits based on ucounts, we need to increase the size of the variable to long.
--- include/linux/user_namespace.h | 4 ++-- kernel/ucount.c | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-)
include/linux/user_namespace.h +2 -2
@@ -85,7 +85,7 @@ struct user_namespace {struct ctl_table_header *sysctls;#endif struct ucounts *ucounts; -int ucount_max[UCOUNT_COUNTS ]; +long ucount_max[UCOUNT_COUNTS ]; } __randomize_layout;struct ucounts {
@@ -93,7 +93,7 @@ struct ucounts {struct user_namespace *ns;kuid_t uid;int count; -atomic_t ucount[UCOUNT_COUNTS ]; +atomic_long_t ucount[UCOUNT_COUNTS ]; };extern struct user_namespace init_user_ns;
kernel/ucount.c +8 -8
@@ -175,14 +175,14 @@ static void put_ucounts(struct ucounts *ucounts)kfree (ucounts); } -static inline bool atomic_inc_below (atomic_t *v,int u) +static inline bool atomic_long_inc_below (atomic_long_t *v,int u) { -int c, old; - c =atomic_read (v); +long c, old; + c =atomic_long_read (v);for (;;) {if (unlikely (c >= u))return false; - old =atomic_cmpxchg (v, c, c+1 ); + old =atomic_long_cmpxchg (v, c, c+1 );if (likely (old == c))return true; c = old;
@@ -196,17 +196,17 @@ struct ucounts *inc_ucount(struct user_namespace *ns, kuid_t uid,struct user_namespace *tns; ucounts =get_ucounts (ns, uid);for (iter = ucounts; iter; iter = tns->ucounts) { -int max; +long max; tns = iter->ns; max =READ_ONCE (tns->ucount_max[type]); -if (!atomic_inc_below (&iter->ucount[type], max)) +if (!atomic_long_inc_below (&iter->ucount[type], max)) goto fail; }return ucounts; fail: bad = iter;for (iter = ucounts; iter != bad; iter = iter->ns->ucounts) -atomic_dec (&iter->ucount[type]); +atomic_long_dec (&iter->ucount[type]);put_ucounts (ucounts);return NULL ;
@@ -216,7 +216,7 @@ void dec_ucount(struct ucounts *ucounts, enum ucount_type type) {struct ucounts *iter;for (iter = ucounts; iter; iter = iter->ns->ucounts) { -int dec =atomic_dec_if_positive (&iter->ucount[type]); +long dec =atomic_long_dec_if_positive (&iter->ucount[type]);WARN_ON_ONCE (dec <0 ); }put_ucounts (ucounts);
Signature · 1 line
2.29.3
PATCH v11 2/9 Add a reference to ucounts for each cred
In reply to [email protected]From: Alexey Gladkov <[email protected]>
For RLIMIT_NPROC and some other rlimits the user_struct that holds the global limit is kept alive for the lifetime of a process by keeping it in struct cred. Adding a pointer to ucounts in the struct cred will allow to track RLIMIT_NPROC not only for user in the system, but for user in the user_namespace.
Updating ucounts may require memory allocation which may fail. So, we cannot change cred.ucounts in the commit_creds() because this function cannot fail and it should always return 0. For this reason, we modify cred.ucounts before calling the commit_creds().
Changelog
v6:
* Fix null-ptr-deref in is_ucounts_overlimit() detected by trinity. This error was caused by the fact that cred_alloc_blank() left the ucounts pointer empty.
--- fs/exec.c | 4 ++++ include/linux/cred.h | 2 ++ include/linux/user_namespace.h | 4 ++++ kernel/cred.c | 40 ++++++++++++++++++++++++++++++++++ kernel/fork.c | 6 +++++ kernel/sys.c | 12 ++++++++++ kernel/ucount.c | 40 +++++++++++++++++++++++++++++++--- kernel/user_namespace.c | 3 +++ 8 files changed, 108 insertions(+), 3 deletions(-)
fs/exec.c +4 -0
@@ -1360,6 +1360,10 @@ int begin_new_exec(struct linux_binprm * bprm)WRITE_ONCE (me->self_exec_id, me->self_exec_id +1 );flush_signal_handlers (me,0 ); + retval =set_cred_ucounts (bprm->cred); +if (retval <0 ) + goto out_unlock; +/* * install the new credentials for this executable */
include/linux/cred.h +2 -0
@@ -144,6 +144,7 @@ struct cred {#endif struct user_struct *user;/* real user ID subscription */ struct user_namespace *user_ns;/* user_ns the caps and keyrings are relative to. */ +struct ucounts *ucounts;struct group_info *group_info;/* supplementary groups for euid/fsgid */ /* RCU deletion */ union {
@@ -170,6 +171,7 @@ extern int set_security_override_from_ctx(struct cred *, const char *);extern int set_create_files_as (struct cred *,struct inode *);extern int cred_fscmp (const struct cred *,const struct cred *);extern void __init cred_init (void ); +extern int set_cred_ucounts (struct cred *);/* * check for validity of credentials
include/linux/user_namespace.h +4 -0
@@ -97,11 +97,15 @@ struct ucounts { };extern struct user_namespace init_user_ns; +extern struct ucounts init_ucounts;bool setup_userns_sysctls (struct user_namespace *ns);void retire_userns_sysctls (struct user_namespace *ns);struct ucounts *inc_ucount (struct user_namespace *ns,kuid_t uid,enum ucount_type type);void dec_ucount (struct ucounts *ucounts,enum ucount_type type); +struct ucounts *alloc_ucounts (struct user_namespace *ns,kuid_t uid); +struct ucounts *get_ucounts (struct ucounts *ucounts); +void put_ucounts (struct ucounts *ucounts);#ifdef CONFIG_USER_NS
kernel/cred.c +40 -0
@@ -60,6 +60,7 @@ struct cred init_cred = { .user = INIT_USER, .user_ns = &init_user_ns, .group_info = &init_groups, + .ucounts = &init_ucounts, };static inline void set_cred_subscribers (struct cred *cred,int n)
@@ -119,6 +120,8 @@ static void put_cred_rcu(struct rcu_head *rcu)if (cred->group_info)put_group_info (cred->group_info);free_uid (cred->user); +if (cred->ucounts) +put_ucounts (cred->ucounts);put_user_ns (cred->user_ns);kmem_cache_free (cred_jar, cred); }
@@ -222,6 +225,7 @@ struct cred *cred_alloc_blank(void)#ifdef CONFIG_DEBUG_CREDENTIALS new->magic =CRED_MAGIC ;#endif + new->ucounts =get_ucounts (&init_ucounts);if (security_cred_alloc_blank (new,GFP_KERNEL_ACCOUNT ) <0 ) goto error;
@@ -284,6 +288,11 @@ struct cred *prepare_creds(void)if (security_prepare_creds (new, old,GFP_KERNEL_ACCOUNT ) <0 ) goto error; + + new->ucounts =get_ucounts (new->ucounts); +if (!new->ucounts) + goto error; +validate_creds (new);return new;
@@ -363,6 +372,8 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags) ret =create_user_ns (new);if (ret <0 ) goto error_put; +if (set_cred_ucounts (new) <0 ) + goto error_put; }#ifdef CONFIG_KEYS
@@ -653,6 +664,31 @@ int cred_fscmp(const struct cred *a, const struct cred *b) }EXPORT_SYMBOL (cred_fscmp); +int set_cred_ucounts (struct cred *new) +{ +struct task_struct *task = current; +const struct cred *old = task->real_cred; +struct ucounts *old_ucounts = new->ucounts; + +if (new->user == old->user && new->user_ns == old->user_ns) +return 0 ; + +/* +* This optimization is needed because alloc_ucounts() uses locks +* for table lookups. +*/ +if (old_ucounts && old_ucounts->ns == new->user_ns &&uid_eq (old_ucounts->uid, new->euid)) +return 0 ; + +if (!(new->ucounts =alloc_ucounts (new->user_ns, new->euid))) +return -EAGAIN ; + +if (old_ucounts) +put_ucounts (old_ucounts); + +return 0 ; +} +/* * initialise the credentials stuff */
@@ -719,6 +755,10 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)if (security_prepare_creds (new, old,GFP_KERNEL_ACCOUNT ) <0 ) goto error; + new->ucounts =get_ucounts (new->ucounts); +if (!new->ucounts) + goto error; +put_cred (old);validate_creds (new);return new;
kernel/fork.c +6 -0
@@ -2995,6 +2995,12 @@ int ksys_unshare(unsigned long unshare_flags)if (err) goto bad_unshare_cleanup_cred; +if (new_cred) { + err =set_cred_ucounts (new_cred); +if (err) + goto bad_unshare_cleanup_cred; + } +if (new_fs || new_fd || do_sysvsem || new_cred || new_nsproxy) {if (do_sysvsem) {/*
kernel/sys.c +12 -0
@@ -552,6 +552,10 @@ long __sys_setreuid(uid_t ruid, uid_t euid)if (retval <0 ) goto error; + retval =set_cred_ucounts (new); +if (retval <0 ) + goto error; +return commit_creds (new); error:
@@ -610,6 +614,10 @@ long __sys_setuid(uid_t uid)if (retval <0 ) goto error; + retval =set_cred_ucounts (new); +if (retval <0 ) + goto error; +return commit_creds (new); error:
@@ -685,6 +693,10 @@ long __sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)if (retval <0 ) goto error; + retval =set_cred_ucounts (new); +if (retval <0 ) + goto error; +return commit_creds (new); error:
kernel/ucount.c +37 -3
@@ -8,6 +8,12 @@#include <linux/kmemleak.h> #include <linux/user_namespace.h> +struct ucounts init_ucounts = { + .ns = &init_user_ns, + .uid =GLOBAL_ROOT_UID , + .count =1 , +}; +#define UCOUNTS_HASHTABLE_BITS 10static struct hlist_head ucounts_hashtable[(1 <<UCOUNTS_HASHTABLE_BITS )];static DEFINE_SPINLOCK (ucounts_lock);
@@ -125,7 +131,15 @@ static struct ucounts *find_ucounts(struct user_namespace *ns, kuid_t uid, strucreturn NULL ; } -static struct ucounts *get_ucounts (struct user_namespace *ns,kuid_t uid) +static void hlist_add_ucounts (struct ucounts *ucounts) +{ +struct hlist_head *hashent =ucounts_hashentry (ucounts->ns, ucounts->uid); +spin_lock_irq (&ucounts_lock); +hlist_add_head (&ucounts->node, hashent); +spin_unlock_irq (&ucounts_lock); +} + +struct ucounts *alloc_ucounts (struct user_namespace *ns,kuid_t uid) {struct hlist_head *hashent =ucounts_hashentry (ns, uid);struct ucounts *ucounts, *new;
@@ -160,7 +174,26 @@ static struct ucounts *get_ucounts(struct user_namespace *ns, kuid_t uid)return ucounts; } -static void put_ucounts (struct ucounts *ucounts) +struct ucounts *get_ucounts (struct ucounts *ucounts) +{ +unsigned long flags; + +if (!ucounts) +return NULL ; + +spin_lock_irqsave (&ucounts_lock, flags); +if (ucounts->count ==INT_MAX ) { +WARN_ONCE (1 ,"ucounts: counter has reached its maximum value" ); + ucounts =NULL ; + }else { + ucounts->count +=1 ; + } +spin_unlock_irqrestore (&ucounts_lock, flags); + +return ucounts; +} + +void put_ucounts (struct ucounts *ucounts) {unsigned long flags;
@@ -194,7 +227,7 @@ struct ucounts *inc_ucount(struct user_namespace *ns, kuid_t uid, {struct ucounts *ucounts, *iter, *bad;struct user_namespace *tns; - ucounts =get_ucounts (ns, uid); + ucounts =alloc_ucounts (ns, uid);for (iter = ucounts; iter; iter = tns->ucounts) {long max; tns = iter->ns;
@@ -237,6 +270,7 @@ static __init int user_namespace_sysctl_init(void)BUG_ON (!user_header);BUG_ON (!setup_userns_sysctls (&init_user_ns));#endif +hlist_add_ucounts (&init_ucounts);return 0 ; }subsys_initcall (user_namespace_sysctl_init);
kernel/user_namespace.c +3 -0
@@ -1281,6 +1281,9 @@ static int userns_install(struct nsset *nsset, struct ns_common *ns)put_user_ns (cred->user_ns);set_cred_user_ns (cred,get_user_ns (user_ns)); +if (set_cred_ucounts (cred) <0 ) +return -EINVAL ; +return 0 ; }
Signature · 1 line
2.29.3
PATCH v11 3/9 Use atomic_t for ucounts reference counting
In reply to [email protected]From: Alexey Gladkov <[email protected]>
The current implementation of the ucounts reference counter requires the use of spin_lock. We're going to use get_ucounts() in more performance critical areas like a handling of RLIMIT_SIGPENDING.
Now we need to use spin_lock only if we want to change the hashtable.
v10:
* Always try to put ucounts in case we cannot increase ucounts->count. This will allow to cover the case when all consumers will return ucounts at once.
v9:
* Use a negative value to check that the ucounts->count is close to overflow.
--- include/linux/user_namespace.h | 4 +-- kernel/ucount.c | 53 ++++++++++++---------------------- 2 files changed, 21 insertions(+), 36 deletions(-)
include/linux/user_namespace.h +2 -2
@@ -92,7 +92,7 @@ struct ucounts {struct hlist_node node;struct user_namespace *ns;kuid_t uid; -int count; +atomic_t count;atomic_long_t ucount[UCOUNT_COUNTS ]; };
@@ -104,7 +104,7 @@ void retire_userns_sysctls(struct user_namespace *ns);struct ucounts *inc_ucount (struct user_namespace *ns,kuid_t uid,enum ucount_type type);void dec_ucount (struct ucounts *ucounts,enum ucount_type type);struct ucounts *alloc_ucounts (struct user_namespace *ns,kuid_t uid); -struct ucounts *get_ucounts (struct ucounts *ucounts); +struct ucounts *__must_check get_ucounts (struct ucounts *ucounts);void put_ucounts (struct ucounts *ucounts);#ifdef CONFIG_USER_NS
kernel/ucount.c +19 -34
87 lines not on this page · raw message
Signature · 6 lines
2.29.3 _______________________________________________ Containers mailing list [email protected] https://lists.linuxfoundation.org/mailman/listinfo/containers
PATCH v11 4/9 Reimplement RLIMIT_NPROC on top of ucounts
In reply to [email protected]From: Alexey Gladkov <[email protected]>
The rlimit counter is tied to uid in the user_namespace. This allows rlimit values to be specified in userns even if they are already globally exceeded by the user. However, the value of the previous user_namespaces cannot be exceeded.
To illustrate the impact of rlimits, let's say there is a program that does not fork. Some service-A wants to run this program as user X in multiple containers. Since the program never fork the service wants to set RLIMIT_NPROC=1.
service-A
\- program (uid=1000, container1, rlimit_nproc=1) \- program (uid=1000, container2, rlimit_nproc=1)
The service-A sets RLIMIT_NPROC=1 and runs the program in container1. When the service-A tries to run a program with RLIMIT_NPROC=1 in container2 it fails since user X already has one running process.
We cannot use existing inc_ucounts / dec_ucounts because they do not allow us to exceed the maximum for the counter. Some rlimits can be overlimited by root or if the user has the appropriate capability.
Changelog
v11:
* Change inc_rlimit_ucounts() which now returns top value of ucounts.
* Drop inc_rlimit_ucounts_and_test() because the return code of inc_rlimit_ucounts() can be checked.
--- fs/exec.c | 2 +- include/linux/cred.h | 2 ++ include/linux/sched/user.h | 1 - include/linux/user_namespace.h | 12 ++++++++++ kernel/cred.c | 10 ++++---- kernel/exit.c | 2 +- kernel/fork.c | 9 +++---- kernel/sys.c | 2 +- kernel/ucount.c | 44 ++++++++++++++++++++++++++++++++++ kernel/user.c | 1 - kernel/user_namespace.c | 3 ++- 11 files changed, 73 insertions(+), 15 deletions(-)
fs/exec.c +1 -1
@@ -1878,7 +1878,7 @@ static int do_execveat_common(int fd, struct filename *filename,* whether NPROC limit is still exceeded. */ if ((current->flags &PF_NPROC_EXCEEDED ) && -atomic_read (¤t_user ()->processes) >rlimit (RLIMIT_NPROC )) { +is_ucounts_overlimit (current_ucounts (),UCOUNT_RLIMIT_NPROC ,rlimit (RLIMIT_NPROC ))) { retval = -EAGAIN ; goto out_ret; }
include/linux/cred.h +2 -0
@@ -372,6 +372,7 @@ static inline void put_cred(const struct cred *_cred)#define task_uid (task) (task_cred_xxx((task), uid))#define task_euid (task) (task_cred_xxx((task), euid)) +#define task_ucounts (task) (task_cred_xxx((task), ucounts))#define current_cred_xxx (xxx) \ ({ \
@@ -388,6 +389,7 @@ static inline void put_cred(const struct cred *_cred)#define current_fsgid () (current_cred_xxx(fsgid))#define current_cap () (current_cred_xxx(cap_effective))#define current_user () (current_cred_xxx(user)) +#define current_ucounts () (current_cred_xxx(ucounts))extern struct user_namespace init_user_ns;#ifdef CONFIG_USER_NS
include/linux/sched/user.h +0 -1
@@ -12,7 +12,6 @@*/ struct user_struct {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 atomic_t fanotify_listeners;
include/linux/user_namespace.h +12 -0
@@ -50,9 +50,12 @@ enum ucount_type { UCOUNT_INOTIFY_INSTANCES, UCOUNT_INOTIFY_WATCHES, #endif + UCOUNT_RLIMIT_NPROC, UCOUNT_COUNTS, }; +#define MAX_PER_NAMESPACE_UCOUNTS UCOUNT_RLIMIT_NPROC + struct user_namespace { struct uid_gid_map uid_map; struct uid_gid_map gid_map;
@@ -107,6 +110,15 @@ struct ucounts *alloc_ucounts(struct user_namespace *ns, kuid_t uid);struct ucounts *__must_check get_ucounts (struct ucounts *ucounts);void put_ucounts (struct ucounts *ucounts); +static inline long get_ucounts_value (struct ucounts *ucounts,enum ucount_type type) +{ +return atomic_long_read (&ucounts->ucount[type]); +} + +long inc_rlimit_ucounts (struct ucounts *ucounts,enum ucount_type type,long v); +bool dec_rlimit_ucounts (struct ucounts *ucounts,enum ucount_type type,long v); +bool is_ucounts_overlimit (struct ucounts *ucounts,enum ucount_type type,unsigned long max); +#ifdef CONFIG_USER_NS static inline struct user_namespace *get_user_ns (struct user_namespace *ns)
kernel/cred.c +5 -5
@@ -360,7 +360,7 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)kdebug ("share_creds(%p{%d,%d})" , p->cred,atomic_read (&p->cred->usage),read_cred_subscribers (p->cred)); -atomic_inc (&p->cred->user->processes); +inc_rlimit_ucounts (task_ucounts (p),UCOUNT_RLIMIT_NPROC ,1 );return 0 ; }
@@ -395,8 +395,8 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags) }#endif -atomic_inc (&new->user->processes); p->cred = p->real_cred =get_cred (new); +inc_rlimit_ucounts (task_ucounts (p),UCOUNT_RLIMIT_NPROC ,1 );alter_cred_subscribers (new,2 );validate_creds (new);return 0 ;
@@ -496,12 +496,12 @@ int commit_creds(struct cred *new)* in set_user(). */ alter_cred_subscribers (new,2 ); -if (new->user != old->user) -atomic_inc (&new->user->processes); +if (new->user != old->user || new->user_ns != old->user_ns) +inc_rlimit_ucounts (new->ucounts,UCOUNT_RLIMIT_NPROC ,1 );rcu_assign_pointer (task->real_cred, new);rcu_assign_pointer (task->cred, new);if (new->user != old->user) -atomic_dec (&old->user->processes); +dec_rlimit_ucounts (old->ucounts,UCOUNT_RLIMIT_NPROC ,1 );alter_cred_subscribers (old,-2 );/* send notifications */
kernel/exit.c +1 -1
@@ -188,7 +188,7 @@ void release_task(struct task_struct *p)/* don't need to get the RCU readlock here - the process is dead and * can't be modifying its own credentials. But shut RCU-lockdep up */ rcu_read_lock (); -atomic_dec (&__task_cred (p)->user->processes); +dec_rlimit_ucounts (task_ucounts (p),UCOUNT_RLIMIT_NPROC ,1 );rcu_read_unlock ();cgroup_release (p);
kernel/fork.c +5 -4
@@ -819,9 +819,11 @@ void __init fork_init(void) init_task.signal->rlim[RLIMIT_SIGPENDING ] = init_task.signal->rlim[RLIMIT_NPROC ]; -for (i =0 ; i <UCOUNT_COUNTS ; i++) +for (i =0 ; i <MAX_PER_NAMESPACE_UCOUNTS ; i++) init_user_ns.ucount_max[i] = max_threads/2 ; + init_user_ns.ucount_max[UCOUNT_RLIMIT_NPROC ] =task_rlimit (&init_task,RLIMIT_NPROC ); +#ifdef CONFIG_VMAP_STACK cpuhp_setup_state (CPUHP_BP_PREPARE_DYN ,"fork:vm_stack_cache" ,NULL , free_vm_stack_cache);
@@ -1972,8 +1974,7 @@ static __latent_entropy struct task_struct *copy_process(DEBUG_LOCKS_WARN_ON (!p->softirqs_enabled);#endif retval = -EAGAIN ; -if (atomic_read (&p->real_cred->user->processes) >= -task_rlimit (p,RLIMIT_NPROC )) { +if (is_ucounts_overlimit (task_ucounts (p),UCOUNT_RLIMIT_NPROC ,rlimit (RLIMIT_NPROC ))) {if (p->real_cred->user !=INIT_USER && !capable (CAP_SYS_RESOURCE ) && !capable (CAP_SYS_ADMIN )) goto bad_fork_free;
@@ -2376,7 +2377,7 @@ static __latent_entropy struct task_struct *copy_process(#endif delayacct_tsk_free (p); bad_fork_cleanup_count: -atomic_dec (&p->cred->user->processes); +dec_rlimit_ucounts (task_ucounts (p),UCOUNT_RLIMIT_NPROC ,1 );exit_creds (p); bad_fork_free: p->state =TASK_DEAD ;
kernel/sys.c +1 -1
@@ -473,7 +473,7 @@ static int set_user(struct cred *new)* for programs doing set*uid()+execve() by harmlessly deferring the * failure to the execve() stage. */ -if (atomic_read (&new_user->processes) >=rlimit (RLIMIT_NPROC ) && +if (is_ucounts_overlimit (new->ucounts,UCOUNT_RLIMIT_NPROC ,rlimit (RLIMIT_NPROC )) && new_user !=INIT_USER ) current->flags |=PF_NPROC_EXCEEDED ;else
kernel/ucount.c +44 -0
62 lines not on this page · raw message
kernel/user.c +0 -1
@@ -98,7 +98,6 @@ static DEFINE_SPINLOCK(uidhash_lock);/* root_user.__count is 1, for init task cred */ struct user_struct root_user = { .__count =REFCOUNT_INIT (1 ), - .processes =ATOMIC_INIT (1 ), .sigpending =ATOMIC_INIT (0 ), .locked_shm =0 , .uid =GLOBAL_ROOT_UID ,
kernel/user_namespace.c +2 -1
@@ -118,9 +118,10 @@ int create_user_ns(struct cred *new) ns->owner = owner; ns->group = group;INIT_WORK (&ns->work, free_user_ns); -for (i =0 ; i <UCOUNT_COUNTS ; i++) { +for (i =0 ; i <MAX_PER_NAMESPACE_UCOUNTS ; i++) { ns->ucount_max[i] =INT_MAX ; } + ns->ucount_max[UCOUNT_RLIMIT_NPROC ] =rlimit (RLIMIT_NPROC ); ns->ucounts = ucounts;/* Inherit USERNS_SETGROUPS_ALLOWED from our parent */
Signature · 1 line
2.29.3
Dan Carpenter
build botRe: [PATCH v11 4/9] Reimplement RLIMIT_NPROC on top of ucounts
CC: kbuild-all(a)lists.01.org In-Reply-To: <c5286a8aa16d2d698c222f7532f3d735c82bc6bc.1619094428.git.legion@kernel.org> References…
CC: kbuild-all(a)lists.01.org
In-Reply-To: <c5286a8aa16d2d698c222f7532f3d735c82bc6bc.1619094428.git.legion@kernel.org>
References: <c5286a8aa16d2d698c222f7532f3d735c82bc6bc.1619094428.git.legion@kernel.org>
TO: legion(a)kernel.org
TO: LKML <[email protected]>
TO: Kernel Hardening <[email protected]>
TO: Linux Containers <[email protected]>
TO: linux-mm(a)kvack.org
CC: Alexey Gladkov <[email protected]>
CC: Andrew Morton <[email protected]>
CC: Linux Memory Management List <[email protected]>
CC: Christian Brauner <[email protected]>
CC: "Eric W . Biederman" <[email protected]>
CC: Jann Horn <[email protected]>
Hi,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on kselftest/next]
[also build test WARNING on linux/master linus/master v5.12] [cannot apply to hnaz-linux-mm/master next-20210427]
[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/legion-kernel-org/Count-rlimits-in-each-user-namespace/20210427-162857 base: https://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest.git next
:::::: branch date: 6 hours ago
:::::: commit date: 6 hours ago
config: arc-randconfig-m031-20210426 (attached as .config)
compiler: arceb-elf-gcc (GCC) 9.3.0
If you fix the issue, kindly add following tag as appropriate
smatch warnings:kernel/ucount.c:270 dec_rlimit_ucounts() error: uninitialized symbol 'new'.
vim +/new +270 kernel/ucount.c
176ec2b092cc22 Alexey Gladkov 2021-04-22 259
176ec2b092cc22 Alexey Gladkov 2021-04-22 260 bool dec_rlimit_ucounts(struct ucounts *ucounts, enum ucount_type type, long v)
176ec2b092cc22 Alexey Gladkov 2021-04-22 261 {
176ec2b092cc22 Alexey Gladkov 2021-04-22 262 struct ucounts *iter;
176ec2b092cc22 Alexey Gladkov 2021-04-22 263 long new;
176ec2b092cc22 Alexey Gladkov 2021-04-22 264 for (iter = ucounts; iter; iter = iter->ns->ucounts) {
176ec2b092cc22 Alexey Gladkov 2021-04-22 265 long dec = atomic_long_add_return(-v, &iter->ucount[type]);
176ec2b092cc22 Alexey Gladkov 2021-04-22 266 WARN_ON_ONCE(dec < 0);
176ec2b092cc22 Alexey Gladkov 2021-04-22 267 if (iter == ucounts)
176ec2b092cc22 Alexey Gladkov 2021-04-22 268 new = dec;
176ec2b092cc22 Alexey Gladkov 2021-04-22 269 }176ec2b092cc22 Alexey Gladkov 2021-04-22 @270 return (new == 0);176ec2b092cc22 Alexey Gladkov 2021-04-22 271 }176ec2b092cc22 Alexey Gladkov 2021-04-22 272
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]
PATCH 1/1 ucounts: Silence warning in dec_rlimit_ucounts
Quoted 29 lines · Dan Carpenter
url: https://github.com/0day-ci/linux/commits/legion-kernel-org/Count-rlimits-in-each-user-namespace/20210427-162857 base: https://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest.git next config: arc-randconfig-m031-20210426 (attached as .config) compiler: arceb-elf-gcc (GCC) 9.3.0If you fix the issue, kindly add following tag as appropriate
Reported-by kernel test robot <[email protected]>Reported-by Dan Carpenter <[email protected]>smatch warnings:
kernel/ucount.c:270dec_rlimit_ucounts()error: uninitialized symbol 'new'.vim +/new +270
kernel/ucount.c176ec2b092cc22 Alexey Gladkov 2021-04-22 260 bool dec_rlimit_ucounts(struct ucounts *ucounts, enum ucount_type type, long v) 176ec2b092cc22 Alexey Gladkov 2021-04-22 261 { 176ec2b092cc22 Alexey Gladkov 2021-04-22 262 struct ucounts *iter; 176ec2b092cc22 Alexey Gladkov 2021-04-22 263 long new; ^^^^^^^^176ec2b092cc22 Alexey Gladkov 2021-04-22 264 for (iter = ucounts; iter; iter = iter->ns->ucounts) { 176ec2b092cc22 Alexey Gladkov 2021-04-22 265 long dec = atomic_long_add_return(-v, &iter->ucount[type]); 176ec2b092cc22 Alexey Gladkov 2021-04-22 266 WARN_ON_ONCE(dec < 0); 176ec2b092cc22 Alexey Gladkov 2021-04-22 267 if (iter == ucounts) 176ec2b092cc22 Alexey Gladkov 2021-04-22 268 new = dec; 176ec2b092cc22 Alexey Gladkov 2021-04-22 269 } 176ec2b092cc22 Alexey Gladkov 2021-04-22 @270 return (new == 0); ^^^^^^^^
Quoted 4 lines
I don't know if this is a bug or not, but I can definitely tell why the static checker complains about it.
176ec2b092cc22Alexey Gladkov 2021-04-22 271 }
In the only two cases that care about the return value of dec_rlimit_ucounts the code first tests to see that ucounts is not NULL. In those cases it is guaranteed at least one iteration of the loop will execute guaranteeing the variable new will be initialized.
Initialize new to -1 so that the return value is well defined even when the loop does not execute and the static checker is silenced.
--- kernel/ucount.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/ucount.c +1 -1
@@ -263,7 +263,7 @@ long inc_rlimit_ucounts(struct ucounts *ucounts, enum ucount_type type, long v)bool dec_rlimit_ucounts (struct ucounts *ucounts,enum ucount_type type,long v) {struct ucounts *iter; -long new; +long new =-1 ;/* Silence compiler warnings */ for (iter = ucounts; iter; iter = iter->ns->ucounts) {long dec =atomic_long_add_return (-v, &iter->ucount[type]);WARN_ON_ONCE (dec <0 );
Signature · 1 line
2.30.1
PATCH v11 5/9 Reimplement RLIMIT_MSGQUEUE on top of ucounts
In reply to [email protected]From: Alexey Gladkov <[email protected]>
The rlimit counter is tied to uid in the user_namespace. This allows rlimit values to be specified in userns even if they are already globally exceeded by the user. However, the value of the previous user_namespaces cannot be exceeded.
--- include/linux/sched/user.h | 4 ---- include/linux/user_namespace.h | 1 + ipc/mqueue.c | 40 ++++++++++++++++++---------------- kernel/fork.c | 1 + kernel/ucount.c | 1 + kernel/user_namespace.c | 1 + 6 files changed, 25 insertions(+), 23 deletions(-)
include/linux/sched/user.h +0 -4
@@ -18,10 +18,6 @@ struct user_struct {#endif #ifdef CONFIG_EPOLL atomic_long_t epoll_watches;/* The number of file descriptors currently watched */ -#endif -#ifdef CONFIG_POSIX_MQUEUE -/* protected by mq_lock */ -unsigned long mq_bytes;/* How many bytes can be allocated to mqueue? */ #endif unsigned long locked_shm;/* How many pages of mlocked shm ? */ unsigned long unix_inflight;/* How many files in flight in unix sockets */
include/linux/user_namespace.h +1 -0
@@ -51,6 +51,7 @@ enum ucount_type {UCOUNT_INOTIFY_WATCHES , #endifUCOUNT_RLIMIT_NPROC , +UCOUNT_RLIMIT_MSGQUEUE , UCOUNT_COUNTS, };
ipc/mqueue.c +21 -19
@@ -144,7 +144,7 @@ struct mqueue_inode_info {struct pid *notify_owner;u32 notify_self_exec_id;struct user_namespace *notify_user_ns; -struct user_struct *user;/* user who created, for accounting */ +struct ucounts *ucounts;/* user who created, for accounting */ struct sock *notify_sock;struct sk_buff *notify_cookie;
@@ -292,7 +292,6 @@ static struct inode *mqueue_get_inode(struct super_block *sb, struct ipc_namespace *ipc_ns, umode_t mode, struct mq_attr *attr) { - struct user_struct *u = current_user(); struct inode *inode;int ret = -ENOMEM ;
@@ -321,7 +320,7 @@ static struct inode *mqueue_get_inode(struct super_block *sb, info->notify_owner =NULL ; info->notify_user_ns =NULL ; info->qsize =0 ; - info->user =NULL ;/* set when all is ok */ + info->ucounts =NULL ;/* set when all is ok */ info->msg_tree =RB_ROOT ; info->msg_tree_rightmost =NULL ; info->node_cache =NULL ;
@@ -371,19 +370,23 @@ static struct inode *mqueue_get_inode(struct super_block *sb,if (mq_bytes + mq_treesize < mq_bytes) goto out_inode; mq_bytes += mq_treesize; -spin_lock (&mq_lock); -if (u->mq_bytes + mq_bytes < u->mq_bytes || - u->mq_bytes + mq_bytes >rlimit (RLIMIT_MSGQUEUE )) { + info->ucounts =get_ucounts (current_ucounts ()); +if (info->ucounts) { +long msgqueue; + +spin_lock (&mq_lock); + msgqueue =inc_rlimit_ucounts (info->ucounts,UCOUNT_RLIMIT_MSGQUEUE , mq_bytes); +if (msgqueue ==LONG_MAX || msgqueue >rlimit (RLIMIT_MSGQUEUE )) { +dec_rlimit_ucounts (info->ucounts,UCOUNT_RLIMIT_MSGQUEUE , mq_bytes); +spin_unlock (&mq_lock); +put_ucounts (info->ucounts); + info->ucounts =NULL ; +/* mqueue_evict_inode() releases info->messages */ + ret = -EMFILE ; + goto out_inode; + }spin_unlock (&mq_lock); -/* mqueue_evict_inode() releases info->messages */ - ret = -EMFILE ; - goto out_inode; } - u->mq_bytes += mq_bytes; -spin_unlock (&mq_lock); - -/* all is ok */ - info->user =get_uid (u); }else if (S_ISDIR (mode)) {inc_nlink (inode);/* Some things misbehave if size == 0 on a directory */
@@ -497,7 +500,6 @@ static void mqueue_free_inode(struct inode *inode)static void mqueue_evict_inode (struct inode *inode) {struct mqueue_inode_info *info; -struct user_struct *user;struct ipc_namespace *ipc_ns;struct msg_msg *msg, *nmsg;LIST_HEAD (tmp_msg);
@@ -520,8 +522,7 @@ static void mqueue_evict_inode(struct inode *inode)free_msg (msg); } - user = info->user; -if (user) { +if (info->ucounts) {unsigned long mq_bytes, mq_treesize;/* Total amount of bytes accounted for the mqueue */
@@ -533,7 +534,7 @@ static void mqueue_evict_inode(struct inode *inode) info->attr.mq_msgsize);spin_lock (&mq_lock); - user->mq_bytes -= mq_bytes; +dec_rlimit_ucounts (info->ucounts,UCOUNT_RLIMIT_MSGQUEUE , mq_bytes);/* * get_ns_from_inode() ensures that the * (ipc_ns = sb->s_fs_info) is either a valid ipc_ns
@@ -543,7 +544,8 @@ static void mqueue_evict_inode(struct inode *inode)if (ipc_ns) ipc_ns->mq_queues_count--;spin_unlock (&mq_lock); -free_uid (user); +put_ucounts (info->ucounts); + info->ucounts =NULL ; }if (ipc_ns)put_ipc_ns (ipc_ns);
kernel/fork.c +1 -0
@@ -823,6 +823,7 @@ void __init fork_init(void) init_user_ns.ucount_max[i] = max_threads/2 ; init_user_ns.ucount_max[UCOUNT_RLIMIT_NPROC ] =task_rlimit (&init_task,RLIMIT_NPROC ); + init_user_ns.ucount_max[UCOUNT_RLIMIT_MSGQUEUE ] =task_rlimit (&init_task,RLIMIT_MSGQUEUE );#ifdef CONFIG_VMAP_STACK cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "fork:vm_stack_cache",
kernel/ucount.c +1 -0
@@ -80,6 +80,7 @@ static struct ctl_table user_table[] = {UCOUNT_ENTRY ("max_inotify_instances" ), UCOUNT_ENTRY("max_inotify_watches"), #endif + { }, { }, { } };
kernel/user_namespace.c +1 -0
@@ -122,6 +122,7 @@ int create_user_ns(struct cred *new) ns->ucount_max[i] =INT_MAX ; } ns->ucount_max[UCOUNT_RLIMIT_NPROC ] =rlimit (RLIMIT_NPROC ); + ns->ucount_max[UCOUNT_RLIMIT_MSGQUEUE ] =rlimit (RLIMIT_MSGQUEUE ); ns->ucounts = ucounts;/* Inherit USERNS_SETGROUPS_ALLOWED from our parent */
Signature · 1 line
2.29.3
PATCH v11 6/9 Reimplement RLIMIT_SIGPENDING on top of ucounts
In reply to [email protected]From: Alexey Gladkov <[email protected]>
The rlimit counter is tied to uid in the user_namespace. This allows rlimit values to be specified in userns even if they are already globally exceeded by the user. However, the value of the previous user_namespaces cannot be exceeded.
Changelog
v11:
* Revert most of changes to fix performance issues.
v10:
* Fix memory leak on get_ucounts failure.
--- fs/proc/array.c | 2 +- include/linux/sched/user.h | 1 - include/linux/signal_types.h | 4 +++- include/linux/user_namespace.h | 1 + kernel/fork.c | 1 + kernel/signal.c | 25 +++++++++++++------------ kernel/ucount.c | 1 + kernel/user.c | 1 - kernel/user_namespace.c | 1 + 9 files changed, 21 insertions(+), 16 deletions(-)
fs/proc/array.c +1 -1
@@ -284,7 +284,7 @@ static inline void task_sig(struct seq_file *m, struct task_struct *p)collect_sigign_sigcatch (p, &ignored, &caught); num_threads =get_nr_threads (p);rcu_read_lock ();/* FIXME: is this correct? */ - qsize =atomic_read (&__task_cred (p)->user->sigpending); + qsize =get_ucounts_value (task_ucounts (p),UCOUNT_RLIMIT_SIGPENDING );rcu_read_unlock (); qlim =task_rlimit (p,RLIMIT_SIGPENDING );unlock_task_sighand (p, &flags);
include/linux/sched/user.h +0 -1
@@ -12,7 +12,6 @@*/ struct user_struct {refcount_t __count;/* reference count */ -atomic_t sigpending;/* How many pending signals does this user have? */ #ifdef CONFIG_FANOTIFY atomic_t fanotify_listeners;#endif
include/linux/signal_types.h +3 -1
@@ -13,6 +13,8 @@ typedef struct kernel_siginfo { __SIGINFO; } kernel_siginfo_t; +struct ucounts ; +/* * Real Time signals may be queued. */
@@ -21,7 +23,7 @@ struct sigqueue {struct list_head list;int flags;kernel_siginfo_t info; -struct user_struct *user; +struct ucounts *ucounts; };/* flags values. */
include/linux/user_namespace.h +1 -0
@@ -52,6 +52,7 @@ enum ucount_type {#endif UCOUNT_RLIMIT_NPROC ,UCOUNT_RLIMIT_MSGQUEUE , +UCOUNT_RLIMIT_SIGPENDING , UCOUNT_COUNTS, };
kernel/fork.c +1 -0
@@ -824,6 +824,7 @@ void __init fork_init(void) init_user_ns.ucount_max[UCOUNT_RLIMIT_NPROC ] =task_rlimit (&init_task,RLIMIT_NPROC ); init_user_ns.ucount_max[UCOUNT_RLIMIT_MSGQUEUE ] =task_rlimit (&init_task,RLIMIT_MSGQUEUE ); + init_user_ns.ucount_max[UCOUNT_RLIMIT_SIGPENDING ] =task_rlimit (&init_task,RLIMIT_SIGPENDING );#ifdef CONFIG_VMAP_STACK cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "fork:vm_stack_cache",
kernel/signal.c +13 -12
@@ -414,8 +414,8 @@ static struct sigqueue * __sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimit) { struct sigqueue *q = NULL; -struct user_struct *user; -int sigpending; +struct ucounts *ucounts =NULL ; +long sigpending;/* * Protect access to @t credentials. This can go away when all
@@ -426,27 +426,26 @@ __sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimi* changes from/to zero. */ rcu_read_lock (); - user =__task_cred (t)->user; - sigpending =atomic_inc_return (&user->sigpending); + ucounts =task_ucounts (t); + sigpending =inc_rlimit_ucounts (ucounts,UCOUNT_RLIMIT_SIGPENDING ,1 );if (sigpending ==1 ) -get_uid (user); + ucounts =get_ucounts (ucounts);rcu_read_unlock (); -if (override_rlimit ||likely (sigpending <=task_rlimit (t,RLIMIT_SIGPENDING ))) { +if (override_rlimit || (sigpending <LONG_MAX && sigpending <=task_rlimit (t,RLIMIT_SIGPENDING ))) { q =kmem_cache_alloc (sigqueue_cachep, flags); }else {print_dropped_signal (sig); }if (unlikely (q ==NULL )) { -if (atomic_dec_and_test (&user->sigpending)) -free_uid (user); +if (ucounts &&dec_rlimit_ucounts (ucounts,UCOUNT_RLIMIT_SIGPENDING ,1 )) +put_ucounts (ucounts); }else {INIT_LIST_HEAD (&q->list); q->flags =0 ; - q->user = user; + q->ucounts = ucounts; } -return q; }
@@ -454,8 +453,10 @@ static void __sigqueue_free(struct sigqueue *q) {if (q->flags &SIGQUEUE_PREALLOC )return ; -if (atomic_dec_and_test (&q->user->sigpending)) -free_uid (q->user); +if (q->ucounts &&dec_rlimit_ucounts (q->ucounts,UCOUNT_RLIMIT_SIGPENDING ,1 )) { +put_ucounts (q->ucounts); + q->ucounts =NULL ; + }kmem_cache_free (sigqueue_cachep, q); }
kernel/ucount.c +1 -0
@@ -80,6 +80,7 @@ static struct ctl_table user_table[] = {UCOUNT_ENTRY ("max_inotify_instances" ), UCOUNT_ENTRY("max_inotify_watches"), #endif + { }, { }, { }, { }
kernel/user.c +0 -1
@@ -98,7 +98,6 @@ static DEFINE_SPINLOCK(uidhash_lock);/* root_user.__count is 1, for init task cred */ struct user_struct root_user = { .__count =REFCOUNT_INIT (1 ), - .sigpending =ATOMIC_INIT (0 ), .locked_shm =0 , .uid =GLOBAL_ROOT_UID , .ratelimit =RATELIMIT_STATE_INIT (root_user.ratelimit,0 ,0 ),
kernel/user_namespace.c +1 -0
@@ -123,6 +123,7 @@ int create_user_ns(struct cred *new) } ns->ucount_max[UCOUNT_RLIMIT_NPROC ] =rlimit (RLIMIT_NPROC ); ns->ucount_max[UCOUNT_RLIMIT_MSGQUEUE ] =rlimit (RLIMIT_MSGQUEUE ); + ns->ucount_max[UCOUNT_RLIMIT_SIGPENDING ] =rlimit (RLIMIT_SIGPENDING ); ns->ucounts = ucounts;/* Inherit USERNS_SETGROUPS_ALLOWED from our parent */
Signature · 1 line
2.29.3
PATCH v11 7/9 Reimplement RLIMIT_MEMLOCK on top of ucounts
In reply to [email protected]From: Alexey Gladkov <[email protected]>
The rlimit counter is tied to uid in the user_namespace. This allows rlimit values to be specified in userns even if they are already globally exceeded by the user. However, the value of the previous user_namespaces cannot be exceeded.
Changelog
v11:
* Fix issue found by lkp robot.
v8:
* Fix issues found by lkp-tests project.
v7:
* Keep only ucounts for RLIMIT_MEMLOCK checks instead of struct cred.
v6:
* Fix bug in hugetlb_file_setup() detected by trinity.
--- fs/hugetlbfs/inode.c | 16 ++++++++-------- include/linux/hugetlb.h | 4 ++-- include/linux/mm.h | 4 ++-- include/linux/sched/user.h | 1 - include/linux/shmem_fs.h | 2 +- include/linux/user_namespace.h | 1 + ipc/shm.c | 26 +++++++++++++------------- kernel/fork.c | 1 + kernel/ucount.c | 1 + kernel/user.c | 1 - kernel/user_namespace.c | 1 + mm/memfd.c | 4 ++-- mm/mlock.c | 22 ++++++++++++++-------- mm/mmap.c | 4 ++-- mm/shmem.c | 10 +++++----- 15 files changed, 53 insertions(+), 45 deletions(-)
fs/hugetlbfs/inode.c +8 -8
@@ -1443,7 +1443,7 @@ static int get_hstate_idx(int page_size_log)* otherwise hugetlb_reserve_pages reserves one less hugepages than intended. */ struct file *hugetlb_file_setup (const char *name,size_t size, -vm_flags_t acctflag,struct user_struct **user, +vm_flags_t acctflag,struct ucounts **ucounts,int creat_flags,int page_size_log) {struct inode *inode;
@@ -1455,20 +1455,20 @@ struct file *hugetlb_file_setup(const char *name, size_t size,if (hstate_idx <0 )return ERR_PTR (-ENODEV ); - *user =NULL ; + *ucounts =NULL ; mnt = hugetlbfs_vfsmount[hstate_idx];if (!mnt)return ERR_PTR (-ENOENT );if (creat_flags ==HUGETLB_SHMFS_INODE && !can_do_hugetlb_shm ()) { - *user =current_user (); -if (user_shm_lock (size, *user)) { + *ucounts =current_ucounts (); +if (user_shm_lock (size, *ucounts)) {task_lock (current);pr_warn_once ("%s (%d): Using mlock ulimits for SHM_HUGETLB is deprecated\n" , current->comm, current->pid);task_unlock (current); }else { - *user =NULL ; + *ucounts =NULL ;return ERR_PTR (-EPERM ); } }
@@ -1495,9 +1495,9 @@ struct file *hugetlb_file_setup(const char *name, size_t size,iput (inode); out: -if (*user) { -user_shm_unlock (size, *user); - *user =NULL ; +if (*ucounts) { +user_shm_unlock (size, *ucounts); + *ucounts =NULL ; }return file; }
include/linux/hugetlb.h +2 -2
@@ -434,7 +434,7 @@ static inline struct hugetlbfs_inode_info *HUGETLBFS_I(struct inode *inode)extern const struct file_operations hugetlbfs_file_operations;extern const struct vm_operations_struct hugetlb_vm_ops;struct file *hugetlb_file_setup (const char *name,size_t size,vm_flags_t acct, -struct user_struct **user,int creat_flags, +struct ucounts **ucounts,int creat_flags,int page_size_log);static inline bool is_file_hugepages (struct file *file)
@@ -454,7 +454,7 @@ static inline struct hstate *hstate_inode(struct inode *i)#define is_file_hugepages (file) falsestatic inline struct file *hugetlb_file_setup (const char *name,size_t size,vm_flags_t acctflag, -struct user_struct **user,int creat_flags, +struct ucounts **ucounts,int creat_flags,int page_size_log) {return ERR_PTR (-ENOSYS );
include/linux/mm.h +2 -2
@@ -1658,8 +1658,8 @@ extern bool can_do_mlock(void);#else static inline bool can_do_mlock (void ) {return false; }#endif -extern int user_shm_lock (size_t ,struct user_struct *); -extern void user_shm_unlock (size_t ,struct user_struct *); +extern int user_shm_lock (size_t ,struct ucounts *); +extern void user_shm_unlock (size_t ,struct ucounts *);/* * Parameter block passed down to zap_pte_range in exceptional cases.
include/linux/sched/user.h +0 -1
@@ -18,7 +18,6 @@ struct user_struct {#ifdef CONFIG_EPOLL atomic_long_t epoll_watches;/* The number of file descriptors currently watched */ #endif -unsigned long locked_shm;/* How many pages of mlocked shm ? */ unsigned long unix_inflight;/* How many files in flight in unix sockets */ atomic_long_t pipe_bufs;/* how many pages are allocated in pipe buffers */
include/linux/shmem_fs.h +1 -1
@@ -65,7 +65,7 @@ extern struct file *shmem_file_setup_with_mnt(struct vfsmount *mnt,extern int shmem_zero_setup (struct vm_area_struct *);extern unsigned long shmem_get_unmapped_area (struct file *,unsigned long addr,unsigned long len,unsigned long pgoff,unsigned long flags); -extern int shmem_lock (struct file *file,int lock,struct user_struct *user); +extern int shmem_lock (struct file *file,int lock,struct ucounts *ucounts);#ifdef CONFIG_SHMEM extern const struct address_space_operations shmem_aops;static inline bool shmem_mapping (struct address_space *mapping)
include/linux/user_namespace.h +1 -0
@@ -53,6 +53,7 @@ enum ucount_type {UCOUNT_RLIMIT_NPROC ,UCOUNT_RLIMIT_MSGQUEUE ,UCOUNT_RLIMIT_SIGPENDING , +UCOUNT_RLIMIT_MEMLOCK , UCOUNT_COUNTS, };
ipc/shm.c +13 -13
@@ -60,7 +60,7 @@ struct shmid_kernel /* private to the kernel */time64_t shm_ctim;struct pid *shm_cprid;struct pid *shm_lprid; -struct user_struct *mlock_user; +struct ucounts *mlock_ucounts;/* The task created the shm object. NULL if the task is dead. */ struct task_struct *shm_creator;
@@ -286,10 +286,10 @@ static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp)shm_rmid (ns, shp);shm_unlock (shp);if (!is_file_hugepages (shm_file)) -shmem_lock (shm_file,0 , shp->mlock_user); -else if (shp->mlock_user) +shmem_lock (shm_file,0 , shp->mlock_ucounts); +else if (shp->mlock_ucounts)user_shm_unlock (i_size_read (file_inode (shm_file)), - shp->mlock_user); + shp->mlock_ucounts);fput (shm_file);ipc_update_pid (&shp->shm_cprid,NULL );ipc_update_pid (&shp->shm_lprid,NULL );
@@ -625,7 +625,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) shp->shm_perm.key = key; shp->shm_perm.mode = (shmflg &S_IRWXUGO ); - shp->mlock_user =NULL ; + shp->mlock_ucounts =NULL ; shp->shm_perm.security =NULL ; error =security_shm_alloc (&shp->shm_perm);
@@ -650,7 +650,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)if (shmflg &SHM_NORESERVE ) acctflag =VM_NORESERVE ; file =hugetlb_file_setup (name, hugesize, acctflag, - &shp->mlock_user,HUGETLB_SHMFS_INODE , + &shp->mlock_ucounts,HUGETLB_SHMFS_INODE , (shmflg >>SHM_HUGE_SHIFT ) &SHM_HUGE_MASK ); }else {/*
@@ -698,8 +698,8 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) no_id:ipc_update_pid (&shp->shm_cprid,NULL );ipc_update_pid (&shp->shm_lprid,NULL ); -if (is_file_hugepages (file) && shp->mlock_user) -user_shm_unlock (size, shp->mlock_user); +if (is_file_hugepages (file) && shp->mlock_ucounts) +user_shm_unlock (size, shp->mlock_ucounts);fput (file);ipc_rcu_putref (&shp->shm_perm, shm_rcu_free);return error;
@@ -1105,12 +1105,12 @@ static int shmctl_do_lock(struct ipc_namespace *ns, int shmid, int cmd) goto out_unlock0;if (cmd ==SHM_LOCK ) { -struct user_struct *user =current_user (); +struct ucounts *ucounts =current_ucounts (); - err =shmem_lock (shm_file,1 , user); + err =shmem_lock (shm_file,1 , ucounts);if (!err && !(shp->shm_perm.mode &SHM_LOCKED )) { shp->shm_perm.mode |=SHM_LOCKED ; - shp->mlock_user = user; + shp->mlock_ucounts = ucounts; } goto out_unlock0; }
@@ -1118,9 +1118,9 @@ static int shmctl_do_lock(struct ipc_namespace *ns, int shmid, int cmd)/* SHM_UNLOCK */ if (!(shp->shm_perm.mode &SHM_LOCKED )) goto out_unlock0; -shmem_lock (shm_file,0 , shp->mlock_user); +shmem_lock (shm_file,0 , shp->mlock_ucounts); shp->shm_perm.mode &= ~SHM_LOCKED ; - shp->mlock_user =NULL ; + shp->mlock_ucounts =NULL ;get_file (shm_file);ipc_unlock_object (&shp->shm_perm);rcu_read_unlock ();
kernel/fork.c +1 -0
@@ -825,6 +825,7 @@ void __init fork_init(void) init_user_ns.ucount_max[UCOUNT_RLIMIT_NPROC ] =task_rlimit (&init_task,RLIMIT_NPROC ); init_user_ns.ucount_max[UCOUNT_RLIMIT_MSGQUEUE ] =task_rlimit (&init_task,RLIMIT_MSGQUEUE ); init_user_ns.ucount_max[UCOUNT_RLIMIT_SIGPENDING ] =task_rlimit (&init_task,RLIMIT_SIGPENDING ); + init_user_ns.ucount_max[UCOUNT_RLIMIT_MEMLOCK ] =task_rlimit (&init_task,RLIMIT_MEMLOCK );#ifdef CONFIG_VMAP_STACK cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "fork:vm_stack_cache",
kernel/ucount.c +1 -0
@@ -83,6 +83,7 @@ static struct ctl_table user_table[] = { { }, { }, { }, + { }, { } };#endif /* CONFIG_SYSCTL */
kernel/user.c +0 -1
@@ -98,7 +98,6 @@ static DEFINE_SPINLOCK(uidhash_lock);/* root_user.__count is 1, for init task cred */ struct user_struct root_user = { .__count =REFCOUNT_INIT (1 ), - .locked_shm =0 , .uid =GLOBAL_ROOT_UID , .ratelimit =RATELIMIT_STATE_INIT (root_user.ratelimit,0 ,0 ), };
kernel/user_namespace.c +1 -0
@@ -124,6 +124,7 @@ int create_user_ns(struct cred *new) ns->ucount_max[UCOUNT_RLIMIT_NPROC ] =rlimit (RLIMIT_NPROC ); ns->ucount_max[UCOUNT_RLIMIT_MSGQUEUE ] =rlimit (RLIMIT_MSGQUEUE ); ns->ucount_max[UCOUNT_RLIMIT_SIGPENDING ] =rlimit (RLIMIT_SIGPENDING ); + ns->ucount_max[UCOUNT_RLIMIT_MEMLOCK ] =rlimit (RLIMIT_MEMLOCK ); ns->ucounts = ucounts;/* Inherit USERNS_SETGROUPS_ALLOWED from our parent */
mm/memfd.c +2 -2
@@ -297,9 +297,9 @@ SYSCALL_DEFINE2(memfd_create, }if (flags &MFD_HUGETLB ) { -struct user_struct *user =NULL ; +struct ucounts *ucounts =NULL ; - file =hugetlb_file_setup (name,0 ,VM_NORESERVE , &user, + file =hugetlb_file_setup (name,0 ,VM_NORESERVE , &ucounts,HUGETLB_ANONHUGE_INODE , (flags >>MFD_HUGE_SHIFT ) &MFD_HUGE_MASK );
mm/mlock.c +14 -8
@@ -817,9 +817,10 @@ SYSCALL_DEFINE0(munlockall)*/ static DEFINE_SPINLOCK (shmlock_user_lock); -int user_shm_lock (size_t size,struct user_struct *user) +int user_shm_lock (size_t size,struct ucounts *ucounts) {unsigned long lock_limit, locked; +long memlock;int allowed =0 ; locked = (size +PAGE_SIZE -1 ) >>PAGE_SHIFT ;
@@ -828,21 +829,26 @@ int user_shm_lock(size_t size, struct user_struct *user) allowed =1 ; lock_limit >>=PAGE_SHIFT ;spin_lock (&shmlock_user_lock); -if (!allowed && - locked + user->locked_shm > lock_limit && !capable (CAP_IPC_LOCK )) + memlock =inc_rlimit_ucounts (ucounts,UCOUNT_RLIMIT_MEMLOCK , locked); + +if (!allowed && (memlock ==LONG_MAX || memlock > lock_limit) && !capable (CAP_IPC_LOCK )) { +dec_rlimit_ucounts (ucounts,UCOUNT_RLIMIT_MEMLOCK , locked); + goto out; + } +if (!get_ucounts (ucounts)) { +dec_rlimit_ucounts (ucounts,UCOUNT_RLIMIT_MEMLOCK , locked); goto out; -get_uid (user); - user->locked_shm += locked; + } allowed =1 ; out:spin_unlock (&shmlock_user_lock);return allowed; } -void user_shm_unlock (size_t size,struct user_struct *user) +void user_shm_unlock (size_t size,struct ucounts *ucounts) {spin_lock (&shmlock_user_lock); - user->locked_shm -= (size +PAGE_SIZE -1 ) >>PAGE_SHIFT ; +dec_rlimit_ucounts (ucounts,UCOUNT_RLIMIT_MEMLOCK , (size +PAGE_SIZE -1 ) >>PAGE_SHIFT );spin_unlock (&shmlock_user_lock); -free_uid (user); +put_ucounts (ucounts); }
mm/mmap.c +2 -2
@@ -1605,7 +1605,7 @@ unsigned long ksys_mmap_pgoff(unsigned long addr, unsigned long len, goto out_fput; } }else if (flags &MAP_HUGETLB ) { -struct user_struct *user =NULL ; +struct ucounts *ucounts =NULL ;struct hstate *hs; hs =hstate_sizelog ((flags >>MAP_HUGE_SHIFT ) &MAP_HUGE_MASK );
@@ -1621,7 +1621,7 @@ unsigned long ksys_mmap_pgoff(unsigned long addr, unsigned long len,*/ file =hugetlb_file_setup (HUGETLB_ANON_FILE , len,VM_NORESERVE , - &user,HUGETLB_ANONHUGE_INODE , + &ucounts,HUGETLB_ANONHUGE_INODE , (flags >>MAP_HUGE_SHIFT ) &MAP_HUGE_MASK );if (IS_ERR (file))return PTR_ERR (file);
mm/shmem.c +5 -5
@@ -2227,7 +2227,7 @@ static struct mempolicy *shmem_get_policy(struct vm_area_struct *vma, }#endif -int shmem_lock (struct file *file,int lock,struct user_struct *user) +int shmem_lock (struct file *file,int lock,struct ucounts *ucounts) {struct inode *inode =file_inode (file);struct shmem_inode_info *info =SHMEM_I (inode);
@@ -2239,13 +2239,13 @@ int shmem_lock(struct file *file, int lock, struct user_struct *user)* no serialization needed when called from shm_destroy(). */ if (lock && !(info->flags &VM_LOCKED )) { -if (!user_shm_lock (inode->i_size, user)) +if (!user_shm_lock (inode->i_size, ucounts)) goto out_nomem; info->flags |=VM_LOCKED ;mapping_set_unevictable (file->f_mapping); } -if (!lock && (info->flags &VM_LOCKED ) && user) { -user_shm_unlock (inode->i_size, user); +if (!lock && (info->flags &VM_LOCKED ) && ucounts) { +user_shm_unlock (inode->i_size, ucounts); info->flags &= ~VM_LOCKED ;mapping_clear_unevictable (file->f_mapping); }
@@ -4093,7 +4093,7 @@ int shmem_unuse(unsigned int type, bool frontswap,return 0 ; } -int shmem_lock (struct file *file,int lock,struct user_struct *user) +int shmem_lock (struct file *file,int lock,struct ucounts *ucounts) {return 0 ; }
Signature · 1 line
2.29.3
PATCH v11 8/9 kselftests: Add test to check for rlimit changes in different user namespaces
In reply to [email protected]From: Alexey Gladkov <[email protected]>
The testcase runs few instances of the program with RLIMIT_NPROC=1 from user uid=60000, in different user namespaces.
--- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/rlimits/.gitignore | 2 + tools/testing/selftests/rlimits/Makefile | 6 + tools/testing/selftests/rlimits/config | 1 + .../selftests/rlimits/rlimits-per-userns.c | 161 ++++++++++++++++++ 5 files changed, 171 insertions(+) create mode 100644 tools/testing/selftests/rlimits/.gitignore create mode 100644 tools/testing/selftests/rlimits/Makefile create mode 100644 tools/testing/selftests/rlimits/config create mode 100644 tools/testing/selftests/rlimits/rlimits-per-userns.c
tools/testing/selftests/Makefile +1 -0
@@ -48,6 +48,7 @@ TARGETS += proc TARGETS += pstore TARGETS += ptrace TARGETS += openat2 +TARGETS += rlimits TARGETS += rseq TARGETS += rtc TARGETS += seccomp
tools/testing/selftests/rlimits/.gitignore new file +2 -0
@@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +rlimits-per-userns
tools/testing/selftests/rlimits/Makefile new file +6 -0
@@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +CFLAGS += -Wall -O2 -g +TEST_GEN_PROGS := rlimits-per-userns + +include ../lib.mk
tools/testing/selftests/rlimits/config new file +1 -0
@@ -0,0 +1 @@ +CONFIG_USER_NS=y
tools/testing/selftests/rlimits/rlimits-per-userns.c new file +161 -0
161 lines not on this page · raw message
Signature · 1 line
2.29.3
PATCH v11 9/9 ucounts: Set ucount_max to the largest positive value the type can hold
In reply to [email protected]From: Alexey Gladkov <[email protected]>
The ns->ucount_max[] is signed long which is less than the rlimit size. We have to protect ucount_max[] from overflow and only use the largest value that we can hold.
On 32bit using "long" instead of "unsigned long" to hold the counts has the downside that RLIMIT_MSGQUEUE and RLIMIT_MEMLOCK are limited to 2GiB instead of 4GiB. I don't think anyone cares but it should be mentioned in case someone does.
The RLIMIT_NPROC and RLIMIT_SIGPENDING used atomic_t so their maximum hasn't changed.
--- include/linux/user_namespace.h | 6 ++++++ kernel/fork.c | 8 ++++---- kernel/user_namespace.c | 8 ++++---- 3 files changed, 14 insertions(+), 8 deletions(-)
include/linux/user_namespace.h +6 -0
@@ -122,6 +122,12 @@ long inc_rlimit_ucounts(struct ucounts *ucounts, enum ucount_type type, long v);bool dec_rlimit_ucounts (struct ucounts *ucounts,enum ucount_type type,long v);bool is_ucounts_overlimit (struct ucounts *ucounts,enum ucount_type type,unsigned long max); +static inline void set_rlimit_ucount_max (struct user_namespace *ns, +enum ucount_type type,unsigned long max) +{ + ns->ucount_max[type] = max <=LONG_MAX ? max :LONG_MAX ; +} +#ifdef CONFIG_USER_NS static inline struct user_namespace *get_user_ns (struct user_namespace *ns)
kernel/fork.c +4 -4
@@ -822,10 +822,10 @@ void __init fork_init(void)for (i =0 ; i <MAX_PER_NAMESPACE_UCOUNTS ; i++) init_user_ns.ucount_max[i] = max_threads/2 ; - init_user_ns.ucount_max[UCOUNT_RLIMIT_NPROC ] =task_rlimit (&init_task,RLIMIT_NPROC ); - init_user_ns.ucount_max[UCOUNT_RLIMIT_MSGQUEUE ] =task_rlimit (&init_task,RLIMIT_MSGQUEUE ); - init_user_ns.ucount_max[UCOUNT_RLIMIT_SIGPENDING ] =task_rlimit (&init_task,RLIMIT_SIGPENDING ); - init_user_ns.ucount_max[UCOUNT_RLIMIT_MEMLOCK ] =task_rlimit (&init_task,RLIMIT_MEMLOCK ); +set_rlimit_ucount_max (&init_user_ns,UCOUNT_RLIMIT_NPROC ,task_rlimit (&init_task,RLIMIT_NPROC )); +set_rlimit_ucount_max (&init_user_ns,UCOUNT_RLIMIT_MSGQUEUE ,task_rlimit (&init_task,RLIMIT_MSGQUEUE )); +set_rlimit_ucount_max (&init_user_ns,UCOUNT_RLIMIT_SIGPENDING ,task_rlimit (&init_task,RLIMIT_SIGPENDING )); +set_rlimit_ucount_max (&init_user_ns,UCOUNT_RLIMIT_MEMLOCK ,task_rlimit (&init_task,RLIMIT_MEMLOCK ));#ifdef CONFIG_VMAP_STACK cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "fork:vm_stack_cache",
kernel/user_namespace.c +4 -4
@@ -121,10 +121,10 @@ int create_user_ns(struct cred *new)for (i =0 ; i <MAX_PER_NAMESPACE_UCOUNTS ; i++) { ns->ucount_max[i] =INT_MAX ; } - ns->ucount_max[UCOUNT_RLIMIT_NPROC ] =rlimit (RLIMIT_NPROC ); - ns->ucount_max[UCOUNT_RLIMIT_MSGQUEUE ] =rlimit (RLIMIT_MSGQUEUE ); - ns->ucount_max[UCOUNT_RLIMIT_SIGPENDING ] =rlimit (RLIMIT_SIGPENDING ); - ns->ucount_max[UCOUNT_RLIMIT_MEMLOCK ] =rlimit (RLIMIT_MEMLOCK ); +set_rlimit_ucount_max (ns,UCOUNT_RLIMIT_NPROC ,rlimit (RLIMIT_NPROC )); +set_rlimit_ucount_max (ns,UCOUNT_RLIMIT_MSGQUEUE ,rlimit (RLIMIT_MSGQUEUE )); +set_rlimit_ucount_max (ns,UCOUNT_RLIMIT_SIGPENDING ,rlimit (RLIMIT_SIGPENDING )); +set_rlimit_ucount_max (ns,UCOUNT_RLIMIT_MEMLOCK ,rlimit (RLIMIT_MEMLOCK )); ns->ucounts = ucounts;/* Inherit USERNS_SETGROUPS_ALLOWED from our parent */
Signature · 6 lines
2.29.3 _______________________________________________ Containers mailing list [email protected] https://lists.linuxfoundation.org/mailman/listinfo/containers
Andrew Morton
maintainerQuoted 1 line · On Thu, 22 Apr 2021 14:27:07 +0200 [email protected] wrote:
These patches are for binding the rlimit counters to a user in user namespace.
It's at v11 and no there has been no acking or reviewing activity? Or have you not been tracking these?
Quoted 6 lines · Andrew Morton
Quoted 1 line · On Thu, 22 Apr 2021 14:27:07 +0200 [email protected] wrote:
These patches are for binding the rlimit counters to a user in user namespace.
It's at v11 and no there has been no acking or reviewing activity? Or have you not been tracking these?
Eric W. Biederman told me he's ready to pick it [1]. But as far as I can see he hasn't put it in for-next yet.
Signature · 1 line
Rgrds, legion
Quoted 6 lines · Andrew Morton
Quoted 1 line · On Thu, 22 Apr 2021 14:27:07 +0200 [email protected] wrote:
These patches are for binding the rlimit counters to a user in user namespace.
It's at v11 and no there has been no acking or reviewing activity? Or have you not been tracking these?
Most of the reviews were noticing things that needed to be changed.
For the ack/review by tags I am the one reviewing it and merging the change so I guess I didn't give Alex any Acked-by or Reviewed-by tags.
Regardless the changes are sitting in linux-next now and seem to be doing fine.
Eric