tools/testing: add a test to check nsfs ioctl-s
authorAndrey Vagin <avagin@openvz.org>
Tue, 6 Sep 2016 07:47:16 +0000 (00:47 -0700)
committerEric W. Biederman <ebiederm@xmission.com>
Fri, 23 Sep 2016 00:59:41 +0000 (19:59 -0500)
There are two new ioctl-s:
One ioctl for the user namespace that owns a file descriptor.
One ioctl for the parent namespace of a namespace file descriptor.

The test checks that these ioctl-s works and that they handle a case
when a target namespace is outside of the current process namespace.

Signed-off-by: Andrei Vagin <avagin@openvz.org>
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
tools/testing/selftests/Makefile
tools/testing/selftests/nsfs/Makefile [new file with mode: 0644]
tools/testing/selftests/nsfs/owner.c [new file with mode: 0644]
tools/testing/selftests/nsfs/pidns.c [new file with mode: 0644]

index ff9e5f20a5a73cedc5f03992066176279c38426e..f770dba2a6f636024c5254b5f9a6d747e8abff3a 100644 (file)
@@ -15,6 +15,7 @@ TARGETS += memory-hotplug
 TARGETS += mount
 TARGETS += mqueue
 TARGETS += net
+TARGETS += nsfs
 TARGETS += powerpc
 TARGETS += pstore
 TARGETS += ptrace
diff --git a/tools/testing/selftests/nsfs/Makefile b/tools/testing/selftests/nsfs/Makefile
new file mode 100644 (file)
index 0000000..2306054
--- /dev/null
@@ -0,0 +1,12 @@
+TEST_PROGS := owner pidns
+
+CFLAGS := -Wall -Werror
+
+all: owner pidns
+owner: owner.c
+pidns: pidns.c
+
+clean:
+       $(RM) owner pidns
+
+include ../lib.mk
diff --git a/tools/testing/selftests/nsfs/owner.c b/tools/testing/selftests/nsfs/owner.c
new file mode 100644 (file)
index 0000000..437205f
--- /dev/null
@@ -0,0 +1,91 @@
+#define _GNU_SOURCE
+#include <sched.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+
+#define NSIO    0xb7
+#define NS_GET_USERNS   _IO(NSIO, 0x1)
+
+#define pr_err(fmt, ...) \
+               ({ \
+                       fprintf(stderr, "%s:%d:" fmt ": %m\n", \
+                               __func__, __LINE__, ##__VA_ARGS__); \
+                       1; \
+               })
+
+int main(int argc, char *argvp[])
+{
+       int pfd[2], ns, uns, init_uns;
+       struct stat st1, st2;
+       char path[128];
+       pid_t pid;
+       char c;
+
+       if (pipe(pfd))
+               return 1;
+
+       pid = fork();
+       if (pid < 0)
+               return pr_err("fork");
+       if (pid == 0) {
+               prctl(PR_SET_PDEATHSIG, SIGKILL);
+               if (unshare(CLONE_NEWUTS | CLONE_NEWUSER))
+                       return pr_err("unshare");
+               close(pfd[0]);
+               close(pfd[1]);
+               while (1)
+                       sleep(1);
+               return 0;
+       }
+       close(pfd[1]);
+       if (read(pfd[0], &c, 1) != 0)
+               return pr_err("Unable to read from pipe");
+       close(pfd[0]);
+
+       snprintf(path, sizeof(path), "/proc/%d/ns/uts", pid);
+       ns = open(path, O_RDONLY);
+       if (ns < 0)
+               return pr_err("Unable to open %s", path);
+
+       uns = ioctl(ns, NS_GET_USERNS);
+       if (uns < 0)
+               return pr_err("Unable to get an owning user namespace");
+
+       if (fstat(uns, &st1))
+               return pr_err("fstat");
+
+       snprintf(path, sizeof(path), "/proc/%d/ns/user", pid);
+       if (stat(path, &st2))
+               return pr_err("stat");
+
+       if (st1.st_ino != st2.st_ino)
+               return pr_err("NS_GET_USERNS returned a wrong namespace");
+
+       init_uns = ioctl(uns, NS_GET_USERNS);
+       if (uns < 0)
+               return pr_err("Unable to get an owning user namespace");
+
+       if (ioctl(init_uns, NS_GET_USERNS) >= 0 || errno != EPERM)
+               return pr_err("Don't get EPERM");
+
+       if (unshare(CLONE_NEWUSER))
+               return pr_err("unshare");
+
+       if (ioctl(ns, NS_GET_USERNS) >= 0 || errno != EPERM)
+               return pr_err("Don't get EPERM");
+       if (ioctl(init_uns, NS_GET_USERNS) >= 0 || errno != EPERM)
+               return pr_err("Don't get EPERM");
+
+       kill(pid, SIGKILL);
+       wait(NULL);
+       return 0;
+}
diff --git a/tools/testing/selftests/nsfs/pidns.c b/tools/testing/selftests/nsfs/pidns.c
new file mode 100644 (file)
index 0000000..ae3a0d6
--- /dev/null
@@ -0,0 +1,78 @@
+#define _GNU_SOURCE
+#include <sched.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+
+#define pr_err(fmt, ...) \
+               ({ \
+                       fprintf(stderr, "%s:%d:" fmt ": %m\n", \
+                               __func__, __LINE__, ##__VA_ARGS__); \
+                       1; \
+               })
+
+#define NSIO   0xb7
+#define NS_GET_USERNS   _IO(NSIO, 0x1)
+#define NS_GET_PARENT   _IO(NSIO, 0x2)
+
+#define __stack_aligned__      __attribute__((aligned(16)))
+struct cr_clone_arg {
+       char stack[128] __stack_aligned__;
+       char stack_ptr[0];
+};
+
+static int child(void *args)
+{
+       prctl(PR_SET_PDEATHSIG, SIGKILL);
+       while (1)
+               sleep(1);
+       exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+       char *ns_strs[] = {"pid", "user"};
+       char path[] = "/proc/0123456789/ns/pid";
+       struct cr_clone_arg ca;
+       struct stat st1, st2;
+       int ns, pns, i;
+       pid_t pid;
+
+       pid = clone(child, ca.stack_ptr, CLONE_NEWUSER | CLONE_NEWPID | SIGCHLD, NULL);
+       if (pid < 0)
+               return pr_err("clone");
+
+       for (i = 0; i < 2; i++) {
+               snprintf(path, sizeof(path), "/proc/%d/ns/%s", pid, ns_strs[i]);
+               ns = open(path, O_RDONLY);
+               if (ns < 0)
+                       return pr_err("Unable to open %s", path);
+
+               pns = ioctl(ns, NS_GET_PARENT);
+               if (pns < 0)
+                       return pr_err("Unable to get a parent pidns");
+
+               snprintf(path, sizeof(path), "/proc/self/ns/%s", ns_strs[i]);
+               if (stat(path, &st2))
+                       return pr_err("Unable to stat %s", path);
+               if (fstat(pns, &st1))
+                       return pr_err("Unable to stat the parent pidns");
+               if (st1.st_ino != st2.st_ino)
+                       return pr_err("NS_GET_PARENT returned a wrong namespace");
+
+               if (ioctl(pns, NS_GET_PARENT) >= 0 || errno != EPERM)
+                       return pr_err("Don't get EPERM");;
+       }
+
+       kill(pid, SIGKILL);
+       wait(NULL);
+       return 0;
+}