On Thu, Mar 31, 2011 at 01:17:11PM +0200, Andreas Mohr wrote:
> Turns out that dash 0.5.4-12 is NOT affected. Re-upgrading to
> current 0.5.5.1-7.4 package then makes it lock up again.
> 
> Non-sudo-prefixed lines (e.g. tested via echo, ls, find, ...)
> in the script work fine, it's specifically sudo-based commands which are 
> problematic.
> 
> 
> Note that manually executing a test script such as
> 
> #!/bin/sh
> 
> /usr/bin/sudo echo Hi
> /usr/bin/sudo whoami
> 
> does terminate properly, when executed as the same user that kvm would
> execute the scripts as (and when indeed having dash-as-sh).
> Thus it appears that _something_ about the way that kvm sets up its
> startup shell scripts context makes things break, i.e. within kvm only.
> 

The issue here is that QEMU is blocking the SIGCHLD signal before
forking, to make sure it won't get any signal due to the child exiting.

sigprocmask() settings are inherited during a fork and dash or sudo do
use SIGCHLD somewhere in their code, without making sure the signal is
enabled.

For me it is a bug in dash or sudo. See the attached code for a reduced
testcase.

-- 
Aurelien Jarno                          GPG: 1024D/F1BCDB73
aurel...@aurel32.net                 http://www.aurel32.net
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
    if (argc > 0) {
        sigset_t oldmask, mask;
        int pid, status;
        char *args[3];
        char **parg;
    
        sigemptyset(&mask);
        sigaddset(&mask, SIGCHLD);
        sigprocmask(SIG_BLOCK, &mask, &oldmask);
    
        pid = fork();
        if (pid == 0) {
            int open_max = sysconf(_SC_OPEN_MAX), i;
    
            for (i = 0; i < open_max; i++) {
                if (i != STDIN_FILENO &&
                    i != STDOUT_FILENO &&
                    i != STDERR_FILENO) {
                    close(i);
                }
            }
	    parg = args;
	    *parg++ = (char *)argv[1];
	    *parg = NULL;
            execv(argv[1], args);
            _exit(1);
        } else if (pid > 0) {
            while (waitpid(pid, &status, 0) != pid) {
                /* loop */
            }
            sigprocmask(SIG_SETMASK, &oldmask, NULL);
    
            if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
                return 0;
            }
        }
        fprintf(stderr, "%s: could not script\n", argv[1]);
        return -1;
    }
}

Reply via email to