mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	Reimplement RLIMIT_SIGPENDING on top of ucounts
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. Signed-off-by: Alexey Gladkov <legion@kernel.org> Link: https://lkml.kernel.org/r/df9d7764dddd50f28616b7840de74ec0f81711a8.1619094428.git.legion@kernel.org Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
This commit is contained in:
		
							parent
							
								
									6e52a9f053
								
							
						
					
					
						commit
						d646969055
					
				
					 9 changed files with 21 additions and 16 deletions
				
			
		| 
						 | 
					@ -284,7 +284,7 @@ static inline void task_sig(struct seq_file *m, struct task_struct *p)
 | 
				
			||||||
		collect_sigign_sigcatch(p, &ignored, &caught);
 | 
							collect_sigign_sigcatch(p, &ignored, &caught);
 | 
				
			||||||
		num_threads = get_nr_threads(p);
 | 
							num_threads = get_nr_threads(p);
 | 
				
			||||||
		rcu_read_lock();  /* FIXME: is this correct? */
 | 
							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();
 | 
							rcu_read_unlock();
 | 
				
			||||||
		qlim = task_rlimit(p, RLIMIT_SIGPENDING);
 | 
							qlim = task_rlimit(p, RLIMIT_SIGPENDING);
 | 
				
			||||||
		unlock_task_sighand(p, &flags);
 | 
							unlock_task_sighand(p, &flags);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,7 +12,6 @@
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
struct user_struct {
 | 
					struct user_struct {
 | 
				
			||||||
	refcount_t __count;	/* reference count */
 | 
						refcount_t __count;	/* reference count */
 | 
				
			||||||
	atomic_t sigpending;	/* How many pending signals does this user have? */
 | 
					 | 
				
			||||||
#ifdef CONFIG_FANOTIFY
 | 
					#ifdef CONFIG_FANOTIFY
 | 
				
			||||||
	atomic_t fanotify_listeners;
 | 
						atomic_t fanotify_listeners;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,6 +13,8 @@ typedef struct kernel_siginfo {
 | 
				
			||||||
	__SIGINFO;
 | 
						__SIGINFO;
 | 
				
			||||||
} kernel_siginfo_t;
 | 
					} kernel_siginfo_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ucounts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Real Time signals may be queued.
 | 
					 * Real Time signals may be queued.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -21,7 +23,7 @@ struct sigqueue {
 | 
				
			||||||
	struct list_head list;
 | 
						struct list_head list;
 | 
				
			||||||
	int flags;
 | 
						int flags;
 | 
				
			||||||
	kernel_siginfo_t info;
 | 
						kernel_siginfo_t info;
 | 
				
			||||||
	struct user_struct *user;
 | 
						struct ucounts *ucounts;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* flags values. */
 | 
					/* flags values. */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,6 +52,7 @@ enum ucount_type {
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	UCOUNT_RLIMIT_NPROC,
 | 
						UCOUNT_RLIMIT_NPROC,
 | 
				
			||||||
	UCOUNT_RLIMIT_MSGQUEUE,
 | 
						UCOUNT_RLIMIT_MSGQUEUE,
 | 
				
			||||||
 | 
						UCOUNT_RLIMIT_SIGPENDING,
 | 
				
			||||||
	UCOUNT_COUNTS,
 | 
						UCOUNT_COUNTS,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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_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_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
 | 
					#ifdef CONFIG_VMAP_STACK
 | 
				
			||||||
	cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "fork:vm_stack_cache",
 | 
						cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "fork:vm_stack_cache",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -413,8 +413,8 @@ static struct sigqueue *
 | 
				
			||||||
__sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimit)
 | 
					__sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimit)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct sigqueue *q = NULL;
 | 
						struct sigqueue *q = NULL;
 | 
				
			||||||
	struct user_struct *user;
 | 
						struct ucounts *ucounts = NULL;
 | 
				
			||||||
	int sigpending;
 | 
						long sigpending;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Protect access to @t credentials. This can go away when all
 | 
						 * Protect access to @t credentials. This can go away when all
 | 
				
			||||||
| 
						 | 
					@ -425,27 +425,26 @@ __sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimi
 | 
				
			||||||
	 * changes from/to zero.
 | 
						 * changes from/to zero.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	rcu_read_lock();
 | 
						rcu_read_lock();
 | 
				
			||||||
	user = __task_cred(t)->user;
 | 
						ucounts = task_ucounts(t);
 | 
				
			||||||
	sigpending = atomic_inc_return(&user->sigpending);
 | 
						sigpending = inc_rlimit_ucounts(ucounts, UCOUNT_RLIMIT_SIGPENDING, 1);
 | 
				
			||||||
	if (sigpending == 1)
 | 
						if (sigpending == 1)
 | 
				
			||||||
		get_uid(user);
 | 
							ucounts = get_ucounts(ucounts);
 | 
				
			||||||
	rcu_read_unlock();
 | 
						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);
 | 
							q = kmem_cache_alloc(sigqueue_cachep, flags);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		print_dropped_signal(sig);
 | 
							print_dropped_signal(sig);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (unlikely(q == NULL)) {
 | 
						if (unlikely(q == NULL)) {
 | 
				
			||||||
		if (atomic_dec_and_test(&user->sigpending))
 | 
							if (ucounts && dec_rlimit_ucounts(ucounts, UCOUNT_RLIMIT_SIGPENDING, 1))
 | 
				
			||||||
			free_uid(user);
 | 
								put_ucounts(ucounts);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		INIT_LIST_HEAD(&q->list);
 | 
							INIT_LIST_HEAD(&q->list);
 | 
				
			||||||
		q->flags = 0;
 | 
							q->flags = 0;
 | 
				
			||||||
		q->user = user;
 | 
							q->ucounts = ucounts;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	return q;
 | 
						return q;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -453,8 +452,10 @@ static void __sigqueue_free(struct sigqueue *q)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (q->flags & SIGQUEUE_PREALLOC)
 | 
						if (q->flags & SIGQUEUE_PREALLOC)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	if (atomic_dec_and_test(&q->user->sigpending))
 | 
						if (q->ucounts && dec_rlimit_ucounts(q->ucounts, UCOUNT_RLIMIT_SIGPENDING, 1)) {
 | 
				
			||||||
		free_uid(q->user);
 | 
							put_ucounts(q->ucounts);
 | 
				
			||||||
 | 
							q->ucounts = NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	kmem_cache_free(sigqueue_cachep, q);
 | 
						kmem_cache_free(sigqueue_cachep, q);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -80,6 +80,7 @@ static struct ctl_table user_table[] = {
 | 
				
			||||||
	UCOUNT_ENTRY("max_inotify_instances"),
 | 
						UCOUNT_ENTRY("max_inotify_instances"),
 | 
				
			||||||
	UCOUNT_ENTRY("max_inotify_watches"),
 | 
						UCOUNT_ENTRY("max_inotify_watches"),
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
						{ },
 | 
				
			||||||
	{ },
 | 
						{ },
 | 
				
			||||||
	{ },
 | 
						{ },
 | 
				
			||||||
	{ }
 | 
						{ }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -98,7 +98,6 @@ static DEFINE_SPINLOCK(uidhash_lock);
 | 
				
			||||||
/* root_user.__count is 1, for init task cred */
 | 
					/* root_user.__count is 1, for init task cred */
 | 
				
			||||||
struct user_struct root_user = {
 | 
					struct user_struct root_user = {
 | 
				
			||||||
	.__count	= REFCOUNT_INIT(1),
 | 
						.__count	= REFCOUNT_INIT(1),
 | 
				
			||||||
	.sigpending	= ATOMIC_INIT(0),
 | 
					 | 
				
			||||||
	.locked_shm     = 0,
 | 
						.locked_shm     = 0,
 | 
				
			||||||
	.uid		= GLOBAL_ROOT_UID,
 | 
						.uid		= GLOBAL_ROOT_UID,
 | 
				
			||||||
	.ratelimit	= RATELIMIT_STATE_INIT(root_user.ratelimit, 0, 0),
 | 
						.ratelimit	= RATELIMIT_STATE_INIT(root_user.ratelimit, 0, 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_NPROC] = rlimit(RLIMIT_NPROC);
 | 
				
			||||||
	ns->ucount_max[UCOUNT_RLIMIT_MSGQUEUE] = rlimit(RLIMIT_MSGQUEUE);
 | 
						ns->ucount_max[UCOUNT_RLIMIT_MSGQUEUE] = rlimit(RLIMIT_MSGQUEUE);
 | 
				
			||||||
 | 
						ns->ucount_max[UCOUNT_RLIMIT_SIGPENDING] = rlimit(RLIMIT_SIGPENDING);
 | 
				
			||||||
	ns->ucounts = ucounts;
 | 
						ns->ucounts = ucounts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Inherit USERNS_SETGROUPS_ALLOWED from our parent */
 | 
						/* Inherit USERNS_SETGROUPS_ALLOWED from our parent */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue