From 6c2cf1d06d99caa848366d21ba115a4d92711a93 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Thu, 1 Oct 2020 13:20:29 +0200 Subject: [PATCH 1/2] chdir to / in parent There's no reason the dumb-init process should hold on to a reference to the original cwd (but of course the child must be spawned with that cwd). One example where that can be problematic: # Terminal 1 $ mkdir -p /tmp/d/e $ sudo mount --make-shared -t tmpfs tmpfs /tmp/d/e # Terminal 2 $ docker run -v /tmp/d:/tmp/d:rshared --rm -ti --workdir /tmp/d/e some-image-with-dumb-init bash root@922ef180b49a:/tmp/d/e# cd .. root@922ef180b49a:/tmp/d# ls -l /proc/*/cwd lrwxrwxrwx 1 root root 0 Oct 1 11:25 /proc/1/cwd -> /tmp/d/e lrwxrwxrwx 1 root root 0 Oct 1 11:25 /proc/6/cwd -> /tmp/d lrwxrwxrwx 1 root root 0 Oct 1 11:25 /proc/self/cwd -> /tmp/d lrwxrwxrwx 1 root root 0 Oct 1 11:25 /proc/thread-self/cwd -> /tmp/d # Terminal 1 $ sudo umount /tmp/d/e umount: /tmp/d/e: target is busy. i.e., the application inside the container has already let go of its reference, but the host is not able to unmount /tmp/d/e because the container's pid 1 still has one. As for a test case, the 'docker run --workdir' could be emulated by something like sh -c "cd /tmp/d/e; exec $PWD/dumb-init bash -i" but one would still need root to do the mount and umount. --- dumb-init.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dumb-init.c b/dumb-init.c index e1a2fab..d12bd24 100644 --- a/dumb-init.c +++ b/dumb-init.c @@ -326,6 +326,11 @@ int main(int argc, char *argv[]) { } else { /* parent */ DEBUG("Child spawned with PID %d.\n", child_pid); + if (chdir("/") == -1) { + DEBUG("Unable to chdir(\"/\") (errno=%d %s)\n", + errno, + strerror(errno)); + } for (;;) { int signum; sigwait(&all_signals, &signum); From f6b6492a5565fd7e1afd42043cde966b518393bf Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Thu, 3 Dec 2020 11:02:40 +0100 Subject: [PATCH 2/2] add test for chdir("/") behaviour --- tests/cwd_test.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/cwd_test.py diff --git a/tests/cwd_test.py b/tests/cwd_test.py new file mode 100644 index 0000000..ff4ff80 --- /dev/null +++ b/tests/cwd_test.py @@ -0,0 +1,22 @@ +import os +import shutil +from subprocess import run, PIPE + +import pytest + +@pytest.mark.usefixtures('both_debug_modes', 'both_setsid_modes') +def test_working_directories(): + """The child process must start in the working directory in which + dumb-init was invoked, but dumb-init itself should not keep a + reference to that.""" + + # We need absolute path to dumb-init since we pass cwd=/tmp to get + # predictable output - so we can't rely on dumb-init being found + # in the "." directory. + dumb_init = os.path.realpath(shutil.which('dumb-init')) + proc = run((dumb_init, + 'sh', '-c', 'readlink /proc/$PPID/cwd && readlink /proc/$$/cwd'), + cwd="/tmp", stdout=PIPE, stderr=PIPE) + + assert proc.returncode == 0 + assert proc.stdout == b'/\n/tmp\n'