diff options
-rw-r--r-- | fs/namespace.c | 6 | ||||
-rw-r--r-- | fs/nsfs.c | 100 | ||||
-rw-r--r-- | include/linux/proc_ns.h | 2 | ||||
-rw-r--r-- | include/linux/user_namespace.h | 7 | ||||
-rw-r--r-- | include/uapi/linux/nsfs.h | 13 | ||||
-rw-r--r-- | ipc/namespace.c | 6 | ||||
-rw-r--r-- | kernel/cgroup.c | 6 | ||||
-rw-r--r-- | kernel/pid_namespace.c | 25 | ||||
-rw-r--r-- | kernel/user_namespace.c | 25 | ||||
-rw-r--r-- | kernel/utsname.c | 6 | ||||
-rw-r--r-- | net/core/net_namespace.c | 6 | ||||
-rw-r--r-- | tools/testing/selftests/Makefile | 1 | ||||
-rw-r--r-- | tools/testing/selftests/nsfs/Makefile | 12 | ||||
-rw-r--r-- | tools/testing/selftests/nsfs/owner.c | 91 | ||||
-rw-r--r-- | tools/testing/selftests/nsfs/pidns.c | 78 |
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 | ||
3371 | static struct user_namespace *mntns_owner(struct ns_common *ns) | ||
3372 | { | ||
3373 | return to_mnt_ns(ns)->user_ns; | ||
3374 | } | ||
3375 | |||
3371 | const struct proc_ns_operations mntns_operations = { | 3376 | const 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 | }; |
@@ -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 | ||
9 | static struct vfsmount *nsfs_mnt; | 11 | static struct vfsmount *nsfs_mnt; |
10 | 12 | ||
13 | static long ns_ioctl(struct file *filp, unsigned int ioctl, | ||
14 | unsigned long arg); | ||
11 | static const struct file_operations ns_file_operations = { | 15 | static const struct file_operations ns_file_operations = { |
12 | .llseek = no_llseek, | 16 | .llseek = no_llseek, |
17 | .unlocked_ioctl = ns_ioctl, | ||
13 | }; | 18 | }; |
14 | 19 | ||
15 | static char *ns_dname(struct dentry *dentry, char *buffer, int buflen) | 20 | static 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 | ||
47 | void *ns_get_path(struct path *path, struct task_struct *task, | 52 | static 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 | ||
57 | again: | ||
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); |
72 | got_it: | 69 | got_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 | ||
107 | void *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 | |||
113 | again: | ||
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 | |||
124 | static 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 | |||
166 | static 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 | |||
109 | int ns_get_name(char *buf, size_t size, struct task_struct *task, | 183 | int 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 | ||
23 | extern const struct proc_ns_operations netns_operations; | 25 | extern 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, | |||
106 | extern int proc_setgroups_show(struct seq_file *m, void *v); | 106 | extern int proc_setgroups_show(struct seq_file *m, void *v); |
107 | extern bool userns_may_setgroups(const struct user_namespace *ns); | 107 | extern bool userns_may_setgroups(const struct user_namespace *ns); |
108 | extern bool current_in_userns(const struct user_namespace *target_ns); | 108 | extern bool current_in_userns(const struct user_namespace *target_ns); |
109 | |||
110 | struct ns_common *ns_get_owner(struct ns_common *ns); | ||
109 | #else | 111 | #else |
110 | 112 | ||
111 | static inline struct user_namespace *get_user_ns(struct user_namespace *ns) | 113 | static 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 | |||
145 | static 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 | ||
191 | static struct user_namespace *ipcns_owner(struct ns_common *ns) | ||
192 | { | ||
193 | return to_ipc_ns(ns)->user_ns; | ||
194 | } | ||
195 | |||
191 | const struct proc_ns_operations ipcns_operations = { | 196 | const 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 | ||
6424 | static struct user_namespace *cgroupns_owner(struct ns_common *ns) | ||
6425 | { | ||
6426 | return to_cg_ns(ns)->user_ns; | ||
6427 | } | ||
6428 | |||
6424 | const struct proc_ns_operations cgroupns_operations = { | 6429 | const 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 | ||
6432 | static __init int cgroup_namespaces_init(void) | 6438 | static __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 | ||
408 | static 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 | |||
426 | static struct user_namespace *pidns_owner(struct ns_common *ns) | ||
427 | { | ||
428 | return to_pid_ns(ns)->user_ns; | ||
429 | } | ||
430 | |||
408 | const struct proc_ns_operations pidns_operations = { | 431 | const 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 | ||
416 | static __init int pid_namespaces_init(void) | 441 | static __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 | ||
1053 | struct 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 | |||
1071 | static struct user_namespace *userns_owner(struct ns_common *ns) | ||
1072 | { | ||
1073 | return to_user_ns(ns)->parent; | ||
1074 | } | ||
1075 | |||
1053 | const struct proc_ns_operations userns_operations = { | 1076 | const 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 | ||
1061 | static __init int user_namespaces_init(void) | 1086 | static __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 | ||
157 | static struct user_namespace *utsns_owner(struct ns_common *ns) | ||
158 | { | ||
159 | return to_uts_ns(ns)->user_ns; | ||
160 | } | ||
161 | |||
157 | const struct proc_ns_operations utsns_operations = { | 162 | const 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 | ||
1019 | static struct user_namespace *netns_owner(struct ns_common *ns) | ||
1020 | { | ||
1021 | return to_net_ns(ns)->user_ns; | ||
1022 | } | ||
1023 | |||
1019 | const struct proc_ns_operations netns_operations = { | 1024 | const 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 | |||
15 | TARGETS += mount | 15 | TARGETS += mount |
16 | TARGETS += mqueue | 16 | TARGETS += mqueue |
17 | TARGETS += net | 17 | TARGETS += net |
18 | TARGETS += nsfs | ||
18 | TARGETS += powerpc | 19 | TARGETS += powerpc |
19 | TARGETS += pstore | 20 | TARGETS += pstore |
20 | TARGETS += ptrace | 21 | TARGETS += 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 @@ | |||
1 | TEST_PROGS := owner pidns | ||
2 | |||
3 | CFLAGS := -Wall -Werror | ||
4 | |||
5 | all: owner pidns | ||
6 | owner: owner.c | ||
7 | pidns: pidns.c | ||
8 | |||
9 | clean: | ||
10 | $(RM) owner pidns | ||
11 | |||
12 | include ../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 | |||
25 | int 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))) | ||
27 | struct cr_clone_arg { | ||
28 | char stack[128] __stack_aligned__; | ||
29 | char stack_ptr[0]; | ||
30 | }; | ||
31 | |||
32 | static int child(void *args) | ||
33 | { | ||
34 | prctl(PR_SET_PDEATHSIG, SIGKILL); | ||
35 | while (1) | ||
36 | sleep(1); | ||
37 | exit(0); | ||
38 | } | ||
39 | |||
40 | int 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 | } | ||