Package: schroot
Version: 1.3.1-1
Severity: minor
User: ubuntu-de...@lists.ubuntu.com
Usertags: origin-ubuntu lucid

Hi!

I seem to have run into a weird glitch when using the "command-prefix"
option of schroot.  Namely, schroot is backgrounded by my shell on exit
because it receives SIGTTOU when attempting to reset the termios on
CTTY_FILENO:

$ schroot -u root -c lucid
cloning (with CLONE_NEWPID) for child
parent waiting
running child
(lucid)r...@system:/tmp# exit
parent done, exiting 0

[1]+  Stopped                 schroot -u root -c lucid
$ echo $?
150
$ fg
schroot -u root -c lucid
$ echo $?
0

This happens for me because I've been playing with the CLONE_NEW* flags,
and it seems that CLONE_NEWPID seems to trigger the tty layer into sending
SIGTTOU when it attempts to reset the termios.

I can, for some reason, trigger this without anything very heavy-weight by
calling "bash -i" from a simple wrapper, but that just illustrates the
problem -- I'm not sure why it's happening under schroot only with
CLONE_NEWPID.

Attached is
 - "minimal.c" shows the signal arriving in a simple environment.
 - "runalt.c" which I use with command-prefix like this:

# works:
#command-prefix=/scratch/src/newpid/altrun,fork
#command-prefix=/scratch/src/newpid/altrun,clone
command-prefix=/scratch/src/newpid/altrun,clone-newnet
# breaks:
#command-prefix=/scratch/src/newpid/altrun,clone-newpid

I'm at a loss.  "minimal.c" behaves the same on older kernels, so I'm
assuming this isn't a kernel bug.  Maybe bash is doing something weird
when it finds itself running as PID 1?

Thanks,

-Kees

-- 
Kees Cook                                            @debian.org
// Copyright (C) 2010, Kees Cook <k...@ubuntu.com>
// License: GPLv3
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>
#include <stdint.h>
#include <stddef.h>
#include <inttypes.h>

void catch_ttou(int sig) {
    printf("Caught SIGTTOU!\n");

    /* need to ignore or we will catch this until our parent backgrounds us */
    signal(SIGTTOU,SIG_IGN);
}

int main(int argc, char * argv[])
{
    struct termios saved_termios;
    struct termios after_termios;

    signal(SIGTTOU,catch_ttou);

    if (tcgetattr(STDIN_FILENO, &saved_termios) < 0) {
        perror("tcgetattr");
        return -1;
    }

    printf("spawning termios fiddler...\n");
    system("/bin/bash -i -c /bin/true");

    printf("reset termios ...\n");
    if (tcsetattr(STDIN_FILENO, TCSANOW, &saved_termios) < 0) {
        perror("tcsetattr");
        return -1;
    }

    return 0;
}
// Copyright (C) 2010, Kees Cook <k...@ubuntu.com>
// License: GPLv3
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <stdint.h>
#include <stddef.h>
#include <inttypes.h>
#include <sched.h>
#include <signal.h>

int child(void *ptr)
{
    char **argv = ptr;
    printf("running child\n");
    execv(*argv,argv);
    perror("execv");
    return -1;
}

int main(int argc, char * argv[])
{
    int status;
    uint8_t stack[4096];

    int flags = SIGCHLD;

    if (argc<3) {
        fprintf(stderr,"Usage: %s [fork|clone|clone-newpid|clone-newnet] cmd arg arg...\n", argv[0]);
        return -1;
    }

    if (strcmp(argv[1],"clone-newpid")==0) {
        flags |= CLONE_NEWPID;
    }
    if (strcmp(argv[1],"clone-newnet")==0) {
        flags |= CLONE_NEWNET;
    }

    if (strcmp(argv[1],"fork")==0) {
        printf("forking for child\n");
        switch (fork()) {
        case -1:
            perror("fork");
            return -1;
        case 0:
            exit(child(argv+2));
        default:
            break;
        }
    }
    else {
        printf("cloning %sfor child\n",
            ((flags & CLONE_NEWPID) == CLONE_NEWPID) ? "(with CLONE_NEWPID) " :
            ( ((flags & CLONE_NEWNET) == CLONE_NEWNET) ? "(with CLONE_NEWNET) " : ""));

        if (clone(child, stack+sizeof(stack), flags, argv+2)<0) {
            perror("clone");
            return -1;
        }
    }

    printf("parent waiting\n");
    waitpid(-1, &status, __WALL);

    printf("parent done, exiting %d\n", WEXITSTATUS(status));
    return WEXITSTATUS(status);
}

Reply via email to