20

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: %i\n", 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).

chrk
  • 4,037
  • 2
  • 39
  • 47
ocirocir
  • 3,543
  • 2
  • 24
  • 34
  • use perror instead of printf - it gives info about ERRNO – Jasen Jan 19 '16 at 09:14
  • Ok, anyway I think that the EBUSY on `mount` devpts is caused by the "silent failure" of `umount`/`mount` proc – ocirocir Jan 19 '16 at 09:17
  • 1
    Your `umount`s fail for me, so I replaced them with `umount2("", MNT_DETACH)`. However, this didn't quite fix the problem: it unmounted (and remounted) /proc and /dev/pts globally! What the heck? – user3035772 Jan 21 '16 at 11:13
  • 1
    Why do you think mount should print error on mtab? – nsilent22 Jan 23 '16 at 15:36
  • Because you have umounted /proc, and `/etc/mtab/` is a link to `/proc/self/mounts` – ocirocir Jan 23 '16 at 16:23
  • Could you try system("ls /proc") to see if it's still mounted or not? I have older system (does not implement CLONE_NEWPID as it's kernel >= 3.8 feature), but unshare with CLONE_NEWNS works well. – nsilent22 Jan 24 '16 at 13:25
  • Tried, still mounted. – ocirocir Jan 25 '16 at 19:15
  • FWIW, on my distro (CentOS 6), `/etc/mtab` is a regular file. `/proc/mounts`, on the other hand, is presented as a symlink to `/proc/self/mounts`. I guess this may vary with kernel version. – John Bollinger Jan 26 '16 at 22:29
  • How about presenting the complete code of the test program you are actually running, including especially the part that demonstrates `umount("/proc")` returning a success code? I am disinclined to believe that it truly demonstrates the behavior you describe for `umount()` and `mount()`. Certainly I have been unable to replicate any such behavior with code based on yours. I suspect the program is flawed, but I can't be sure because you have not presented all of it. – John Bollinger Jan 26 '16 at 22:56
  • http://pastebin.com/qZQbW8fG (I removed the /dev/pts part) As suggested by Maquefel, perror("umount") reports "Device or resource busy". – ocirocir Jan 27 '16 at 11:51
  • Ok, I discovered that "sometimes" `umount` returns 0 "sometimes" -1, but in the end it does not unmount `/proc` at all. In case it return -1, perror states "Device or resource busy". – ocirocir Jan 27 '16 at 11:59

4 Answers4

2

Despite your comment that

"sometimes" umount returns 0 "sometimes" -1, but in the end it does not unmount /proc at all

, in 10000 trials of the code from your pastebin, umount() always failed for me, returning -1 and not unmounting /proc. I am disinclined to believe that umount() ever returns 0 despite having failed to perform the requested unmounting, but if ever it does then that would constitute a bug in umount(). If you can in fact substantiate such a bug then the community-minded response would be to file a bug report against glibc.


The question then becomes why and how your bash script behaves differently. In fact, however, it does not seem to do.

In the first place, you have the wrong expectation of the unshare(1) command. Unlike the unshare(2) function, the unshare command does not affect the shell in which it is executed. Instead, it launches a separate process that has its own private copies of the specified namespaces. Normally you would specify the command to launch that process on the unshare command line, and in fact the program's manual page indicates that doing so is mandatory.

Empirically, I find that if I fail to specify such a command -- as you do -- then unshare launches a new shell as the target process. In particular, when I run your script (with sufficient privilege to use unshare), I immediately get a new prompt, but it is the prompt of the new shell, running in the foreground. This is immediately evident to me because the prompt is different (your prompt might not be any different under those circumstances, however). There is no error message, etc. from umount at that point because it has not yet run. If I manually attempt to umount proc in the (unshared) subshell, it fails with "device is busy" -- this is the analog of what your C program tries to do.

When I exit the subshell, the rest of the script runs, with both umounts and both mounts failing. That is to be expected, because the main script shares its mount namespace.


It is entirely plausible that /proc really is busy and therefore cannot be unmounted, even for a process that has a private copy of the mount namespace. It is likely that such a process is itself using its private copy of the mount of /proc. In contrast, I find that I can successfully unmount /dev/pts in a process with an unshared mount namespace, but not in a process that shares the system's copy of that namespace.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • I agree with your analysis, however in my system I'm able to unmount `/proc` and I do not use a script to execute that commands: http://imagebin.ca/v/2Uw7vXG71WhQ – ocirocir Jan 27 '16 at 20:17
  • However, I've just seen that the remount of /dev/pts affects all system and not only the private copy, I don't know why. – ocirocir Jan 27 '16 at 20:20
  • @FedericoReghenzani, as I wrote in my answer, if I try to `umount` `/proc` from within an `unshare`d subshell it fails, exactly as does attempting to do the same thing from the C program does. This does not depend on running the commands as a script. The behavior is consistent for me, and if it really is inconsistent for you then I'd opine that you have a bug in your distro that I do not have in mine. – John Bollinger Jan 27 '16 at 22:38
  • @FedericoReghenzani, also, unmounting `/dev/pts` within an `unshare`d subshell seems to work for me exactly as I would expect. The filesystem is successfully unmounted in the subshell -- listing the mount point directory shows no contents -- but it remains mounted as far as all other processes are concerned. – John Bollinger Jan 27 '16 at 22:41
  • Ok I tried in another system. Try to (before the unshare) umount (with -lf) and remount /proc. Now it should be possible to umount /proc in unshared environment. However it seems that umount /proc in unshared namespace affects the whole system. – ocirocir Jan 28 '16 at 05:35
1

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);
}
ocirocir
  • 3,543
  • 2
  • 24
  • 34
0

I think the issue is with the system("mount") which is spawns a shell and doesn't carry over the umount. Try opening a file in /proc/ after umount and see it works as expected.

See this -

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

printf(">>> My pid: %d\n", getpid()); // It prints 1 as expected
rc = umount2("/proc", MNT_FORCE); // Returns 0
printf(">>> umount returned %d. errno = %d, desc = (%s)\n", rc, errno, strerror(errno));

rc = open("/proc/cpuinfo", O_RDONLY);
printf(">>> open returned %d. errno = %d, desc = (%s)\n", rc, errno, strerror(errno));
gusaki
  • 741
  • 7
  • 13
-1

unshare bash != unshare c

unshare - run program with some namespaces unshared from parent

So basically with --fork you are forking from /bin/sh of /bin/bash (whatever you executing your script with) with --pid and --mount options. "fork" followed by "unshare"

unshare - disassociate parts of the process execution context (current proccess) You are unsharing from init and then forking.

CLONE_NEWPID is "clone" flag not "unshare"

So depending on what you are trying to achieve - i assume you are trying to make "/proc" and "/dev/pts" exclusive for child proccess.

Here is a little example with mount --bind local folders:

# mkdir mnt point
# touch point/point.txt
# mount --bind point mnt
# ls mnt
point.txt

# ./unshare
My pid: 28377
Child:
point.txt
Parent:

# ls mnt

Code:

#define _GNU_SOURCE
#include <sched.h>

int main(int argc, char *argv[])
{
        /** umount global */
        system("umount mnt/");
        int pid = fork();
        if (pid != 0) {
                int status;
                waitpid(-1, &status, 0);
                printf("Parent:\n");
                /* and here we don't */
                system("ls mnt/");
                return status;
        }
        /* unshare */
        unshare(CLONE_FS | CLONE_NEWNS);
        printf("My pid: %i\n", getpid()); // It prints 1 as expected
        /* mount exclusively */
        system("mount --bind point/ mnt/");
        printf("Child:\n");
        /* here we see it */
        system("ls mnt/"); 

        return 0;
}

there is also a nice example with bash: http://karelzak.blogspot.ru/2009/12/unshare1.html

continuation:

mount depends on /etc/mtab which is not always a symbolic link to /proc/mounts

so check /etc/mtab with ls -la.

also check the code for umount on /dev/pts with:

int ret = umount("/dev/pts");
int errsv = errno;
if(ret == -1) {
  printf("Error on umount: %s\n", strerror(errsv));
}

I am pretty sure it is used - check it with fuser /dev/pts/

** EDITED **

Finally - i am not sure that you can umount procfs in namespace only (i think it is impossible)

but you can mount your own copy of procfs in your namespace :

# mount -t proc proc /proc/

Now only your process is visible via ps -e.

Maquefel
  • 480
  • 4
  • 16
  • Why do you mount/umount `mnt/`? My problem is with `/proc`. CLONE_NEWPID is also a flag of unshare since Linux 3.8 (check http://man7.org/linux/man-pages/man2/unshare.2.html). Your code does not detach the child execution (the `printf` with `getpid` do not return 1, because you detach only the filesystem). As explained in the question, the bash code works well, including the `unmount /proc`. – ocirocir Jan 25 '16 at 19:19
  • Simple: you can't umount proc that's all. And you don't need to umount proc, unshare simply doesn't work as you expected. The problem is that your new namespace relays on proc too. If your question is about unmounting proc it was necessary to arrange suitable title. – Maquefel Jan 25 '16 at 21:53
  • No sir: 1. it is possible to umount /proc (the shell command works!) and it's necessary (otherwise the mount of /dev/pts fails) 2. please re-read my question, the problem is the different behaviour between perfectly working shell code and corresponding C API. 3. your C code does not work. 4. You're asking me to close the question when you do not provide me an answer, just to remark, it is possible to umount /proc, try yourself the shell code. – ocirocir Jan 26 '16 at 06:19
  • First of all your title is "'unshare' does not work as expected in C api" - no it is working as excepected. 2) I don't believe you that you can umount in bash and can't do it with c api, thats impossible - provide out put of fuser /proc/ after unshare --fork --pid --mount - i am sure that you can't umount proc via console command also 3) What do you mean my code doesn't work - you can't compile it ? 4) You are ignoring requests of checking - you haven't check what is mtab a file or symvoliv link. – Maquefel Jan 26 '16 at 09:27
  • And basing on your comments - It becomes apparent that you don't understand bash and unshare mechanics - try execute your bash script not as script, but each command by hand step by step and you will understand your mistake. – Maquefel Jan 26 '16 at 09:34
  • You are continue saying that is impossible, but it works... just try it. Do you believe me or you want a video? ... (and no, I'm not using a bash script -_-) As I said, I can compile your code, but it does not detach the PID namespace, so it's useless for what I can try to do... mtab is a link to ../proc/self/mounts, as usual. – ocirocir Jan 26 '16 at 11:03
  • 1) Then why are you stating that you can't umount via C API ? 2) Just add CLONE_NEWPID flag to unshare call - isn't obvious? 3) If you call unmount without -n flag you should recieve a warning: umount: /etc/mtab: No such file or directory - if not your umount is an alias or you are not using util-linux umount. – Maquefel Jan 26 '16 at 11:33
  • Because, as said in question and comments, system("mount") and system("ls /proc") indicate that /proc is still mounted. Also umount/mount of devpts do not work (probably due to fail of /proc unmount). I can add CLONE_NEWPID to your code, but without a fork() after it does not any effect (check doc). If I move the fork, it is essentially identical to my code (note that I have to remount it at /proc, not at other location). – ocirocir Jan 26 '16 at 17:39
  • ok if you are stating that - compile and run this code and present output: https://bpaste.net/show/8bca6ec12074 – Maquefel Jan 26 '16 at 17:52
  • It depends on execution. "Sometimes" it prints success on umount (but it does not umount really). – ocirocir Jan 27 '16 at 11:56