diff options
Diffstat (limited to 'kernel/pid.c')
| -rw-r--r-- | kernel/pid.c | 86 | 
1 files changed, 63 insertions, 23 deletions
diff --git a/kernel/pid.c b/kernel/pid.c index 0a9f2e437217..2278e249141d 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -157,7 +157,8 @@ void free_pid(struct pid *pid)  	call_rcu(&pid->rcu, delayed_put_pid);  } -struct pid *alloc_pid(struct pid_namespace *ns) +struct pid *alloc_pid(struct pid_namespace *ns, pid_t *set_tid, +		      size_t set_tid_size)  {  	struct pid *pid;  	enum pid_type type; @@ -166,6 +167,17 @@ struct pid *alloc_pid(struct pid_namespace *ns)  	struct upid *upid;  	int retval = -ENOMEM; +	/* +	 * set_tid_size contains the size of the set_tid array. Starting at +	 * the most nested currently active PID namespace it tells alloc_pid() +	 * which PID to set for a process in that most nested PID namespace +	 * up to set_tid_size PID namespaces. It does not have to set the PID +	 * for a process in all nested PID namespaces but set_tid_size must +	 * never be greater than the current ns->level + 1. +	 */ +	if (set_tid_size > ns->level + 1) +		return ERR_PTR(-EINVAL); +  	pid = kmem_cache_alloc(ns->pid_cachep, GFP_KERNEL);  	if (!pid)  		return ERR_PTR(retval); @@ -174,24 +186,54 @@ struct pid *alloc_pid(struct pid_namespace *ns)  	pid->level = ns->level;  	for (i = ns->level; i >= 0; i--) { -		int pid_min = 1; +		int tid = 0; + +		if (set_tid_size) { +			tid = set_tid[ns->level - i]; + +			retval = -EINVAL; +			if (tid < 1 || tid >= pid_max) +				goto out_free; +			/* +			 * Also fail if a PID != 1 is requested and +			 * no PID 1 exists. +			 */ +			if (tid != 1 && !tmp->child_reaper) +				goto out_free; +			retval = -EPERM; +			if (!ns_capable(tmp->user_ns, CAP_SYS_ADMIN)) +				goto out_free; +			set_tid_size--; +		}  		idr_preload(GFP_KERNEL);  		spin_lock_irq(&pidmap_lock); -		/* -		 * init really needs pid 1, but after reaching the maximum -		 * wrap back to RESERVED_PIDS -		 */ -		if (idr_get_cursor(&tmp->idr) > RESERVED_PIDS) -			pid_min = RESERVED_PIDS; - -		/* -		 * Store a null pointer so find_pid_ns does not find -		 * a partially initialized PID (see below). -		 */ -		nr = idr_alloc_cyclic(&tmp->idr, NULL, pid_min, -				      pid_max, GFP_ATOMIC); +		if (tid) { +			nr = idr_alloc(&tmp->idr, NULL, tid, +				       tid + 1, GFP_ATOMIC); +			/* +			 * If ENOSPC is returned it means that the PID is +			 * alreay in use. Return EEXIST in that case. +			 */ +			if (nr == -ENOSPC) +				nr = -EEXIST; +		} else { +			int pid_min = 1; +			/* +			 * init really needs pid 1, but after reaching the +			 * maximum wrap back to RESERVED_PIDS +			 */ +			if (idr_get_cursor(&tmp->idr) > RESERVED_PIDS) +				pid_min = RESERVED_PIDS; + +			/* +			 * Store a null pointer so find_pid_ns does not find +			 * a partially initialized PID (see below). +			 */ +			nr = idr_alloc_cyclic(&tmp->idr, NULL, pid_min, +					      pid_max, GFP_ATOMIC); +		}  		spin_unlock_irq(&pidmap_lock);  		idr_preload_end(); @@ -299,7 +341,7 @@ static void __change_pid(struct task_struct *task, enum pid_type type,  	*pid_ptr = new;  	for (tmp = PIDTYPE_MAX; --tmp >= 0; ) -		if (!hlist_empty(&pid->tasks[tmp])) +		if (pid_has_task(pid, tmp))  			return;  	free_pid(pid); @@ -497,7 +539,7 @@ static int pidfd_create(struct pid *pid)   */  SYSCALL_DEFINE2(pidfd_open, pid_t, pid, unsigned int, flags)  { -	int fd, ret; +	int fd;  	struct pid *p;  	if (flags) @@ -510,13 +552,11 @@ SYSCALL_DEFINE2(pidfd_open, pid_t, pid, unsigned int, flags)  	if (!p)  		return -ESRCH; -	ret = 0; -	rcu_read_lock(); -	if (!pid_task(p, PIDTYPE_TGID)) -		ret = -EINVAL; -	rcu_read_unlock(); +	if (pid_has_task(p, PIDTYPE_TGID)) +		fd = pidfd_create(p); +	else +		fd = -EINVAL; -	fd = ret ?: pidfd_create(p);  	put_pid(p);  	return fd;  }  |