diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-09 20:10:41 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-09 20:10:41 -0400 |
commit | 77e40aae766ccbbbb0324cb92ab22e6e998375d7 (patch) | |
tree | fb4e8e840aaeeaac62249d7585249c4634886baa /fs/proc | |
parent | 96784de59fb35077c2bb33c39328992b836d87d3 (diff) | |
parent | 344470cac42e887e68cfb5bdfa6171baf27f1eb5 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace
Pull namespace updates from Eric Biederman:
"This is a bunch of small changes built against 3.16-rc6. The most
significant change for users is the first patch which makes setns
drmatically faster by removing unneded rcu handling.
The next chunk of changes are so that "mount -o remount,.." will not
allow the user namespace root to drop flags on a mount set by the
system wide root. Aks this forces read-only mounts to stay read-only,
no-dev mounts to stay no-dev, no-suid mounts to stay no-suid, no-exec
mounts to stay no exec and it prevents unprivileged users from messing
with a mounts atime settings. I have included my test case as the
last patch in this series so people performing backports can verify
this change works correctly.
The next change fixes a bug in NFS that was discovered while auditing
nsproxy users for the first optimization. Today you can oops the
kernel by reading /proc/fs/nfsfs/{servers,volumes} if you are clever
with pid namespaces. I rebased and fixed the build of the
!CONFIG_NFS_FS case yesterday when a build bot caught my typo. Given
that no one to my knowledge bases anything on my tree fixing the typo
in place seems more responsible that requiring a typo-fix to be
backported as well.
The last change is a small semantic cleanup introducing
/proc/thread-self and pointing /proc/mounts and /proc/net at it. This
prevents several kinds of problemantic corner cases. It is a
user-visible change so it has a minute chance of causing regressions
so the change to /proc/mounts and /proc/net are individual one line
commits that can be trivially reverted. Unfortunately I lost and
could not find the email of the original reporter so he is not
credited. From at least one perspective this change to /proc/net is a
refgression fix to allow pthread /proc/net uses that were broken by
the introduction of the network namespace"
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace:
proc: Point /proc/mounts at /proc/thread-self/mounts instead of /proc/self/mounts
proc: Point /proc/net at /proc/thread-self/net instead of /proc/self/net
proc: Implement /proc/thread-self to point at the directory of the current thread
proc: Have net show up under /proc/<tgid>/task/<tid>
NFS: Fix /proc/fs/nfsfs/servers and /proc/fs/nfsfs/volumes
mnt: Add tests for unprivileged remount cases that have found to be faulty
mnt: Change the default remount atime from relatime to the existing value
mnt: Correct permission checks in do_remount
mnt: Move the test for MNT_LOCK_READONLY from change_mount_flags into do_remount
mnt: Only change user settable mount flags in remount
namespaces: Use task_lock and not rcu to protect nsproxy
Diffstat (limited to 'fs/proc')
-rw-r--r-- | fs/proc/Makefile | 1 | ||||
-rw-r--r-- | fs/proc/base.c | 18 | ||||
-rw-r--r-- | fs/proc/inode.c | 7 | ||||
-rw-r--r-- | fs/proc/internal.h | 6 | ||||
-rw-r--r-- | fs/proc/proc_net.c | 6 | ||||
-rw-r--r-- | fs/proc/root.c | 5 | ||||
-rw-r--r-- | fs/proc/thread_self.c | 85 |
7 files changed, 119 insertions, 9 deletions
diff --git a/fs/proc/Makefile b/fs/proc/Makefile index 239493ec718e..7151ea428041 100644 --- a/fs/proc/Makefile +++ b/fs/proc/Makefile | |||
@@ -23,6 +23,7 @@ proc-y += version.o | |||
23 | proc-y += softirqs.o | 23 | proc-y += softirqs.o |
24 | proc-y += namespaces.o | 24 | proc-y += namespaces.o |
25 | proc-y += self.o | 25 | proc-y += self.o |
26 | proc-y += thread_self.o | ||
26 | proc-$(CONFIG_PROC_SYSCTL) += proc_sysctl.o | 27 | proc-$(CONFIG_PROC_SYSCTL) += proc_sysctl.o |
27 | proc-$(CONFIG_NET) += proc_net.o | 28 | proc-$(CONFIG_NET) += proc_net.o |
28 | proc-$(CONFIG_PROC_KCORE) += kcore.o | 29 | proc-$(CONFIG_PROC_KCORE) += kcore.o |
diff --git a/fs/proc/base.c b/fs/proc/base.c index 043c83cb51f9..baf852b648ad 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -2814,7 +2814,7 @@ retry: | |||
2814 | return iter; | 2814 | return iter; |
2815 | } | 2815 | } |
2816 | 2816 | ||
2817 | #define TGID_OFFSET (FIRST_PROCESS_ENTRY + 1) | 2817 | #define TGID_OFFSET (FIRST_PROCESS_ENTRY + 2) |
2818 | 2818 | ||
2819 | /* for the /proc/ directory itself, after non-process stuff has been done */ | 2819 | /* for the /proc/ directory itself, after non-process stuff has been done */ |
2820 | int proc_pid_readdir(struct file *file, struct dir_context *ctx) | 2820 | int proc_pid_readdir(struct file *file, struct dir_context *ctx) |
@@ -2826,14 +2826,19 @@ int proc_pid_readdir(struct file *file, struct dir_context *ctx) | |||
2826 | if (pos >= PID_MAX_LIMIT + TGID_OFFSET) | 2826 | if (pos >= PID_MAX_LIMIT + TGID_OFFSET) |
2827 | return 0; | 2827 | return 0; |
2828 | 2828 | ||
2829 | if (pos == TGID_OFFSET - 1) { | 2829 | if (pos == TGID_OFFSET - 2) { |
2830 | struct inode *inode = ns->proc_self->d_inode; | 2830 | struct inode *inode = ns->proc_self->d_inode; |
2831 | if (!dir_emit(ctx, "self", 4, inode->i_ino, DT_LNK)) | 2831 | if (!dir_emit(ctx, "self", 4, inode->i_ino, DT_LNK)) |
2832 | return 0; | 2832 | return 0; |
2833 | iter.tgid = 0; | 2833 | ctx->pos = pos = pos + 1; |
2834 | } else { | 2834 | } |
2835 | iter.tgid = pos - TGID_OFFSET; | 2835 | if (pos == TGID_OFFSET - 1) { |
2836 | struct inode *inode = ns->proc_thread_self->d_inode; | ||
2837 | if (!dir_emit(ctx, "thread-self", 11, inode->i_ino, DT_LNK)) | ||
2838 | return 0; | ||
2839 | ctx->pos = pos = pos + 1; | ||
2836 | } | 2840 | } |
2841 | iter.tgid = pos - TGID_OFFSET; | ||
2837 | iter.task = NULL; | 2842 | iter.task = NULL; |
2838 | for (iter = next_tgid(ns, iter); | 2843 | for (iter = next_tgid(ns, iter); |
2839 | iter.task; | 2844 | iter.task; |
@@ -2862,6 +2867,9 @@ static const struct pid_entry tid_base_stuff[] = { | |||
2862 | DIR("fd", S_IRUSR|S_IXUSR, proc_fd_inode_operations, proc_fd_operations), | 2867 | DIR("fd", S_IRUSR|S_IXUSR, proc_fd_inode_operations, proc_fd_operations), |
2863 | DIR("fdinfo", S_IRUSR|S_IXUSR, proc_fdinfo_inode_operations, proc_fdinfo_operations), | 2868 | DIR("fdinfo", S_IRUSR|S_IXUSR, proc_fdinfo_inode_operations, proc_fdinfo_operations), |
2864 | DIR("ns", S_IRUSR|S_IXUGO, proc_ns_dir_inode_operations, proc_ns_dir_operations), | 2869 | DIR("ns", S_IRUSR|S_IXUGO, proc_ns_dir_inode_operations, proc_ns_dir_operations), |
2870 | #ifdef CONFIG_NET | ||
2871 | DIR("net", S_IRUGO|S_IXUGO, proc_net_inode_operations, proc_net_operations), | ||
2872 | #endif | ||
2865 | REG("environ", S_IRUSR, proc_environ_operations), | 2873 | REG("environ", S_IRUSR, proc_environ_operations), |
2866 | ONE("auxv", S_IRUSR, proc_pid_auxv), | 2874 | ONE("auxv", S_IRUSR, proc_pid_auxv), |
2867 | ONE("status", S_IRUGO, proc_pid_status), | 2875 | ONE("status", S_IRUGO, proc_pid_status), |
diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 0adbc02d60e3..333080d7a671 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c | |||
@@ -442,6 +442,7 @@ struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de) | |||
442 | int proc_fill_super(struct super_block *s) | 442 | int proc_fill_super(struct super_block *s) |
443 | { | 443 | { |
444 | struct inode *root_inode; | 444 | struct inode *root_inode; |
445 | int ret; | ||
445 | 446 | ||
446 | s->s_flags |= MS_NODIRATIME | MS_NOSUID | MS_NOEXEC; | 447 | s->s_flags |= MS_NODIRATIME | MS_NOSUID | MS_NOEXEC; |
447 | s->s_blocksize = 1024; | 448 | s->s_blocksize = 1024; |
@@ -463,5 +464,9 @@ int proc_fill_super(struct super_block *s) | |||
463 | return -ENOMEM; | 464 | return -ENOMEM; |
464 | } | 465 | } |
465 | 466 | ||
466 | return proc_setup_self(s); | 467 | ret = proc_setup_self(s); |
468 | if (ret) { | ||
469 | return ret; | ||
470 | } | ||
471 | return proc_setup_thread_self(s); | ||
467 | } | 472 | } |
diff --git a/fs/proc/internal.h b/fs/proc/internal.h index a024cf7b260f..7da13e49128a 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h | |||
@@ -231,6 +231,12 @@ static inline int proc_net_init(void) { return 0; } | |||
231 | extern int proc_setup_self(struct super_block *); | 231 | extern int proc_setup_self(struct super_block *); |
232 | 232 | ||
233 | /* | 233 | /* |
234 | * proc_thread_self.c | ||
235 | */ | ||
236 | extern int proc_setup_thread_self(struct super_block *); | ||
237 | extern void proc_thread_self_init(void); | ||
238 | |||
239 | /* | ||
234 | * proc_sysctl.c | 240 | * proc_sysctl.c |
235 | */ | 241 | */ |
236 | #ifdef CONFIG_PROC_SYSCTL | 242 | #ifdef CONFIG_PROC_SYSCTL |
diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c index 4677bb7dc7c2..39481028ec08 100644 --- a/fs/proc/proc_net.c +++ b/fs/proc/proc_net.c | |||
@@ -113,9 +113,11 @@ static struct net *get_proc_task_net(struct inode *dir) | |||
113 | rcu_read_lock(); | 113 | rcu_read_lock(); |
114 | task = pid_task(proc_pid(dir), PIDTYPE_PID); | 114 | task = pid_task(proc_pid(dir), PIDTYPE_PID); |
115 | if (task != NULL) { | 115 | if (task != NULL) { |
116 | ns = task_nsproxy(task); | 116 | task_lock(task); |
117 | ns = task->nsproxy; | ||
117 | if (ns != NULL) | 118 | if (ns != NULL) |
118 | net = get_net(ns->net_ns); | 119 | net = get_net(ns->net_ns); |
120 | task_unlock(task); | ||
119 | } | 121 | } |
120 | rcu_read_unlock(); | 122 | rcu_read_unlock(); |
121 | 123 | ||
@@ -224,7 +226,7 @@ static struct pernet_operations __net_initdata proc_net_ns_ops = { | |||
224 | 226 | ||
225 | int __init proc_net_init(void) | 227 | int __init proc_net_init(void) |
226 | { | 228 | { |
227 | proc_symlink("net", NULL, "self/net"); | 229 | proc_symlink("net", NULL, "thread-self/net"); |
228 | 230 | ||
229 | return register_pernet_subsys(&proc_net_ns_ops); | 231 | return register_pernet_subsys(&proc_net_ns_ops); |
230 | } | 232 | } |
diff --git a/fs/proc/root.c b/fs/proc/root.c index 574bafc41f0b..6296c7626963 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c | |||
@@ -149,6 +149,8 @@ static void proc_kill_sb(struct super_block *sb) | |||
149 | ns = (struct pid_namespace *)sb->s_fs_info; | 149 | ns = (struct pid_namespace *)sb->s_fs_info; |
150 | if (ns->proc_self) | 150 | if (ns->proc_self) |
151 | dput(ns->proc_self); | 151 | dput(ns->proc_self); |
152 | if (ns->proc_thread_self) | ||
153 | dput(ns->proc_thread_self); | ||
152 | kill_anon_super(sb); | 154 | kill_anon_super(sb); |
153 | put_pid_ns(ns); | 155 | put_pid_ns(ns); |
154 | } | 156 | } |
@@ -170,7 +172,8 @@ void __init proc_root_init(void) | |||
170 | return; | 172 | return; |
171 | 173 | ||
172 | proc_self_init(); | 174 | proc_self_init(); |
173 | proc_symlink("mounts", NULL, "self/mounts"); | 175 | proc_thread_self_init(); |
176 | proc_symlink("mounts", NULL, "thread-self/mounts"); | ||
174 | 177 | ||
175 | proc_net_init(); | 178 | proc_net_init(); |
176 | 179 | ||
diff --git a/fs/proc/thread_self.c b/fs/proc/thread_self.c new file mode 100644 index 000000000000..59075b509df3 --- /dev/null +++ b/fs/proc/thread_self.c | |||
@@ -0,0 +1,85 @@ | |||
1 | #include <linux/sched.h> | ||
2 | #include <linux/namei.h> | ||
3 | #include <linux/slab.h> | ||
4 | #include <linux/pid_namespace.h> | ||
5 | #include "internal.h" | ||
6 | |||
7 | /* | ||
8 | * /proc/thread_self: | ||
9 | */ | ||
10 | static int proc_thread_self_readlink(struct dentry *dentry, char __user *buffer, | ||
11 | int buflen) | ||
12 | { | ||
13 | struct pid_namespace *ns = dentry->d_sb->s_fs_info; | ||
14 | pid_t tgid = task_tgid_nr_ns(current, ns); | ||
15 | pid_t pid = task_pid_nr_ns(current, ns); | ||
16 | char tmp[PROC_NUMBUF + 6 + PROC_NUMBUF]; | ||
17 | if (!pid) | ||
18 | return -ENOENT; | ||
19 | sprintf(tmp, "%d/task/%d", tgid, pid); | ||
20 | return readlink_copy(buffer, buflen, tmp); | ||
21 | } | ||
22 | |||
23 | static void *proc_thread_self_follow_link(struct dentry *dentry, struct nameidata *nd) | ||
24 | { | ||
25 | struct pid_namespace *ns = dentry->d_sb->s_fs_info; | ||
26 | pid_t tgid = task_tgid_nr_ns(current, ns); | ||
27 | pid_t pid = task_pid_nr_ns(current, ns); | ||
28 | char *name = ERR_PTR(-ENOENT); | ||
29 | if (pid) { | ||
30 | name = kmalloc(PROC_NUMBUF + 6 + PROC_NUMBUF, GFP_KERNEL); | ||
31 | if (!name) | ||
32 | name = ERR_PTR(-ENOMEM); | ||
33 | else | ||
34 | sprintf(name, "%d/task/%d", tgid, pid); | ||
35 | } | ||
36 | nd_set_link(nd, name); | ||
37 | return NULL; | ||
38 | } | ||
39 | |||
40 | static const struct inode_operations proc_thread_self_inode_operations = { | ||
41 | .readlink = proc_thread_self_readlink, | ||
42 | .follow_link = proc_thread_self_follow_link, | ||
43 | .put_link = kfree_put_link, | ||
44 | }; | ||
45 | |||
46 | static unsigned thread_self_inum; | ||
47 | |||
48 | int proc_setup_thread_self(struct super_block *s) | ||
49 | { | ||
50 | struct inode *root_inode = s->s_root->d_inode; | ||
51 | struct pid_namespace *ns = s->s_fs_info; | ||
52 | struct dentry *thread_self; | ||
53 | |||
54 | mutex_lock(&root_inode->i_mutex); | ||
55 | thread_self = d_alloc_name(s->s_root, "thread-self"); | ||
56 | if (thread_self) { | ||
57 | struct inode *inode = new_inode_pseudo(s); | ||
58 | if (inode) { | ||
59 | inode->i_ino = thread_self_inum; | ||
60 | inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; | ||
61 | inode->i_mode = S_IFLNK | S_IRWXUGO; | ||
62 | inode->i_uid = GLOBAL_ROOT_UID; | ||
63 | inode->i_gid = GLOBAL_ROOT_GID; | ||
64 | inode->i_op = &proc_thread_self_inode_operations; | ||
65 | d_add(thread_self, inode); | ||
66 | } else { | ||
67 | dput(thread_self); | ||
68 | thread_self = ERR_PTR(-ENOMEM); | ||
69 | } | ||
70 | } else { | ||
71 | thread_self = ERR_PTR(-ENOMEM); | ||
72 | } | ||
73 | mutex_unlock(&root_inode->i_mutex); | ||
74 | if (IS_ERR(thread_self)) { | ||
75 | pr_err("proc_fill_super: can't allocate /proc/thread_self\n"); | ||
76 | return PTR_ERR(thread_self); | ||
77 | } | ||
78 | ns->proc_thread_self = thread_self; | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | void __init proc_thread_self_init(void) | ||
83 | { | ||
84 | proc_alloc_inum(&thread_self_inum); | ||
85 | } | ||