• O
    ptrace: partially fix the do_wait(WEXITED) vs EXIT_DEAD->EXIT_ZOMBIE race · 50b8d257
    Oleg Nesterov 提交于
    Test-case:
    
    	int main(void)
    	{
    		int pid, status;
    
    		pid = fork();
    		if (!pid) {
    			for (;;) {
    				if (!fork())
    					return 0;
    				if (waitpid(-1, &status, 0) < 0) {
    					printf("ERR!! wait: %m\n");
    					return 0;
    				}
    			}
    		}
    
    		assert(ptrace(PTRACE_ATTACH, pid, 0,0) == 0);
    		assert(waitpid(-1, NULL, 0) == pid);
    
    		assert(ptrace(PTRACE_SETOPTIONS, pid, 0,
    					PTRACE_O_TRACEFORK) == 0);
    
    		do {
    			ptrace(PTRACE_CONT, pid, 0, 0);
    			pid = waitpid(-1, NULL, 0);
    		} while (pid > 0);
    
    		return 1;
    	}
    
    It fails because ->real_parent sees its child in EXIT_DEAD state
    while the tracer is going to change the state back to EXIT_ZOMBIE
    in wait_task_zombie().
    
    The offending commit is 823b018e which moved the EXIT_DEAD check,
    but in fact we should not blame it. The original code was not
    correct as well because it didn't take ptrace_reparented() into
    account and because we can't really trust ->ptrace.
    
    This patch adds the additional check to close this particular
    race but it doesn't solve the whole problem. We simply can't
    rely on ->ptrace in this case, it can be cleared if the tracer
    is multithreaded by the exiting ->parent.
    
    I think we should kill EXIT_DEAD altogether, we should always
    remove the soon-to-be-reaped child from ->children or at least
    we should never do the DEAD->ZOMBIE transition. But this is too
    complex for 3.2.
    Reported-and-tested-by: NDenys Vlasenko <vda.linux@googlemail.com>
    Tested-by: NLukasz Michalik <lmi@ift.uni.wroc.pl>
    Acked-by: NTejun Heo <tj@kernel.org>
    Cc: <stable@kernel.org>		[3.0+]
    Signed-off-by: NOleg Nesterov <oleg@redhat.com>
    Signed-off-by: NLinus Torvalds <torvalds@linux-foundation.org>
    50b8d257
exit.c 46.6 KB