From: Roland McGrath wait* syscalls return -ECHILD even when an individual PID of a live child was requested explicitly, when security_task_wait denies the operation. This means that something like a broken SELinux policy can produce an unexpected failure that looks just like a bug with wait or ptrace or something. This patch makes do_wait return -EPERM instead of -ECHILD if some children were ruled out solely because security_task_wait failed. Signed-off-by: Roland McGrath Cc: Stephen Smalley Cc: Chris Wright Cc: James Morris Signed-off-by: Andrew Morton --- kernel/exit.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff -puN kernel/exit.c~return-eperm-not-echild-on-security_task_wait-failure kernel/exit.c --- a/kernel/exit.c~return-eperm-not-echild-on-security_task_wait-failure +++ a/kernel/exit.c @@ -1067,7 +1067,7 @@ static int eligible_child(pid_t pid, int return 2; if (security_task_wait(p)) - return 0; + return -1; return 1; } @@ -1449,6 +1449,7 @@ static long do_wait(pid_t pid, int optio DECLARE_WAITQUEUE(wait, current); struct task_struct *tsk; int flag, retval; + int allowed, denied; add_wait_queue(¤t->signal->wait_chldexit,&wait); repeat: @@ -1457,6 +1458,7 @@ repeat: * match our criteria, even if we are not able to reap it yet. */ flag = 0; + allowed = denied = 0; current->state = TASK_INTERRUPTIBLE; read_lock(&tasklist_lock); tsk = current; @@ -1472,6 +1474,12 @@ repeat: if (!ret) continue; + if (unlikely(ret < 0)) { + denied = 1; + continue; + } + allowed = 1; + switch (p->state) { case TASK_TRACED: /* @@ -1570,6 +1578,8 @@ check_continued: goto repeat; } retval = -ECHILD; + if (unlikely(denied) && !allowed) + retval = -EPERM; end: current->state = TASK_RUNNING; remove_wait_queue(¤t->signal->wait_chldexit,&wait); _