aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/namespace.c6
-rw-r--r--fs/nsfs.c100
-rw-r--r--include/linux/proc_ns.h2
-rw-r--r--include/linux/user_namespace.h7
-rw-r--r--include/uapi/linux/nsfs.h13
-rw-r--r--ipc/namespace.c6
-rw-r--r--kernel/cgroup.c6
-rw-r--r--kernel/pid_namespace.c25
-rw-r--r--kernel/user_namespace.c25
-rw-r--r--kernel/utsname.c6
-rw-r--r--net/core/net_namespace.c6
-rw-r--r--tools/testing/selftests/Makefile1
-rw-r--r--tools/testing/selftests/nsfs/Makefile12
-rw-r--r--tools/testing/selftests/nsfs/owner.c91
-rw-r--r--tools/testing/selftests/nsfs/pidns.c78
15 files changed, 371 insertions, 13 deletions
diff --git a/fs/namespace.c b/fs/namespace.c
index cf2cc234c8b4..8a0e90eb81d3 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -3368,10 +3368,16 @@ static int mntns_install(struct nsproxy *nsproxy, struct ns_common *ns)
3368 return 0; 3368 return 0;
3369} 3369}
3370 3370
3371static struct user_namespace *mntns_owner(struct ns_common *ns)
3372{
3373 return to_mnt_ns(ns)->user_ns;
3374}
3375
3371const struct proc_ns_operations mntns_operations = { 3376const struct proc_ns_operations mntns_operations = {
3372 .name = "mnt", 3377 .name = "mnt",
3373 .type = CLONE_NEWNS, 3378 .type = CLONE_NEWNS,
3374 .get = mntns_get, 3379 .get = mntns_get,
3375 .put = mntns_put, 3380 .put = mntns_put,
3376 .install = mntns_install, 3381 .install = mntns_install,
3382 .owner = mntns_owner,
3377}; 3383};
diff --git a/fs/nsfs.c b/fs/nsfs.c
index 8f20d6016e20..fb7b397a1297 100644
--- a/fs/nsfs.c
+++ b/fs/nsfs.c
@@ -5,11 +5,16 @@
5#include <linux/magic.h> 5#include <linux/magic.h>
6#include <linux/ktime.h> 6#include <linux/ktime.h>
7#include <linux/seq_file.h> 7#include <linux/seq_file.h>
8#include <linux/user_namespace.h>
9#include <linux/nsfs.h>
8 10
9static struct vfsmount *nsfs_mnt; 11static struct vfsmount *nsfs_mnt;
10 12
13static long ns_ioctl(struct file *filp, unsigned int ioctl,
14 unsigned long arg);
11static const struct file_operations ns_file_operations = { 15static const struct file_operations ns_file_operations = {
12 .llseek = no_llseek, 16 .llseek = no_llseek,
17 .unlocked_ioctl = ns_ioctl,
13}; 18};
14 19
15static char *ns_dname(struct dentry *dentry, char *buffer, int buflen) 20static char *ns_dname(struct dentry *dentry, char *buffer, int buflen)
@@ -44,22 +49,14 @@ static void nsfs_evict(struct inode *inode)
44 ns->ops->put(ns); 49 ns->ops->put(ns);
45} 50}
46 51
47void *ns_get_path(struct path *path, struct task_struct *task, 52static void *__ns_get_path(struct path *path, struct ns_common *ns)
48 const struct proc_ns_operations *ns_ops)
49{ 53{
50 struct vfsmount *mnt = mntget(nsfs_mnt); 54 struct vfsmount *mnt = mntget(nsfs_mnt);
51 struct qstr qname = { .name = "", }; 55 struct qstr qname = { .name = "", };
52 struct dentry *dentry; 56 struct dentry *dentry;
53 struct inode *inode; 57 struct inode *inode;
54 struct ns_common *ns;
55 unsigned long d; 58 unsigned long d;
56 59
57again:
58 ns = ns_ops->get(task);
59 if (!ns) {
60 mntput(mnt);
61 return ERR_PTR(-ENOENT);
62 }
63 rcu_read_lock(); 60 rcu_read_lock();
64 d = atomic_long_read(&ns->stashed); 61 d = atomic_long_read(&ns->stashed);
65 if (!d) 62 if (!d)
@@ -68,7 +65,7 @@ again:
68 if (!lockref_get_not_dead(&dentry->d_lockref)) 65 if (!lockref_get_not_dead(&dentry->d_lockref))
69 goto slow; 66 goto slow;
70 rcu_read_unlock(); 67 rcu_read_unlock();
71 ns_ops->put(ns); 68 ns->ops->put(ns);
72got_it: 69got_it:
73 path->mnt = mnt; 70 path->mnt = mnt;
74 path->dentry = dentry; 71 path->dentry = dentry;
@@ -77,7 +74,7 @@ slow:
77 rcu_read_unlock(); 74 rcu_read_unlock();
78 inode = new_inode_pseudo(mnt->mnt_sb); 75 inode = new_inode_pseudo(mnt->mnt_sb);
79 if (!inode) { 76 if (!inode) {
80 ns_ops->put(ns); 77 ns->ops->put(ns);
81 mntput(mnt); 78 mntput(mnt);
82 return ERR_PTR(-ENOMEM); 79 return ERR_PTR(-ENOMEM);
83 } 80 }
@@ -95,17 +92,94 @@ slow:
95 return ERR_PTR(-ENOMEM); 92 return ERR_PTR(-ENOMEM);
96 } 93 }
97 d_instantiate(dentry, inode); 94 d_instantiate(dentry, inode);
98 dentry->d_fsdata = (void *)ns_ops; 95 dentry->d_fsdata = (void *)ns->ops;
99 d = atomic_long_cmpxchg(&ns->stashed, 0, (unsigned long)dentry); 96 d = atomic_long_cmpxchg(&ns->stashed, 0, (unsigned long)dentry);
100 if (d) { 97 if (d) {
101 d_delete(dentry); /* make sure ->d_prune() does nothing */ 98 d_delete(dentry); /* make sure ->d_prune() does nothing */
102 dput(dentry); 99 dput(dentry);
100 mntput(mnt);
103 cpu_relax(); 101 cpu_relax();
104 goto again; 102 return ERR_PTR(-EAGAIN);
105 } 103 }
106 goto got_it; 104 goto got_it;
107} 105}
108 106
107void *ns_get_path(struct path *path, struct task_struct *task,
108 const struct proc_ns_operations *ns_ops)
109{
110 struct ns_common *ns;
111 void *ret;
112
113again:
114 ns = ns_ops->get(task);
115 if (!ns)
116 return ERR_PTR(-ENOENT);
117
118 ret = __ns_get_path(path, ns);
119 if (IS_ERR(ret) && PTR_ERR(ret) == -EAGAIN)
120 goto again;
121 return ret;
122}
123
124static int open_related_ns(struct ns_common *ns,
125 struct ns_common *(*get_ns)(struct ns_common *ns))
126{
127 struct path path = {};
128 struct file *f;
129 void *err;
130 int fd;
131
132 fd = get_unused_fd_flags(O_CLOEXEC);
133 if (fd < 0)
134 return fd;
135
136 while (1) {
137 struct ns_common *relative;
138
139 relative = get_ns(ns);
140 if (IS_ERR(relative)) {
141 put_unused_fd(fd);
142 return PTR_ERR(relative);
143 }
144
145 err = __ns_get_path(&path, relative);
146 if (IS_ERR(err) && PTR_ERR(err) == -EAGAIN)
147 continue;
148 break;
149 }
150 if (IS_ERR(err)) {
151 put_unused_fd(fd);
152 return PTR_ERR(err);
153 }
154
155 f = dentry_open(&path, O_RDONLY, current_cred());
156 path_put(&path);
157 if (IS_ERR(f)) {
158 put_unused_fd(fd);
159 fd = PTR_ERR(f);
160 } else
161 fd_install(fd, f);
162
163 return fd;
164}
165
166static long ns_ioctl(struct file *filp, unsigned int ioctl,
167 unsigned long arg)
168{
169 struct ns_common *ns = get_proc_ns(file_inode(filp));
170
171 switch (ioctl) {
172 case NS_GET_USERNS:
173 return open_related_ns(ns, ns_get_owner);
174 case NS_GET_PARENT:
175 if (!ns->ops->get_parent)
176 return -EINVAL;
177 return open_related_ns(ns, ns->ops->get_parent);
178 default:
179 return -ENOTTY;
180 }
181}
182
109int ns_get_name(char *buf, size_t size, struct task_struct *task, 183int ns_get_name(char *buf, size_t size, struct task_struct *task,
110 const struct proc_ns_operations *ns_ops) 184 const struct proc_ns_operations *ns_ops)
111{ 185{
diff --git a/include/linux/proc_ns.h b/include/linux/proc_ns.h
index de0e7719d4c5..12cb8bd81d2d 100644
--- a/include/linux/proc_ns.h
+++ b/include/linux/proc_ns.h
@@ -18,6 +18,8 @@ struct proc_ns_operations {
18 struct ns_common *(*get)(struct task_struct *task); 18 struct ns_common *(*get)(struct task_struct *task);
19 void (*put)(struct ns_common *ns); 19 void (*put)(struct ns_common *ns);
20 int (*install)(struct nsproxy *nsproxy, struct ns_common *ns); 20 int (*install)(struct nsproxy *nsproxy, struct ns_common *ns);
21 struct user_namespace *(*owner)(struct ns_common *ns);
22 struct ns_common *(*get_parent)(struct ns_common *ns);
21}; 23};
22 24
23extern const struct proc_ns_operations netns_operations; 25extern const struct proc_ns_operations netns_operations;
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 30ffe10cda18..eb209d4523f5 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -106,6 +106,8 @@ extern ssize_t proc_setgroups_write(struct file *, const char __user *, size_t,
106extern int proc_setgroups_show(struct seq_file *m, void *v); 106extern int proc_setgroups_show(struct seq_file *m, void *v);
107extern bool userns_may_setgroups(const struct user_namespace *ns); 107extern bool userns_may_setgroups(const struct user_namespace *ns);
108extern bool current_in_userns(const struct user_namespace *target_ns); 108extern bool current_in_userns(const struct user_namespace *target_ns);
109
110struct ns_common *ns_get_owner(struct ns_common *ns);
109#else 111#else
110 112
111static inline struct user_namespace *get_user_ns(struct user_namespace *ns) 113static inline struct user_namespace *get_user_ns(struct user_namespace *ns)
@@ -139,6 +141,11 @@ static inline bool current_in_userns(const struct user_namespace *target_ns)
139{ 141{
140 return true; 142 return true;
141} 143}
144
145static inline struct ns_common *ns_get_owner(struct ns_common *ns)
146{
147 return ERR_PTR(-EPERM);
148}
142#endif 149#endif
143 150
144#endif /* _LINUX_USER_H */ 151#endif /* _LINUX_USER_H */
diff --git a/include/uapi/linux/nsfs.h b/include/uapi/linux/nsfs.h
new file mode 100644
index 000000000000..3af617230d1b
--- /dev/null
+++ b/include/uapi/linux/nsfs.h
@@ -0,0 +1,13 @@
1#ifndef __LINUX_NSFS_H
2#define __LINUX_NSFS_H
3
4#include <linux/ioctl.h>
5
6#define NSIO 0xb7
7
8/* Returns a file descriptor that refers to an owning user namespace */
9#define NS_GET_USERNS _IO(NSIO, 0x1)
10/* Returns a file descriptor that refers to a parent namespace */
11#define NS_GET_PARENT _IO(NSIO, 0x2)
12
13#endif /* __LINUX_NSFS_H */
diff --git a/ipc/namespace.c b/ipc/namespace.c
index fab727d9fe09..0abdea496493 100644
--- a/ipc/namespace.c
+++ b/ipc/namespace.c
@@ -188,10 +188,16 @@ static int ipcns_install(struct nsproxy *nsproxy, struct ns_common *new)
188 return 0; 188 return 0;
189} 189}
190 190
191static struct user_namespace *ipcns_owner(struct ns_common *ns)
192{
193 return to_ipc_ns(ns)->user_ns;
194}
195
191const struct proc_ns_operations ipcns_operations = { 196const struct proc_ns_operations ipcns_operations = {
192 .name = "ipc", 197 .name = "ipc",
193 .type = CLONE_NEWIPC, 198 .type = CLONE_NEWIPC,
194 .get = ipcns_get, 199 .get = ipcns_get,
195 .put = ipcns_put, 200 .put = ipcns_put,
196 .install = ipcns_install, 201 .install = ipcns_install,
202 .owner = ipcns_owner,
197}; 203};
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index f1dd4b076210..d6504338e284 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -6421,12 +6421,18 @@ static void cgroupns_put(struct ns_common *ns)
6421 put_cgroup_ns(to_cg_ns(ns)); 6421 put_cgroup_ns(to_cg_ns(ns));
6422} 6422}
6423 6423
6424static struct user_namespace *cgroupns_owner(struct ns_common *ns)
6425{
6426 return to_cg_ns(ns)->user_ns;
6427}
6428
6424const struct proc_ns_operations cgroupns_operations = { 6429const struct proc_ns_operations cgroupns_operations = {
6425 .name = "cgroup", 6430 .name = "cgroup",
6426 .type = CLONE_NEWCGROUP, 6431 .type = CLONE_NEWCGROUP,
6427 .get = cgroupns_get, 6432 .get = cgroupns_get,
6428 .put = cgroupns_put, 6433 .put = cgroupns_put,
6429 .install = cgroupns_install, 6434 .install = cgroupns_install,
6435 .owner = cgroupns_owner,
6430}; 6436};
6431 6437
6432static __init int cgroup_namespaces_init(void) 6438static __init int cgroup_namespaces_init(void)
diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c
index 7542b28cc929..df9e8e9e0be7 100644
--- a/kernel/pid_namespace.c
+++ b/kernel/pid_namespace.c
@@ -405,12 +405,37 @@ static int pidns_install(struct nsproxy *nsproxy, struct ns_common *ns)
405 return 0; 405 return 0;
406} 406}
407 407
408static struct ns_common *pidns_get_parent(struct ns_common *ns)
409{
410 struct pid_namespace *active = task_active_pid_ns(current);
411 struct pid_namespace *pid_ns, *p;
412
413 /* See if the parent is in the current namespace */
414 pid_ns = p = to_pid_ns(ns)->parent;
415 for (;;) {
416 if (!p)
417 return ERR_PTR(-EPERM);
418 if (p == active)
419 break;
420 p = p->parent;
421 }
422
423 return &get_pid_ns(pid_ns)->ns;
424}
425
426static struct user_namespace *pidns_owner(struct ns_common *ns)
427{
428 return to_pid_ns(ns)->user_ns;
429}
430
408const struct proc_ns_operations pidns_operations = { 431const struct proc_ns_operations pidns_operations = {
409 .name = "pid", 432 .name = "pid",
410 .type = CLONE_NEWPID, 433 .type = CLONE_NEWPID,
411 .get = pidns_get, 434 .get = pidns_get,
412 .put = pidns_put, 435 .put = pidns_put,
413 .install = pidns_install, 436 .install = pidns_install,
437 .owner = pidns_owner,
438 .get_parent = pidns_get_parent,
414}; 439};
415 440
416static __init int pid_namespaces_init(void) 441static __init int pid_namespaces_init(void)
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index f2c5ba5505f1..86b7854fec8e 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -1050,12 +1050,37 @@ static int userns_install(struct nsproxy *nsproxy, struct ns_common *ns)
1050 return commit_creds(cred); 1050 return commit_creds(cred);
1051} 1051}
1052 1052
1053struct ns_common *ns_get_owner(struct ns_common *ns)
1054{
1055 struct user_namespace *my_user_ns = current_user_ns();
1056 struct user_namespace *owner, *p;
1057
1058 /* See if the owner is in the current user namespace */
1059 owner = p = ns->ops->owner(ns);
1060 for (;;) {
1061 if (!p)
1062 return ERR_PTR(-EPERM);
1063 if (p == my_user_ns)
1064 break;
1065 p = p->parent;
1066 }
1067
1068 return &get_user_ns(owner)->ns;
1069}
1070
1071static struct user_namespace *userns_owner(struct ns_common *ns)
1072{
1073 return to_user_ns(ns)->parent;
1074}
1075
1053const struct proc_ns_operations userns_operations = { 1076const struct proc_ns_operations userns_operations = {
1054 .name = "user", 1077 .name = "user",
1055 .type = CLONE_NEWUSER, 1078 .type = CLONE_NEWUSER,
1056 .get = userns_get, 1079 .get = userns_get,
1057 .put = userns_put, 1080 .put = userns_put,
1058 .install = userns_install, 1081 .install = userns_install,
1082 .owner = userns_owner,
1083 .get_parent = ns_get_owner,
1059}; 1084};
1060 1085
1061static __init int user_namespaces_init(void) 1086static __init int user_namespaces_init(void)
diff --git a/kernel/utsname.c b/kernel/utsname.c
index 35587b76faa3..6976cd47dcf6 100644
--- a/kernel/utsname.c
+++ b/kernel/utsname.c
@@ -154,10 +154,16 @@ static int utsns_install(struct nsproxy *nsproxy, struct ns_common *new)
154 return 0; 154 return 0;
155} 155}
156 156
157static struct user_namespace *utsns_owner(struct ns_common *ns)
158{
159 return to_uts_ns(ns)->user_ns;
160}
161
157const struct proc_ns_operations utsns_operations = { 162const struct proc_ns_operations utsns_operations = {
158 .name = "uts", 163 .name = "uts",
159 .type = CLONE_NEWUTS, 164 .type = CLONE_NEWUTS,
160 .get = utsns_get, 165 .get = utsns_get,
161 .put = utsns_put, 166 .put = utsns_put,
162 .install = utsns_install, 167 .install = utsns_install,
168 .owner = utsns_owner,
163}; 169};
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 06af5d6a883c..e8be581b47b0 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -1016,11 +1016,17 @@ static int netns_install(struct nsproxy *nsproxy, struct ns_common *ns)
1016 return 0; 1016 return 0;
1017} 1017}
1018 1018
1019static struct user_namespace *netns_owner(struct ns_common *ns)
1020{
1021 return to_net_ns(ns)->user_ns;
1022}
1023
1019const struct proc_ns_operations netns_operations = { 1024const struct proc_ns_operations netns_operations = {
1020 .name = "net", 1025 .name = "net",
1021 .type = CLONE_NEWNET, 1026 .type = CLONE_NEWNET,
1022 .get = netns_get, 1027 .get = netns_get,
1023 .put = netns_put, 1028 .put = netns_put,
1024 .install = netns_install, 1029 .install = netns_install,
1030 .owner = netns_owner,
1025}; 1031};
1026#endif 1032#endif
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index ff9e5f20a5a7..f770dba2a6f6 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -15,6 +15,7 @@ TARGETS += memory-hotplug
15TARGETS += mount 15TARGETS += mount
16TARGETS += mqueue 16TARGETS += mqueue
17TARGETS += net 17TARGETS += net
18TARGETS += nsfs
18TARGETS += powerpc 19TARGETS += powerpc
19TARGETS += pstore 20TARGETS += pstore
20TARGETS += ptrace 21TARGETS += ptrace
diff --git a/tools/testing/selftests/nsfs/Makefile b/tools/testing/selftests/nsfs/Makefile
new file mode 100644
index 000000000000..2306054a901a
--- /dev/null
+++ b/tools/testing/selftests/nsfs/Makefile
@@ -0,0 +1,12 @@
1TEST_PROGS := owner pidns
2
3CFLAGS := -Wall -Werror
4
5all: owner pidns
6owner: owner.c
7pidns: pidns.c
8
9clean:
10 $(RM) owner pidns
11
12include ../lib.mk
diff --git a/tools/testing/selftests/nsfs/owner.c b/tools/testing/selftests/nsfs/owner.c
new file mode 100644
index 000000000000..437205f8b714
--- /dev/null
+++ b/tools/testing/selftests/nsfs/owner.c
@@ -0,0 +1,91 @@
1#define _GNU_SOURCE
2#include <sched.h>
3#include <unistd.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <signal.h>
7#include <errno.h>
8#include <sys/types.h>
9#include <sys/stat.h>
10#include <fcntl.h>
11#include <sys/ioctl.h>
12#include <sys/prctl.h>
13#include <sys/wait.h>
14
15#define NSIO 0xb7
16#define NS_GET_USERNS _IO(NSIO, 0x1)
17
18#define pr_err(fmt, ...) \
19 ({ \
20 fprintf(stderr, "%s:%d:" fmt ": %m\n", \
21 __func__, __LINE__, ##__VA_ARGS__); \
22 1; \
23 })
24
25int main(int argc, char *argvp[])
26{
27 int pfd[2], ns, uns, init_uns;
28 struct stat st1, st2;
29 char path[128];
30 pid_t pid;
31 char c;
32
33 if (pipe(pfd))
34 return 1;
35
36 pid = fork();
37 if (pid < 0)
38 return pr_err("fork");
39 if (pid == 0) {
40 prctl(PR_SET_PDEATHSIG, SIGKILL);
41 if (unshare(CLONE_NEWUTS | CLONE_NEWUSER))
42 return pr_err("unshare");
43 close(pfd[0]);
44 close(pfd[1]);
45 while (1)
46 sleep(1);
47 return 0;
48 }
49 close(pfd[1]);
50 if (read(pfd[0], &c, 1) != 0)
51 return pr_err("Unable to read from pipe");
52 close(pfd[0]);
53
54 snprintf(path, sizeof(path), "/proc/%d/ns/uts", pid);
55 ns = open(path, O_RDONLY);
56 if (ns < 0)
57 return pr_err("Unable to open %s", path);
58
59 uns = ioctl(ns, NS_GET_USERNS);
60 if (uns < 0)
61 return pr_err("Unable to get an owning user namespace");
62
63 if (fstat(uns, &st1))
64 return pr_err("fstat");
65
66 snprintf(path, sizeof(path), "/proc/%d/ns/user", pid);
67 if (stat(path, &st2))
68 return pr_err("stat");
69
70 if (st1.st_ino != st2.st_ino)
71 return pr_err("NS_GET_USERNS returned a wrong namespace");
72
73 init_uns = ioctl(uns, NS_GET_USERNS);
74 if (uns < 0)
75 return pr_err("Unable to get an owning user namespace");
76
77 if (ioctl(init_uns, NS_GET_USERNS) >= 0 || errno != EPERM)
78 return pr_err("Don't get EPERM");
79
80 if (unshare(CLONE_NEWUSER))
81 return pr_err("unshare");
82
83 if (ioctl(ns, NS_GET_USERNS) >= 0 || errno != EPERM)
84 return pr_err("Don't get EPERM");
85 if (ioctl(init_uns, NS_GET_USERNS) >= 0 || errno != EPERM)
86 return pr_err("Don't get EPERM");
87
88 kill(pid, SIGKILL);
89 wait(NULL);
90 return 0;
91}
diff --git a/tools/testing/selftests/nsfs/pidns.c b/tools/testing/selftests/nsfs/pidns.c
new file mode 100644
index 000000000000..ae3a0d68e966
--- /dev/null
+++ b/tools/testing/selftests/nsfs/pidns.c
@@ -0,0 +1,78 @@
1#define _GNU_SOURCE
2#include <sched.h>
3#include <unistd.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <signal.h>
7#include <errno.h>
8#include <sys/types.h>
9#include <sys/stat.h>
10#include <fcntl.h>
11#include <sys/ioctl.h>
12#include <sys/prctl.h>
13#include <sys/wait.h>
14
15#define pr_err(fmt, ...) \
16 ({ \
17 fprintf(stderr, "%s:%d:" fmt ": %m\n", \
18 __func__, __LINE__, ##__VA_ARGS__); \
19 1; \
20 })
21
22#define NSIO 0xb7
23#define NS_GET_USERNS _IO(NSIO, 0x1)
24#define NS_GET_PARENT _IO(NSIO, 0x2)
25
26#define __stack_aligned__ __attribute__((aligned(16)))
27struct cr_clone_arg {
28 char stack[128] __stack_aligned__;
29 char stack_ptr[0];
30};
31
32static int child(void *args)
33{
34 prctl(PR_SET_PDEATHSIG, SIGKILL);
35 while (1)
36 sleep(1);
37 exit(0);
38}
39
40int main(int argc, char *argv[])
41{
42 char *ns_strs[] = {"pid", "user"};
43 char path[] = "/proc/0123456789/ns/pid";
44 struct cr_clone_arg ca;
45 struct stat st1, st2;
46 int ns, pns, i;
47 pid_t pid;
48
49 pid = clone(child, ca.stack_ptr, CLONE_NEWUSER | CLONE_NEWPID | SIGCHLD, NULL);
50 if (pid < 0)
51 return pr_err("clone");
52
53 for (i = 0; i < 2; i++) {
54 snprintf(path, sizeof(path), "/proc/%d/ns/%s", pid, ns_strs[i]);
55 ns = open(path, O_RDONLY);
56 if (ns < 0)
57 return pr_err("Unable to open %s", path);
58
59 pns = ioctl(ns, NS_GET_PARENT);
60 if (pns < 0)
61 return pr_err("Unable to get a parent pidns");
62
63 snprintf(path, sizeof(path), "/proc/self/ns/%s", ns_strs[i]);
64 if (stat(path, &st2))
65 return pr_err("Unable to stat %s", path);
66 if (fstat(pns, &st1))
67 return pr_err("Unable to stat the parent pidns");
68 if (st1.st_ino != st2.st_ino)
69 return pr_err("NS_GET_PARENT returned a wrong namespace");
70
71 if (ioctl(pns, NS_GET_PARENT) >= 0 || errno != EPERM)
72 return pr_err("Don't get EPERM");;
73 }
74
75 kill(pid, SIGKILL);
76 wait(NULL);
77 return 0;
78}