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 | } | ||