Skip to content
Advertisement

‘unshare’ does not work as expected in C api

This sequence of commands works:

unshare --fork --pid --mount 
umount /proc
mount -t proc proc /proc
umount /dev/pts
mount -t devpts devpts /dev/pts

However, the corresponding C program does not work as expected (it seems it does not unmount the previous /proc, and also it provides EBUSY trying to unmount the devpts):

unshare(CLONE_NEWPID | CLONE_NEWNS );
int pid = fork();
if (pid != 0) {
    int status;
    waitpid(-1, &status, 0);
    return status;
}

printf("My pid: %in", getpid()); // It prints 1 as expected

umount("/proc"); // Returns 0

system("mount"); // Should print error on mtab, but it prints the previous mounted filesystems

mount("proc", "/proc", "proc",
      MS_MGC_VAL | MS_NOSUID | MS_NOEXEC | MS_NODEV,
      NULL));  // Returns 0

umount("/dev/pts");  // Returns -1 errno = 0 (??)

mount("devpts", "/dev/pts", "devpts", 
      MS_MGC_VAL | MS_NOSUID | MS_NOEXEC | MS_NODEV,
      NULL) ); // Returns -1 errno = EBUSY

I omitted here error checking for readability

I think that unshare or unmount does not work as expect: even if it returns zero, it seems that does not unmount /proc (if I try to exec a system("mount") after that, it prints the mounted filesystems).

Advertisement

Answer

I found the problem checking the source code of unshare command. The /proc must be unmounted with MS_PRIVATE | MS_REC and mounted without them, this is essentially to ensure that the the mount has effect only in the current (the new) namespace. The second problem is it is not possible to umount /dev/pts without producing an effect on global namespace (this is caused by the internal routine of devpts driver). To have a private /dev/pts the only way is to mount it with the dedicated -o newinstance option. Finally /dev/ptmx should also be bind-remounted.

Therefore, this is the C working code as expected:

unshare(CLONE_NEWPID | CLONE_NEWNS );
int pid = fork();
if (pid != 0) {
    int status;
    waitpid(-1, &status, 0);
    return status;
}

printf("New PID after unshare is %i", getpid());

if (mount("none", "/proc", NULL, MS_PRIVATE|MS_REC, NULL)) {
    printf("Cannot umount proc! errno=%i", errno);
    exit(1);
}

if (mount("proc", "/proc", "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL)) {
    printf("Cannot mount proc! errno=%i", errno);
    exit(1);
}


if (mount("devpts", "/dev/pts", "devpts", MS_MGC_VAL | MS_NOSUID | MS_NOEXEC, "newinstance") ) {
    printf("Cannot mount pts! errno=%i", errno);
    exit(1);
}

if (mount("/dev/pts/ptmx", "/dev/ptmx", NULL, MS_MGC_VAL | MS_NOSUID | MS_NOEXEC | MS_BIND, NULL) ) {
    printf("Cannot mount ptmx! errno=%i", errno);
    exit(1);
}
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement