summaryrefslogtreecommitdiff
path: root/kernel/pid.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/pid.c')
-rw-r--r--kernel/pid.c45
1 files changed, 27 insertions, 18 deletions
diff --git a/kernel/pid.c b/kernel/pid.c
index 3b96571d0fe6..f55189a3d07d 100644
--- a/kernel/pid.c
+++ b/kernel/pid.c
@@ -128,12 +128,11 @@ void free_pid(struct pid *pid)
* is the reaper wake up the reaper. The reaper
* may be sleeping in zap_pid_ns_processes().
*/
- wake_up_process(ns->child_reaper);
+ wake_up_process(READ_ONCE(ns->child_reaper));
break;
case PIDNS_ADDING:
- /* Handle a fork failure of the first process */
- WARN_ON(ns->child_reaper);
- ns->pid_allocated = 0;
+ /* Only possible if the 1st fork fails */
+ WARN_ON(READ_ONCE(ns->child_reaper));
break;
}
@@ -215,12 +214,6 @@ struct pid *alloc_pid(struct pid_namespace *ns, pid_t *arg_set_tid,
retval = -EINVAL;
if (tid < 1 || tid >= pid_max[ns->level - i])
goto out_abort;
- /*
- * Also fail if a PID != 1 is requested and
- * no PID 1 exists.
- */
- if (tid != 1 && !tmp->child_reaper)
- goto out_abort;
retval = -EPERM;
if (!checkpoint_restore_ns_capable(tmp->user_ns))
goto out_abort;
@@ -236,6 +229,10 @@ struct pid *alloc_pid(struct pid_namespace *ns, pid_t *arg_set_tid,
retried_preload = false;
idr_preload(GFP_KERNEL);
spin_lock(&pidmap_lock);
+ /* For the case when the previous attempt to create init failed */
+ if (ns->pid_allocated == PIDNS_ADDING)
+ idr_set_cursor(&ns->idr, 0);
+
for (tmp = ns, i = ns->level; i >= 0;) {
int tid = set_tid[ns->level - i];
@@ -296,9 +293,18 @@ struct pid *alloc_pid(struct pid_namespace *ns, pid_t *arg_set_tid,
pid->numbers[i].nr = nr;
pid->numbers[i].ns = tmp;
- tmp = tmp->parent;
i--;
retried_preload = false;
+
+ /*
+ * PID 1 (init) must be created first.
+ */
+ if (!READ_ONCE(tmp->child_reaper) && nr != 1) {
+ retval = -EINVAL;
+ goto out_free;
+ }
+
+ tmp = tmp->parent;
}
/*
@@ -311,6 +317,11 @@ struct pid *alloc_pid(struct pid_namespace *ns, pid_t *arg_set_tid,
*
* This can't be done earlier because we need to preserve other
* error conditions.
+ *
+ * We need this even if copy_process() does the same check. If two
+ * or more tasks from parent namespace try to inject a child into a
+ * dead namespace, one of free_pid() calls from the copy_process()
+ * error path may try to wakeup the possibly freed ns->child_reaper.
*/
retval = -ENOMEM;
if (unlikely(!(ns->pid_allocated & PIDNS_ADDING)))
@@ -338,10 +349,6 @@ out_free:
idr_remove(&upid->ns->idr, upid->nr);
}
- /* On failure to allocate the first pid, reset the state */
- if (ns->pid_allocated == PIDNS_ADDING)
- idr_set_cursor(&ns->idr, 0);
-
spin_unlock(&pidmap_lock);
idr_preload_end();
@@ -878,10 +885,12 @@ static struct file *__pidfd_fget(struct task_struct *task, int fd)
if (ret)
return ERR_PTR(ret);
- if (ptrace_may_access(task, PTRACE_MODE_ATTACH_REALCREDS))
- file = fget_task(task, fd);
- else
+ if (!ptrace_may_access(task, PTRACE_MODE_ATTACH_REALCREDS))
file = ERR_PTR(-EPERM);
+ else if (task->flags & PF_EXITING)
+ file = ERR_PTR(-ESRCH);
+ else
+ file = fget_task(task, fd);
up_read(&task->signal->exec_update_lock);