diff options
| -rw-r--r-- | fs/namespace.c | 65 | ||||
| -rw-r--r-- | fs/nfs/client.c | 95 | ||||
| -rw-r--r-- | fs/nfs/inode.c | 3 | ||||
| -rw-r--r-- | fs/nfs/internal.h | 9 | ||||
| -rw-r--r-- | fs/nfs/netns.h | 3 | ||||
| -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 | ||||
| -rw-r--r-- | fs/proc_namespace.c | 8 | ||||
| -rw-r--r-- | include/linux/mount.h | 9 | ||||
| -rw-r--r-- | include/linux/nsproxy.h | 16 | ||||
| -rw-r--r-- | include/linux/pid_namespace.h | 1 | ||||
| -rw-r--r-- | ipc/namespace.c | 6 | ||||
| -rw-r--r-- | kernel/nsproxy.c | 15 | ||||
| -rw-r--r-- | kernel/utsname.c | 6 | ||||
| -rw-r--r-- | net/core/net_namespace.c | 10 | ||||
| -rw-r--r-- | tools/testing/selftests/Makefile | 1 | ||||
| -rw-r--r-- | tools/testing/selftests/mount/Makefile | 17 | ||||
| -rw-r--r-- | tools/testing/selftests/mount/unprivileged-remount-test.c | 242 |
23 files changed, 537 insertions, 97 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index 2a1447c946e7..0acabea58319 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
| @@ -890,8 +890,21 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root, | |||
| 890 | 890 | ||
| 891 | mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~(MNT_WRITE_HOLD|MNT_MARKED); | 891 | mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~(MNT_WRITE_HOLD|MNT_MARKED); |
| 892 | /* Don't allow unprivileged users to change mount flags */ | 892 | /* Don't allow unprivileged users to change mount flags */ |
| 893 | if ((flag & CL_UNPRIVILEGED) && (mnt->mnt.mnt_flags & MNT_READONLY)) | 893 | if (flag & CL_UNPRIVILEGED) { |
| 894 | mnt->mnt.mnt_flags |= MNT_LOCK_READONLY; | 894 | mnt->mnt.mnt_flags |= MNT_LOCK_ATIME; |
| 895 | |||
| 896 | if (mnt->mnt.mnt_flags & MNT_READONLY) | ||
| 897 | mnt->mnt.mnt_flags |= MNT_LOCK_READONLY; | ||
| 898 | |||
| 899 | if (mnt->mnt.mnt_flags & MNT_NODEV) | ||
| 900 | mnt->mnt.mnt_flags |= MNT_LOCK_NODEV; | ||
| 901 | |||
| 902 | if (mnt->mnt.mnt_flags & MNT_NOSUID) | ||
| 903 | mnt->mnt.mnt_flags |= MNT_LOCK_NOSUID; | ||
| 904 | |||
| 905 | if (mnt->mnt.mnt_flags & MNT_NOEXEC) | ||
| 906 | mnt->mnt.mnt_flags |= MNT_LOCK_NOEXEC; | ||
| 907 | } | ||
| 895 | 908 | ||
| 896 | /* Don't allow unprivileged users to reveal what is under a mount */ | 909 | /* Don't allow unprivileged users to reveal what is under a mount */ |
| 897 | if ((flag & CL_UNPRIVILEGED) && list_empty(&old->mnt_expire)) | 910 | if ((flag & CL_UNPRIVILEGED) && list_empty(&old->mnt_expire)) |
| @@ -1896,9 +1909,6 @@ static int change_mount_flags(struct vfsmount *mnt, int ms_flags) | |||
| 1896 | if (readonly_request == __mnt_is_readonly(mnt)) | 1909 | if (readonly_request == __mnt_is_readonly(mnt)) |
| 1897 | return 0; | 1910 | return 0; |
| 1898 | 1911 | ||
| 1899 | if (mnt->mnt_flags & MNT_LOCK_READONLY) | ||
| 1900 | return -EPERM; | ||
| 1901 | |||
| 1902 | if (readonly_request) | 1912 | if (readonly_request) |
| 1903 | error = mnt_make_readonly(real_mount(mnt)); | 1913 | error = mnt_make_readonly(real_mount(mnt)); |
| 1904 | else | 1914 | else |
| @@ -1924,6 +1934,33 @@ static int do_remount(struct path *path, int flags, int mnt_flags, | |||
| 1924 | if (path->dentry != path->mnt->mnt_root) | 1934 | if (path->dentry != path->mnt->mnt_root) |
| 1925 | return -EINVAL; | 1935 | return -EINVAL; |
| 1926 | 1936 | ||
| 1937 | /* Don't allow changing of locked mnt flags. | ||
| 1938 | * | ||
| 1939 | * No locks need to be held here while testing the various | ||
| 1940 | * MNT_LOCK flags because those flags can never be cleared | ||
| 1941 | * once they are set. | ||
| 1942 | */ | ||
| 1943 | if ((mnt->mnt.mnt_flags & MNT_LOCK_READONLY) && | ||
| 1944 | !(mnt_flags & MNT_READONLY)) { | ||
| 1945 | return -EPERM; | ||
| 1946 | } | ||
| 1947 | if ((mnt->mnt.mnt_flags & MNT_LOCK_NODEV) && | ||
| 1948 | !(mnt_flags & MNT_NODEV)) { | ||
| 1949 | return -EPERM; | ||
| 1950 | } | ||
| 1951 | if ((mnt->mnt.mnt_flags & MNT_LOCK_NOSUID) && | ||
| 1952 | !(mnt_flags & MNT_NOSUID)) { | ||
| 1953 | return -EPERM; | ||
| 1954 | } | ||
| 1955 | if ((mnt->mnt.mnt_flags & MNT_LOCK_NOEXEC) && | ||
| 1956 | !(mnt_flags & MNT_NOEXEC)) { | ||
| 1957 | return -EPERM; | ||
| 1958 | } | ||
| 1959 | if ((mnt->mnt.mnt_flags & MNT_LOCK_ATIME) && | ||
| 1960 | ((mnt->mnt.mnt_flags & MNT_ATIME_MASK) != (mnt_flags & MNT_ATIME_MASK))) { | ||
| 1961 | return -EPERM; | ||
| 1962 | } | ||
| 1963 | |||
| 1927 | err = security_sb_remount(sb, data); | 1964 | err = security_sb_remount(sb, data); |
| 1928 | if (err) | 1965 | if (err) |
| 1929 | return err; | 1966 | return err; |
| @@ -1937,7 +1974,7 @@ static int do_remount(struct path *path, int flags, int mnt_flags, | |||
| 1937 | err = do_remount_sb(sb, flags, data, 0); | 1974 | err = do_remount_sb(sb, flags, data, 0); |
| 1938 | if (!err) { | 1975 | if (!err) { |
| 1939 | lock_mount_hash(); | 1976 | lock_mount_hash(); |
| 1940 | mnt_flags |= mnt->mnt.mnt_flags & MNT_PROPAGATION_MASK; | 1977 | mnt_flags |= mnt->mnt.mnt_flags & ~MNT_USER_SETTABLE_MASK; |
| 1941 | mnt->mnt.mnt_flags = mnt_flags; | 1978 | mnt->mnt.mnt_flags = mnt_flags; |
| 1942 | touch_mnt_namespace(mnt->mnt_ns); | 1979 | touch_mnt_namespace(mnt->mnt_ns); |
| 1943 | unlock_mount_hash(); | 1980 | unlock_mount_hash(); |
| @@ -2122,7 +2159,7 @@ static int do_new_mount(struct path *path, const char *fstype, int flags, | |||
| 2122 | */ | 2159 | */ |
| 2123 | if (!(type->fs_flags & FS_USERNS_DEV_MOUNT)) { | 2160 | if (!(type->fs_flags & FS_USERNS_DEV_MOUNT)) { |
| 2124 | flags |= MS_NODEV; | 2161 | flags |= MS_NODEV; |
| 2125 | mnt_flags |= MNT_NODEV; | 2162 | mnt_flags |= MNT_NODEV | MNT_LOCK_NODEV; |
| 2126 | } | 2163 | } |
| 2127 | } | 2164 | } |
| 2128 | 2165 | ||
| @@ -2436,6 +2473,14 @@ long do_mount(const char *dev_name, const char *dir_name, | |||
| 2436 | if (flags & MS_RDONLY) | 2473 | if (flags & MS_RDONLY) |
| 2437 | mnt_flags |= MNT_READONLY; | 2474 | mnt_flags |= MNT_READONLY; |
| 2438 | 2475 | ||
| 2476 | /* The default atime for remount is preservation */ | ||
| 2477 | if ((flags & MS_REMOUNT) && | ||
| 2478 | ((flags & (MS_NOATIME | MS_NODIRATIME | MS_RELATIME | | ||
| 2479 | MS_STRICTATIME)) == 0)) { | ||
| 2480 | mnt_flags &= ~MNT_ATIME_MASK; | ||
| 2481 | mnt_flags |= path.mnt->mnt_flags & MNT_ATIME_MASK; | ||
| 2482 | } | ||
| 2483 | |||
| 2439 | flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | MS_BORN | | 2484 | flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | MS_BORN | |
| 2440 | MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT | | 2485 | MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT | |
| 2441 | MS_STRICTATIME); | 2486 | MS_STRICTATIME); |
| @@ -2972,13 +3017,13 @@ static void *mntns_get(struct task_struct *task) | |||
| 2972 | struct mnt_namespace *ns = NULL; | 3017 | struct mnt_namespace *ns = NULL; |
| 2973 | struct nsproxy *nsproxy; | 3018 | struct nsproxy *nsproxy; |
| 2974 | 3019 | ||
| 2975 | rcu_read_lock(); | 3020 | task_lock(task); |
| 2976 | nsproxy = task_nsproxy(task); | 3021 | nsproxy = task->nsproxy; |
| 2977 | if (nsproxy) { | 3022 | if (nsproxy) { |
| 2978 | ns = nsproxy->mnt_ns; | 3023 | ns = nsproxy->mnt_ns; |
| 2979 | get_mnt_ns(ns); | 3024 | get_mnt_ns(ns); |
| 2980 | } | 3025 | } |
| 2981 | rcu_read_unlock(); | 3026 | task_unlock(task); |
| 2982 | 3027 | ||
| 2983 | return ns; | 3028 | return ns; |
| 2984 | } | 3029 | } |
diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 1d09289c8f0e..180d1ec9c32e 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c | |||
| @@ -1205,7 +1205,7 @@ static const struct file_operations nfs_server_list_fops = { | |||
| 1205 | .open = nfs_server_list_open, | 1205 | .open = nfs_server_list_open, |
| 1206 | .read = seq_read, | 1206 | .read = seq_read, |
| 1207 | .llseek = seq_lseek, | 1207 | .llseek = seq_lseek, |
| 1208 | .release = seq_release, | 1208 | .release = seq_release_net, |
| 1209 | .owner = THIS_MODULE, | 1209 | .owner = THIS_MODULE, |
| 1210 | }; | 1210 | }; |
| 1211 | 1211 | ||
| @@ -1226,7 +1226,7 @@ static const struct file_operations nfs_volume_list_fops = { | |||
| 1226 | .open = nfs_volume_list_open, | 1226 | .open = nfs_volume_list_open, |
| 1227 | .read = seq_read, | 1227 | .read = seq_read, |
| 1228 | .llseek = seq_lseek, | 1228 | .llseek = seq_lseek, |
| 1229 | .release = seq_release, | 1229 | .release = seq_release_net, |
| 1230 | .owner = THIS_MODULE, | 1230 | .owner = THIS_MODULE, |
| 1231 | }; | 1231 | }; |
| 1232 | 1232 | ||
| @@ -1236,19 +1236,8 @@ static const struct file_operations nfs_volume_list_fops = { | |||
| 1236 | */ | 1236 | */ |
| 1237 | static int nfs_server_list_open(struct inode *inode, struct file *file) | 1237 | static int nfs_server_list_open(struct inode *inode, struct file *file) |
| 1238 | { | 1238 | { |
| 1239 | struct seq_file *m; | 1239 | return seq_open_net(inode, file, &nfs_server_list_ops, |
| 1240 | int ret; | 1240 | sizeof(struct seq_net_private)); |
| 1241 | struct pid_namespace *pid_ns = file->f_dentry->d_sb->s_fs_info; | ||
| 1242 | struct net *net = pid_ns->child_reaper->nsproxy->net_ns; | ||
| 1243 | |||
| 1244 | ret = seq_open(file, &nfs_server_list_ops); | ||
| 1245 | if (ret < 0) | ||
| 1246 | return ret; | ||
| 1247 | |||
| 1248 | m = file->private_data; | ||
| 1249 | m->private = net; | ||
| 1250 | |||
| 1251 | return 0; | ||
| 1252 | } | 1241 | } |
| 1253 | 1242 | ||
| 1254 | /* | 1243 | /* |
| @@ -1256,7 +1245,7 @@ static int nfs_server_list_open(struct inode *inode, struct file *file) | |||
| 1256 | */ | 1245 | */ |
| 1257 | static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos) | 1246 | static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos) |
| 1258 | { | 1247 | { |
| 1259 | struct nfs_net *nn = net_generic(m->private, nfs_net_id); | 1248 | struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); |
| 1260 | 1249 | ||
| 1261 | /* lock the list against modification */ | 1250 | /* lock the list against modification */ |
| 1262 | spin_lock(&nn->nfs_client_lock); | 1251 | spin_lock(&nn->nfs_client_lock); |
| @@ -1268,7 +1257,7 @@ static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos) | |||
| 1268 | */ | 1257 | */ |
| 1269 | static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos) | 1258 | static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos) |
| 1270 | { | 1259 | { |
| 1271 | struct nfs_net *nn = net_generic(p->private, nfs_net_id); | 1260 | struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); |
| 1272 | 1261 | ||
| 1273 | return seq_list_next(v, &nn->nfs_client_list, pos); | 1262 | return seq_list_next(v, &nn->nfs_client_list, pos); |
| 1274 | } | 1263 | } |
| @@ -1278,7 +1267,7 @@ static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos) | |||
| 1278 | */ | 1267 | */ |
| 1279 | static void nfs_server_list_stop(struct seq_file *p, void *v) | 1268 | static void nfs_server_list_stop(struct seq_file *p, void *v) |
| 1280 | { | 1269 | { |
| 1281 | struct nfs_net *nn = net_generic(p->private, nfs_net_id); | 1270 | struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); |
| 1282 | 1271 | ||
| 1283 | spin_unlock(&nn->nfs_client_lock); | 1272 | spin_unlock(&nn->nfs_client_lock); |
| 1284 | } | 1273 | } |
| @@ -1289,7 +1278,7 @@ static void nfs_server_list_stop(struct seq_file *p, void *v) | |||
| 1289 | static int nfs_server_list_show(struct seq_file *m, void *v) | 1278 | static int nfs_server_list_show(struct seq_file *m, void *v) |
| 1290 | { | 1279 | { |
| 1291 | struct nfs_client *clp; | 1280 | struct nfs_client *clp; |
| 1292 | struct nfs_net *nn = net_generic(m->private, nfs_net_id); | 1281 | struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); |
| 1293 | 1282 | ||
| 1294 | /* display header on line 1 */ | 1283 | /* display header on line 1 */ |
| 1295 | if (v == &nn->nfs_client_list) { | 1284 | if (v == &nn->nfs_client_list) { |
| @@ -1321,19 +1310,8 @@ static int nfs_server_list_show(struct seq_file *m, void *v) | |||
| 1321 | */ | 1310 | */ |
| 1322 | static int nfs_volume_list_open(struct inode *inode, struct file *file) | 1311 | static int nfs_volume_list_open(struct inode *inode, struct file *file) |
| 1323 | { | 1312 | { |
| 1324 | struct seq_file *m; | 1313 | return seq_open_net(inode, file, &nfs_server_list_ops, |
| 1325 | int ret; | 1314 | sizeof(struct seq_net_private)); |
| 1326 | struct pid_namespace *pid_ns = file->f_dentry->d_sb->s_fs_info; | ||
| 1327 | struct net *net = pid_ns->child_reaper->nsproxy->net_ns; | ||
| 1328 | |||
| 1329 | ret = seq_open(file, &nfs_volume_list_ops); | ||
| 1330 | if (ret < 0) | ||
| 1331 | return ret; | ||
| 1332 | |||
| 1333 | m = file->private_data; | ||
| 1334 | m->private = net; | ||
| 1335 | |||
| 1336 | return 0; | ||
| 1337 | } | 1315 | } |
| 1338 | 1316 | ||
| 1339 | /* | 1317 | /* |
| @@ -1341,7 +1319,7 @@ static int nfs_volume_list_open(struct inode *inode, struct file *file) | |||
| 1341 | */ | 1319 | */ |
| 1342 | static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos) | 1320 | static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos) |
| 1343 | { | 1321 | { |
| 1344 | struct nfs_net *nn = net_generic(m->private, nfs_net_id); | 1322 | struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); |
| 1345 | 1323 | ||
| 1346 | /* lock the list against modification */ | 1324 | /* lock the list against modification */ |
| 1347 | spin_lock(&nn->nfs_client_lock); | 1325 | spin_lock(&nn->nfs_client_lock); |
| @@ -1353,7 +1331,7 @@ static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos) | |||
| 1353 | */ | 1331 | */ |
| 1354 | static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos) | 1332 | static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos) |
| 1355 | { | 1333 | { |
| 1356 | struct nfs_net *nn = net_generic(p->private, nfs_net_id); | 1334 | struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); |
| 1357 | 1335 | ||
| 1358 | return seq_list_next(v, &nn->nfs_volume_list, pos); | 1336 | return seq_list_next(v, &nn->nfs_volume_list, pos); |
| 1359 | } | 1337 | } |
| @@ -1363,7 +1341,7 @@ static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos) | |||
| 1363 | */ | 1341 | */ |
| 1364 | static void nfs_volume_list_stop(struct seq_file *p, void *v) | 1342 | static void nfs_volume_list_stop(struct seq_file *p, void *v) |
| 1365 | { | 1343 | { |
| 1366 | struct nfs_net *nn = net_generic(p->private, nfs_net_id); | 1344 | struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); |
| 1367 | 1345 | ||
| 1368 | spin_unlock(&nn->nfs_client_lock); | 1346 | spin_unlock(&nn->nfs_client_lock); |
| 1369 | } | 1347 | } |
| @@ -1376,7 +1354,7 @@ static int nfs_volume_list_show(struct seq_file *m, void *v) | |||
| 1376 | struct nfs_server *server; | 1354 | struct nfs_server *server; |
| 1377 | struct nfs_client *clp; | 1355 | struct nfs_client *clp; |
| 1378 | char dev[8], fsid[17]; | 1356 | char dev[8], fsid[17]; |
| 1379 | struct nfs_net *nn = net_generic(m->private, nfs_net_id); | 1357 | struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); |
| 1380 | 1358 | ||
| 1381 | /* display header on line 1 */ | 1359 | /* display header on line 1 */ |
| 1382 | if (v == &nn->nfs_volume_list) { | 1360 | if (v == &nn->nfs_volume_list) { |
| @@ -1407,6 +1385,45 @@ static int nfs_volume_list_show(struct seq_file *m, void *v) | |||
| 1407 | return 0; | 1385 | return 0; |
| 1408 | } | 1386 | } |
| 1409 | 1387 | ||
| 1388 | int nfs_fs_proc_net_init(struct net *net) | ||
| 1389 | { | ||
| 1390 | struct nfs_net *nn = net_generic(net, nfs_net_id); | ||
| 1391 | struct proc_dir_entry *p; | ||
| 1392 | |||
| 1393 | nn->proc_nfsfs = proc_net_mkdir(net, "nfsfs", net->proc_net); | ||
| 1394 | if (!nn->proc_nfsfs) | ||
| 1395 | goto error_0; | ||
| 1396 | |||
| 1397 | /* a file of servers with which we're dealing */ | ||
| 1398 | p = proc_create("servers", S_IFREG|S_IRUGO, | ||
| 1399 | nn->proc_nfsfs, &nfs_server_list_fops); | ||
| 1400 | if (!p) | ||
| 1401 | goto error_1; | ||
| 1402 | |||
| 1403 | /* a file of volumes that we have mounted */ | ||
| 1404 | p = proc_create("volumes", S_IFREG|S_IRUGO, | ||
| 1405 | nn->proc_nfsfs, &nfs_volume_list_fops); | ||
| 1406 | if (!p) | ||
| 1407 | goto error_2; | ||
| 1408 | return 0; | ||
| 1409 | |||
| 1410 | error_2: | ||
| 1411 | remove_proc_entry("servers", nn->proc_nfsfs); | ||
| 1412 | error_1: | ||
| 1413 | remove_proc_entry("fs/nfsfs", NULL); | ||
| 1414 | error_0: | ||
| 1415 | return -ENOMEM; | ||
| 1416 | } | ||
| 1417 | |||
| 1418 | void nfs_fs_proc_net_exit(struct net *net) | ||
| 1419 | { | ||
| 1420 | struct nfs_net *nn = net_generic(net, nfs_net_id); | ||
| 1421 | |||
| 1422 | remove_proc_entry("volumes", nn->proc_nfsfs); | ||
| 1423 | remove_proc_entry("servers", nn->proc_nfsfs); | ||
| 1424 | remove_proc_entry("fs/nfsfs", NULL); | ||
| 1425 | } | ||
| 1426 | |||
| 1410 | /* | 1427 | /* |
| 1411 | * initialise the /proc/fs/nfsfs/ directory | 1428 | * initialise the /proc/fs/nfsfs/ directory |
| 1412 | */ | 1429 | */ |
| @@ -1419,14 +1436,12 @@ int __init nfs_fs_proc_init(void) | |||
| 1419 | goto error_0; | 1436 | goto error_0; |
| 1420 | 1437 | ||
| 1421 | /* a file of servers with which we're dealing */ | 1438 | /* a file of servers with which we're dealing */ |
| 1422 | p = proc_create("servers", S_IFREG|S_IRUGO, | 1439 | p = proc_symlink("servers", proc_fs_nfs, "../../net/nfsfs/servers"); |
| 1423 | proc_fs_nfs, &nfs_server_list_fops); | ||
| 1424 | if (!p) | 1440 | if (!p) |
| 1425 | goto error_1; | 1441 | goto error_1; |
| 1426 | 1442 | ||
| 1427 | /* a file of volumes that we have mounted */ | 1443 | /* a file of volumes that we have mounted */ |
| 1428 | p = proc_create("volumes", S_IFREG|S_IRUGO, | 1444 | p = proc_symlink("volumes", proc_fs_nfs, "../../net/nfsfs/volumes"); |
| 1429 | proc_fs_nfs, &nfs_volume_list_fops); | ||
| 1430 | if (!p) | 1445 | if (!p) |
| 1431 | goto error_2; | 1446 | goto error_2; |
| 1432 | return 0; | 1447 | return 0; |
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index abd37a380535..68921b01b792 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
| @@ -1840,11 +1840,12 @@ EXPORT_SYMBOL_GPL(nfs_net_id); | |||
| 1840 | static int nfs_net_init(struct net *net) | 1840 | static int nfs_net_init(struct net *net) |
| 1841 | { | 1841 | { |
| 1842 | nfs_clients_init(net); | 1842 | nfs_clients_init(net); |
| 1843 | return 0; | 1843 | return nfs_fs_proc_net_init(net); |
| 1844 | } | 1844 | } |
| 1845 | 1845 | ||
| 1846 | static void nfs_net_exit(struct net *net) | 1846 | static void nfs_net_exit(struct net *net) |
| 1847 | { | 1847 | { |
| 1848 | nfs_fs_proc_net_exit(net); | ||
| 1848 | nfs_cleanup_cb_ident_idr(net); | 1849 | nfs_cleanup_cb_ident_idr(net); |
| 1849 | } | 1850 | } |
| 1850 | 1851 | ||
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 617f36611d4a..e2a45ae5014e 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h | |||
| @@ -195,7 +195,16 @@ extern struct rpc_clnt *nfs4_find_or_create_ds_client(struct nfs_client *, | |||
| 195 | #ifdef CONFIG_PROC_FS | 195 | #ifdef CONFIG_PROC_FS |
| 196 | extern int __init nfs_fs_proc_init(void); | 196 | extern int __init nfs_fs_proc_init(void); |
| 197 | extern void nfs_fs_proc_exit(void); | 197 | extern void nfs_fs_proc_exit(void); |
| 198 | extern int nfs_fs_proc_net_init(struct net *net); | ||
| 199 | extern void nfs_fs_proc_net_exit(struct net *net); | ||
| 198 | #else | 200 | #else |
| 201 | static inline int nfs_fs_proc_net_init(struct net *net) | ||
| 202 | { | ||
| 203 | return 0; | ||
| 204 | } | ||
| 205 | static inline void nfs_fs_proc_net_exit(struct net *net) | ||
| 206 | { | ||
| 207 | } | ||
| 199 | static inline int nfs_fs_proc_init(void) | 208 | static inline int nfs_fs_proc_init(void) |
| 200 | { | 209 | { |
| 201 | return 0; | 210 | return 0; |
diff --git a/fs/nfs/netns.h b/fs/nfs/netns.h index 8ee1fab83268..ef221fb8a183 100644 --- a/fs/nfs/netns.h +++ b/fs/nfs/netns.h | |||
| @@ -29,6 +29,9 @@ struct nfs_net { | |||
| 29 | #endif | 29 | #endif |
| 30 | spinlock_t nfs_client_lock; | 30 | spinlock_t nfs_client_lock; |
| 31 | struct timespec boot_time; | 31 | struct timespec boot_time; |
| 32 | #ifdef CONFIG_PROC_FS | ||
| 33 | struct proc_dir_entry *proc_nfsfs; | ||
| 34 | #endif | ||
| 32 | }; | 35 | }; |
| 33 | 36 | ||
| 34 | extern int nfs_net_id; | 37 | extern int nfs_net_id; |
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 | } | ||
diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c index 1a81373947f3..73ca1740d839 100644 --- a/fs/proc_namespace.c +++ b/fs/proc_namespace.c | |||
| @@ -232,17 +232,15 @@ static int mounts_open_common(struct inode *inode, struct file *file, | |||
| 232 | if (!task) | 232 | if (!task) |
| 233 | goto err; | 233 | goto err; |
| 234 | 234 | ||
| 235 | rcu_read_lock(); | 235 | task_lock(task); |
| 236 | nsp = task_nsproxy(task); | 236 | nsp = task->nsproxy; |
| 237 | if (!nsp || !nsp->mnt_ns) { | 237 | if (!nsp || !nsp->mnt_ns) { |
| 238 | rcu_read_unlock(); | 238 | task_unlock(task); |
| 239 | put_task_struct(task); | 239 | put_task_struct(task); |
| 240 | goto err; | 240 | goto err; |
| 241 | } | 241 | } |
| 242 | ns = nsp->mnt_ns; | 242 | ns = nsp->mnt_ns; |
| 243 | get_mnt_ns(ns); | 243 | get_mnt_ns(ns); |
| 244 | rcu_read_unlock(); | ||
| 245 | task_lock(task); | ||
| 246 | if (!task->fs) { | 244 | if (!task->fs) { |
| 247 | task_unlock(task); | 245 | task_unlock(task); |
| 248 | put_task_struct(task); | 246 | put_task_struct(task); |
diff --git a/include/linux/mount.h b/include/linux/mount.h index 839bac270904..b0c1e6574e7f 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h | |||
| @@ -42,13 +42,20 @@ struct mnt_namespace; | |||
| 42 | * flag, consider how it interacts with shared mounts. | 42 | * flag, consider how it interacts with shared mounts. |
| 43 | */ | 43 | */ |
| 44 | #define MNT_SHARED_MASK (MNT_UNBINDABLE) | 44 | #define MNT_SHARED_MASK (MNT_UNBINDABLE) |
| 45 | #define MNT_PROPAGATION_MASK (MNT_SHARED | MNT_UNBINDABLE) | 45 | #define MNT_USER_SETTABLE_MASK (MNT_NOSUID | MNT_NODEV | MNT_NOEXEC \ |
| 46 | | MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME \ | ||
| 47 | | MNT_READONLY) | ||
| 48 | #define MNT_ATIME_MASK (MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME ) | ||
| 46 | 49 | ||
| 47 | #define MNT_INTERNAL_FLAGS (MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL | \ | 50 | #define MNT_INTERNAL_FLAGS (MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL | \ |
| 48 | MNT_DOOMED | MNT_SYNC_UMOUNT | MNT_MARKED) | 51 | MNT_DOOMED | MNT_SYNC_UMOUNT | MNT_MARKED) |
| 49 | 52 | ||
| 50 | #define MNT_INTERNAL 0x4000 | 53 | #define MNT_INTERNAL 0x4000 |
| 51 | 54 | ||
| 55 | #define MNT_LOCK_ATIME 0x040000 | ||
| 56 | #define MNT_LOCK_NOEXEC 0x080000 | ||
| 57 | #define MNT_LOCK_NOSUID 0x100000 | ||
| 58 | #define MNT_LOCK_NODEV 0x200000 | ||
| 52 | #define MNT_LOCK_READONLY 0x400000 | 59 | #define MNT_LOCK_READONLY 0x400000 |
| 53 | #define MNT_LOCKED 0x800000 | 60 | #define MNT_LOCKED 0x800000 |
| 54 | #define MNT_DOOMED 0x1000000 | 61 | #define MNT_DOOMED 0x1000000 |
diff --git a/include/linux/nsproxy.h b/include/linux/nsproxy.h index b4ec59d159ac..35fa08fd7739 100644 --- a/include/linux/nsproxy.h +++ b/include/linux/nsproxy.h | |||
| @@ -40,32 +40,28 @@ extern struct nsproxy init_nsproxy; | |||
| 40 | * the namespaces access rules are: | 40 | * the namespaces access rules are: |
| 41 | * | 41 | * |
| 42 | * 1. only current task is allowed to change tsk->nsproxy pointer or | 42 | * 1. only current task is allowed to change tsk->nsproxy pointer or |
| 43 | * any pointer on the nsproxy itself | 43 | * any pointer on the nsproxy itself. Current must hold the task_lock |
| 44 | * when changing tsk->nsproxy. | ||
| 44 | * | 45 | * |
| 45 | * 2. when accessing (i.e. reading) current task's namespaces - no | 46 | * 2. when accessing (i.e. reading) current task's namespaces - no |
| 46 | * precautions should be taken - just dereference the pointers | 47 | * precautions should be taken - just dereference the pointers |
| 47 | * | 48 | * |
| 48 | * 3. the access to other task namespaces is performed like this | 49 | * 3. the access to other task namespaces is performed like this |
| 49 | * rcu_read_lock(); | 50 | * task_lock(task); |
| 50 | * nsproxy = task_nsproxy(tsk); | 51 | * nsproxy = task->nsproxy; |
| 51 | * if (nsproxy != NULL) { | 52 | * if (nsproxy != NULL) { |
| 52 | * / * | 53 | * / * |
| 53 | * * work with the namespaces here | 54 | * * work with the namespaces here |
| 54 | * * e.g. get the reference on one of them | 55 | * * e.g. get the reference on one of them |
| 55 | * * / | 56 | * * / |
| 56 | * } / * | 57 | * } / * |
| 57 | * * NULL task_nsproxy() means that this task is | 58 | * * NULL task->nsproxy means that this task is |
| 58 | * * almost dead (zombie) | 59 | * * almost dead (zombie) |
| 59 | * * / | 60 | * * / |
| 60 | * rcu_read_unlock(); | 61 | * task_unlock(task); |
| 61 | * | 62 | * |
| 62 | */ | 63 | */ |
| 63 | 64 | ||
| 64 | static inline struct nsproxy *task_nsproxy(struct task_struct *tsk) | ||
| 65 | { | ||
| 66 | return rcu_dereference(tsk->nsproxy); | ||
| 67 | } | ||
| 68 | |||
| 69 | int copy_namespaces(unsigned long flags, struct task_struct *tsk); | 65 | int copy_namespaces(unsigned long flags, struct task_struct *tsk); |
| 70 | void exit_task_namespaces(struct task_struct *tsk); | 66 | void exit_task_namespaces(struct task_struct *tsk); |
| 71 | void switch_task_namespaces(struct task_struct *tsk, struct nsproxy *new); | 67 | void switch_task_namespaces(struct task_struct *tsk, struct nsproxy *new); |
diff --git a/include/linux/pid_namespace.h b/include/linux/pid_namespace.h index 7246ef3d4455..1997ffc295a7 100644 --- a/include/linux/pid_namespace.h +++ b/include/linux/pid_namespace.h | |||
| @@ -33,6 +33,7 @@ struct pid_namespace { | |||
| 33 | #ifdef CONFIG_PROC_FS | 33 | #ifdef CONFIG_PROC_FS |
| 34 | struct vfsmount *proc_mnt; | 34 | struct vfsmount *proc_mnt; |
| 35 | struct dentry *proc_self; | 35 | struct dentry *proc_self; |
| 36 | struct dentry *proc_thread_self; | ||
| 36 | #endif | 37 | #endif |
| 37 | #ifdef CONFIG_BSD_PROCESS_ACCT | 38 | #ifdef CONFIG_BSD_PROCESS_ACCT |
| 38 | struct bsd_acct_struct *bacct; | 39 | struct bsd_acct_struct *bacct; |
diff --git a/ipc/namespace.c b/ipc/namespace.c index 59451c1e214d..b54468e48e32 100644 --- a/ipc/namespace.c +++ b/ipc/namespace.c | |||
| @@ -154,11 +154,11 @@ static void *ipcns_get(struct task_struct *task) | |||
| 154 | struct ipc_namespace *ns = NULL; | 154 | struct ipc_namespace *ns = NULL; |
| 155 | struct nsproxy *nsproxy; | 155 | struct nsproxy *nsproxy; |
| 156 | 156 | ||
| 157 | rcu_read_lock(); | 157 | task_lock(task); |
| 158 | nsproxy = task_nsproxy(task); | 158 | nsproxy = task->nsproxy; |
| 159 | if (nsproxy) | 159 | if (nsproxy) |
| 160 | ns = get_ipc_ns(nsproxy->ipc_ns); | 160 | ns = get_ipc_ns(nsproxy->ipc_ns); |
| 161 | rcu_read_unlock(); | 161 | task_unlock(task); |
| 162 | 162 | ||
| 163 | return ns; | 163 | return ns; |
| 164 | } | 164 | } |
diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c index 8e7811086b82..ef42d0ab3115 100644 --- a/kernel/nsproxy.c +++ b/kernel/nsproxy.c | |||
| @@ -204,20 +204,13 @@ void switch_task_namespaces(struct task_struct *p, struct nsproxy *new) | |||
| 204 | 204 | ||
| 205 | might_sleep(); | 205 | might_sleep(); |
| 206 | 206 | ||
| 207 | task_lock(p); | ||
| 207 | ns = p->nsproxy; | 208 | ns = p->nsproxy; |
| 209 | p->nsproxy = new; | ||
| 210 | task_unlock(p); | ||
| 208 | 211 | ||
| 209 | rcu_assign_pointer(p->nsproxy, new); | 212 | if (ns && atomic_dec_and_test(&ns->count)) |
| 210 | |||
| 211 | if (ns && atomic_dec_and_test(&ns->count)) { | ||
| 212 | /* | ||
| 213 | * wait for others to get what they want from this nsproxy. | ||
| 214 | * | ||
| 215 | * cannot release this nsproxy via the call_rcu() since | ||
| 216 | * put_mnt_ns() will want to sleep | ||
| 217 | */ | ||
| 218 | synchronize_rcu(); | ||
| 219 | free_nsproxy(ns); | 213 | free_nsproxy(ns); |
| 220 | } | ||
| 221 | } | 214 | } |
| 222 | 215 | ||
| 223 | void exit_task_namespaces(struct task_struct *p) | 216 | void exit_task_namespaces(struct task_struct *p) |
diff --git a/kernel/utsname.c b/kernel/utsname.c index fd393124e507..883aaaa7de8a 100644 --- a/kernel/utsname.c +++ b/kernel/utsname.c | |||
| @@ -93,13 +93,13 @@ static void *utsns_get(struct task_struct *task) | |||
| 93 | struct uts_namespace *ns = NULL; | 93 | struct uts_namespace *ns = NULL; |
| 94 | struct nsproxy *nsproxy; | 94 | struct nsproxy *nsproxy; |
| 95 | 95 | ||
| 96 | rcu_read_lock(); | 96 | task_lock(task); |
| 97 | nsproxy = task_nsproxy(task); | 97 | nsproxy = task->nsproxy; |
| 98 | if (nsproxy) { | 98 | if (nsproxy) { |
| 99 | ns = nsproxy->uts_ns; | 99 | ns = nsproxy->uts_ns; |
| 100 | get_uts_ns(ns); | 100 | get_uts_ns(ns); |
| 101 | } | 101 | } |
| 102 | rcu_read_unlock(); | 102 | task_unlock(task); |
| 103 | 103 | ||
| 104 | return ns; | 104 | return ns; |
| 105 | } | 105 | } |
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 85b62691f4f2..7c6b51a58968 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c | |||
| @@ -373,9 +373,11 @@ struct net *get_net_ns_by_pid(pid_t pid) | |||
| 373 | tsk = find_task_by_vpid(pid); | 373 | tsk = find_task_by_vpid(pid); |
| 374 | if (tsk) { | 374 | if (tsk) { |
| 375 | struct nsproxy *nsproxy; | 375 | struct nsproxy *nsproxy; |
| 376 | nsproxy = task_nsproxy(tsk); | 376 | task_lock(tsk); |
| 377 | nsproxy = tsk->nsproxy; | ||
| 377 | if (nsproxy) | 378 | if (nsproxy) |
| 378 | net = get_net(nsproxy->net_ns); | 379 | net = get_net(nsproxy->net_ns); |
| 380 | task_unlock(tsk); | ||
| 379 | } | 381 | } |
| 380 | rcu_read_unlock(); | 382 | rcu_read_unlock(); |
| 381 | return net; | 383 | return net; |
| @@ -632,11 +634,11 @@ static void *netns_get(struct task_struct *task) | |||
| 632 | struct net *net = NULL; | 634 | struct net *net = NULL; |
| 633 | struct nsproxy *nsproxy; | 635 | struct nsproxy *nsproxy; |
| 634 | 636 | ||
| 635 | rcu_read_lock(); | 637 | task_lock(task); |
| 636 | nsproxy = task_nsproxy(task); | 638 | nsproxy = task->nsproxy; |
| 637 | if (nsproxy) | 639 | if (nsproxy) |
| 638 | net = get_net(nsproxy->net_ns); | 640 | net = get_net(nsproxy->net_ns); |
| 639 | rcu_read_unlock(); | 641 | task_unlock(task); |
| 640 | 642 | ||
| 641 | return net; | 643 | return net; |
| 642 | } | 644 | } |
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 6fd2a4402069..36ff2e4c7b6f 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile | |||
| @@ -5,6 +5,7 @@ TARGETS += kcmp | |||
| 5 | TARGETS += memfd | 5 | TARGETS += memfd |
| 6 | TARGETS += memory-hotplug | 6 | TARGETS += memory-hotplug |
| 7 | TARGETS += mqueue | 7 | TARGETS += mqueue |
| 8 | TARGETS += mount | ||
| 8 | TARGETS += net | 9 | TARGETS += net |
| 9 | TARGETS += ptrace | 10 | TARGETS += ptrace |
| 10 | TARGETS += timers | 11 | TARGETS += timers |
diff --git a/tools/testing/selftests/mount/Makefile b/tools/testing/selftests/mount/Makefile new file mode 100644 index 000000000000..337d853c2b72 --- /dev/null +++ b/tools/testing/selftests/mount/Makefile | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # Makefile for mount selftests. | ||
| 2 | |||
| 3 | all: unprivileged-remount-test | ||
| 4 | |||
| 5 | unprivileged-remount-test: unprivileged-remount-test.c | ||
| 6 | gcc -Wall -O2 unprivileged-remount-test.c -o unprivileged-remount-test | ||
| 7 | |||
| 8 | # Allow specific tests to be selected. | ||
| 9 | test_unprivileged_remount: unprivileged-remount-test | ||
| 10 | @if [ -f /proc/self/uid_map ] ; then ./unprivileged-remount-test ; fi | ||
| 11 | |||
| 12 | run_tests: all test_unprivileged_remount | ||
| 13 | |||
| 14 | clean: | ||
| 15 | rm -f unprivileged-remount-test | ||
| 16 | |||
| 17 | .PHONY: all test_unprivileged_remount | ||
diff --git a/tools/testing/selftests/mount/unprivileged-remount-test.c b/tools/testing/selftests/mount/unprivileged-remount-test.c new file mode 100644 index 000000000000..1b3ff2fda4d0 --- /dev/null +++ b/tools/testing/selftests/mount/unprivileged-remount-test.c | |||
| @@ -0,0 +1,242 @@ | |||
| 1 | #define _GNU_SOURCE | ||
| 2 | #include <sched.h> | ||
| 3 | #include <stdio.h> | ||
| 4 | #include <errno.h> | ||
| 5 | #include <string.h> | ||
| 6 | #include <sys/types.h> | ||
| 7 | #include <sys/mount.h> | ||
| 8 | #include <sys/wait.h> | ||
| 9 | #include <stdlib.h> | ||
| 10 | #include <unistd.h> | ||
| 11 | #include <fcntl.h> | ||
| 12 | #include <grp.h> | ||
| 13 | #include <stdbool.h> | ||
| 14 | #include <stdarg.h> | ||
| 15 | |||
| 16 | #ifndef CLONE_NEWNS | ||
| 17 | # define CLONE_NEWNS 0x00020000 | ||
| 18 | #endif | ||
| 19 | #ifndef CLONE_NEWUTS | ||
| 20 | # define CLONE_NEWUTS 0x04000000 | ||
| 21 | #endif | ||
| 22 | #ifndef CLONE_NEWIPC | ||
| 23 | # define CLONE_NEWIPC 0x08000000 | ||
| 24 | #endif | ||
| 25 | #ifndef CLONE_NEWNET | ||
| 26 | # define CLONE_NEWNET 0x40000000 | ||
| 27 | #endif | ||
| 28 | #ifndef CLONE_NEWUSER | ||
| 29 | # define CLONE_NEWUSER 0x10000000 | ||
| 30 | #endif | ||
| 31 | #ifndef CLONE_NEWPID | ||
| 32 | # define CLONE_NEWPID 0x20000000 | ||
| 33 | #endif | ||
| 34 | |||
| 35 | #ifndef MS_RELATIME | ||
| 36 | #define MS_RELATIME (1 << 21) | ||
| 37 | #endif | ||
| 38 | #ifndef MS_STRICTATIME | ||
| 39 | #define MS_STRICTATIME (1 << 24) | ||
| 40 | #endif | ||
| 41 | |||
| 42 | static void die(char *fmt, ...) | ||
| 43 | { | ||
| 44 | va_list ap; | ||
| 45 | va_start(ap, fmt); | ||
| 46 | vfprintf(stderr, fmt, ap); | ||
| 47 | va_end(ap); | ||
| 48 | exit(EXIT_FAILURE); | ||
| 49 | } | ||
| 50 | |||
| 51 | static void write_file(char *filename, char *fmt, ...) | ||
| 52 | { | ||
| 53 | char buf[4096]; | ||
| 54 | int fd; | ||
| 55 | ssize_t written; | ||
| 56 | int buf_len; | ||
| 57 | va_list ap; | ||
| 58 | |||
| 59 | va_start(ap, fmt); | ||
| 60 | buf_len = vsnprintf(buf, sizeof(buf), fmt, ap); | ||
| 61 | va_end(ap); | ||
| 62 | if (buf_len < 0) { | ||
| 63 | die("vsnprintf failed: %s\n", | ||
| 64 | strerror(errno)); | ||
| 65 | } | ||
| 66 | if (buf_len >= sizeof(buf)) { | ||
| 67 | die("vsnprintf output truncated\n"); | ||
| 68 | } | ||
| 69 | |||
| 70 | fd = open(filename, O_WRONLY); | ||
| 71 | if (fd < 0) { | ||
| 72 | die("open of %s failed: %s\n", | ||
| 73 | filename, strerror(errno)); | ||
| 74 | } | ||
| 75 | written = write(fd, buf, buf_len); | ||
| 76 | if (written != buf_len) { | ||
| 77 | if (written >= 0) { | ||
| 78 | die("short write to %s\n", filename); | ||
| 79 | } else { | ||
| 80 | die("write to %s failed: %s\n", | ||
| 81 | filename, strerror(errno)); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | if (close(fd) != 0) { | ||
| 85 | die("close of %s failed: %s\n", | ||
| 86 | filename, strerror(errno)); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | static void create_and_enter_userns(void) | ||
| 91 | { | ||
| 92 | uid_t uid; | ||
| 93 | gid_t gid; | ||
| 94 | |||
| 95 | uid = getuid(); | ||
| 96 | gid = getgid(); | ||
| 97 | |||
| 98 | if (unshare(CLONE_NEWUSER) !=0) { | ||
| 99 | die("unshare(CLONE_NEWUSER) failed: %s\n", | ||
| 100 | strerror(errno)); | ||
| 101 | } | ||
| 102 | |||
| 103 | write_file("/proc/self/uid_map", "0 %d 1", uid); | ||
| 104 | write_file("/proc/self/gid_map", "0 %d 1", gid); | ||
| 105 | |||
| 106 | if (setgroups(0, NULL) != 0) { | ||
| 107 | die("setgroups failed: %s\n", | ||
| 108 | strerror(errno)); | ||
| 109 | } | ||
| 110 | if (setgid(0) != 0) { | ||
| 111 | die ("setgid(0) failed %s\n", | ||
| 112 | strerror(errno)); | ||
| 113 | } | ||
| 114 | if (setuid(0) != 0) { | ||
| 115 | die("setuid(0) failed %s\n", | ||
| 116 | strerror(errno)); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | static | ||
| 121 | bool test_unpriv_remount(int mount_flags, int remount_flags, int invalid_flags) | ||
| 122 | { | ||
| 123 | pid_t child; | ||
| 124 | |||
| 125 | child = fork(); | ||
| 126 | if (child == -1) { | ||
| 127 | die("fork failed: %s\n", | ||
| 128 | strerror(errno)); | ||
| 129 | } | ||
| 130 | if (child != 0) { /* parent */ | ||
| 131 | pid_t pid; | ||
| 132 | int status; | ||
| 133 | pid = waitpid(child, &status, 0); | ||
| 134 | if (pid == -1) { | ||
| 135 | die("waitpid failed: %s\n", | ||
| 136 | strerror(errno)); | ||
| 137 | } | ||
| 138 | if (pid != child) { | ||
| 139 | die("waited for %d got %d\n", | ||
| 140 | child, pid); | ||
| 141 | } | ||
| 142 | if (!WIFEXITED(status)) { | ||
| 143 | die("child did not terminate cleanly\n"); | ||
| 144 | } | ||
| 145 | return WEXITSTATUS(status) == EXIT_SUCCESS ? true : false; | ||
| 146 | } | ||
| 147 | |||
| 148 | create_and_enter_userns(); | ||
| 149 | if (unshare(CLONE_NEWNS) != 0) { | ||
| 150 | die("unshare(CLONE_NEWNS) failed: %s\n", | ||
| 151 | strerror(errno)); | ||
| 152 | } | ||
| 153 | |||
| 154 | if (mount("testing", "/tmp", "ramfs", mount_flags, NULL) != 0) { | ||
| 155 | die("mount of /tmp failed: %s\n", | ||
| 156 | strerror(errno)); | ||
| 157 | } | ||
| 158 | |||
| 159 | create_and_enter_userns(); | ||
| 160 | |||
| 161 | if (unshare(CLONE_NEWNS) != 0) { | ||
| 162 | die("unshare(CLONE_NEWNS) failed: %s\n", | ||
| 163 | strerror(errno)); | ||
| 164 | } | ||
| 165 | |||
| 166 | if (mount("/tmp", "/tmp", "none", | ||
| 167 | MS_REMOUNT | MS_BIND | remount_flags, NULL) != 0) { | ||
| 168 | /* system("cat /proc/self/mounts"); */ | ||
| 169 | die("remount of /tmp failed: %s\n", | ||
| 170 | strerror(errno)); | ||
| 171 | } | ||
| 172 | |||
| 173 | if (mount("/tmp", "/tmp", "none", | ||
| 174 | MS_REMOUNT | MS_BIND | invalid_flags, NULL) == 0) { | ||
| 175 | /* system("cat /proc/self/mounts"); */ | ||
| 176 | die("remount of /tmp with invalid flags " | ||
| 177 | "succeeded unexpectedly\n"); | ||
| 178 | } | ||
| 179 | exit(EXIT_SUCCESS); | ||
| 180 | } | ||
| 181 | |||
| 182 | static bool test_unpriv_remount_simple(int mount_flags) | ||
| 183 | { | ||
| 184 | return test_unpriv_remount(mount_flags, mount_flags, 0); | ||
| 185 | } | ||
| 186 | |||
| 187 | static bool test_unpriv_remount_atime(int mount_flags, int invalid_flags) | ||
| 188 | { | ||
| 189 | return test_unpriv_remount(mount_flags, mount_flags, invalid_flags); | ||
| 190 | } | ||
| 191 | |||
| 192 | int main(int argc, char **argv) | ||
| 193 | { | ||
| 194 | if (!test_unpriv_remount_simple(MS_RDONLY|MS_NODEV)) { | ||
| 195 | die("MS_RDONLY malfunctions\n"); | ||
| 196 | } | ||
| 197 | if (!test_unpriv_remount_simple(MS_NODEV)) { | ||
| 198 | die("MS_NODEV malfunctions\n"); | ||
| 199 | } | ||
| 200 | if (!test_unpriv_remount_simple(MS_NOSUID|MS_NODEV)) { | ||
| 201 | die("MS_NOSUID malfunctions\n"); | ||
| 202 | } | ||
| 203 | if (!test_unpriv_remount_simple(MS_NOEXEC|MS_NODEV)) { | ||
| 204 | die("MS_NOEXEC malfunctions\n"); | ||
| 205 | } | ||
| 206 | if (!test_unpriv_remount_atime(MS_RELATIME|MS_NODEV, | ||
| 207 | MS_NOATIME|MS_NODEV)) | ||
| 208 | { | ||
| 209 | die("MS_RELATIME malfunctions\n"); | ||
| 210 | } | ||
| 211 | if (!test_unpriv_remount_atime(MS_STRICTATIME|MS_NODEV, | ||
| 212 | MS_NOATIME|MS_NODEV)) | ||
| 213 | { | ||
| 214 | die("MS_STRICTATIME malfunctions\n"); | ||
| 215 | } | ||
| 216 | if (!test_unpriv_remount_atime(MS_NOATIME|MS_NODEV, | ||
| 217 | MS_STRICTATIME|MS_NODEV)) | ||
| 218 | { | ||
| 219 | die("MS_RELATIME malfunctions\n"); | ||
| 220 | } | ||
| 221 | if (!test_unpriv_remount_atime(MS_RELATIME|MS_NODIRATIME|MS_NODEV, | ||
| 222 | MS_NOATIME|MS_NODEV)) | ||
| 223 | { | ||
| 224 | die("MS_RELATIME malfunctions\n"); | ||
| 225 | } | ||
| 226 | if (!test_unpriv_remount_atime(MS_STRICTATIME|MS_NODIRATIME|MS_NODEV, | ||
| 227 | MS_NOATIME|MS_NODEV)) | ||
| 228 | { | ||
| 229 | die("MS_RELATIME malfunctions\n"); | ||
| 230 | } | ||
| 231 | if (!test_unpriv_remount_atime(MS_NOATIME|MS_NODIRATIME|MS_NODEV, | ||
| 232 | MS_STRICTATIME|MS_NODEV)) | ||
| 233 | { | ||
| 234 | die("MS_RELATIME malfunctions\n"); | ||
| 235 | } | ||
| 236 | if (!test_unpriv_remount(MS_STRICTATIME|MS_NODEV, MS_NODEV, | ||
| 237 | MS_NOATIME|MS_NODEV)) | ||
| 238 | { | ||
| 239 | die("Default atime malfunctions\n"); | ||
| 240 | } | ||
| 241 | return EXIT_SUCCESS; | ||
| 242 | } | ||
