aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2016-09-22 21:00:36 -0400
committerEric W. Biederman <ebiederm@xmission.com>2016-09-22 21:00:36 -0400
commit78725596644be0181c46f55c52aadfb8c70bcdb7 (patch)
treebaaea28de07a45f932f7674cfcd6c83522940770
parent93f0a88bd4ad99a515f500a09f4a489ff03073eb (diff)
parent6ad92bf63e45f97e306da48cd1cbce6e4fef1e5d (diff)
Merge branch 'nsfs-ioctls' into HEAD
From: Andrey Vagin <avagin@openvz.org> Each namespace has an owning user namespace and now there is not way to discover these relationships. Pid and user namepaces are hierarchical. There is no way to discover parent-child relationships too. Why we may want to know relationships between namespaces? One use would be visualization, in order to understand the running system. Another would be to answer the question: what capability does process X have to perform operations on a resource governed by namespace Y? One more use-case (which usually called abnormal) is checkpoint/restart. In CRIU we are going to dump and restore nested namespaces. There [1] was a discussion about which interface to choose to determing relationships between namespaces. Eric suggested to add two ioctl-s [2]: > Grumble, Grumble. I think this may actually a case for creating ioctls > for these two cases. Now that random nsfs file descriptors are bind > mountable the original reason for using proc files is not as pressing. > > One ioctl for the user namespace that owns a file descriptor. > One ioctl for the parent namespace of a namespace file descriptor. Here is an implementaions of these ioctl-s. $ man man7/namespaces.7 ... Since Linux 4.X, the following ioctl(2) calls are supported for namespace file descriptors. The correct syntax is: fd = ioctl(ns_fd, ioctl_type); where ioctl_type is one of the following: NS_GET_USERNS Returns a file descriptor that refers to an owning user namesā€ pace. NS_GET_PARENT Returns a file descriptor that refers to a parent namespace. This ioctl(2) can be used for pid and user namespaces. For user namespaces, NS_GET_PARENT and NS_GET_USERNS have the same meaning. In addition to generic ioctl(2) errors, the following specific ones can occur: EINVAL NS_GET_PARENT was called for a nonhierarchical namespace. EPERM The requested namespace is outside of the current namespace scope. [1] https://lkml.org/lkml/2016/7/6/158 [2] https://lkml.org/lkml/2016/7/9/101 Changes for v2: * don't return ENOENT for init_user_ns and init_pid_ns. There is nothing outside of the init namespace, so we can return EPERM in this case too. > The fewer special cases the easier the code is to get > correct, and the easier it is to read. // Eric Changes for v3: * rename ns->get_owner() to ns->owner(). get_* usually means that it grabs a reference. Cc: "Eric W. Biederman" <ebiederm@xmission.com> Cc: James Bottomley <James.Bottomley@HansenPartnership.com> Cc: "Michael Kerrisk (man-pages)" <mtk.manpages@gmail.com> Cc: "W. Trevor King" <wking@tremily.us> Cc: Alexander Viro <viro@zeniv.linux.org.uk> Cc: Serge Hallyn <serge.hallyn@canonical.com>
-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}