aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-12-07 11:14:42 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2011-12-07 11:14:42 -0500
commit3172f8fe1ca1d432b196efad453c0ceb89302075 (patch)
tree0b9e8af7ea9ee9883ff1a834357694254200335a
parentb835c0f47f725d864bf2545f10c733b754bb6d51 (diff)
parent02125a826459a6ad142f8d91c5b6357562f96615 (diff)
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: fix apparmor dereferencing potentially freed dentry, sanitize __d_path() API
-rw-r--r--fs/dcache.c71
-rw-r--r--fs/namespace.c20
-rw-r--r--fs/seq_file.c6
-rw-r--r--include/linux/dcache.h3
-rw-r--r--include/linux/fs.h1
-rw-r--r--security/apparmor/path.c65
-rw-r--r--security/tomoyo/realpath.c3
7 files changed, 100 insertions, 69 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index 10ba92def3f6..89509b5a090e 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -2439,16 +2439,14 @@ static int prepend_name(char **buffer, int *buflen, struct qstr *name)
2439/** 2439/**
2440 * prepend_path - Prepend path string to a buffer 2440 * prepend_path - Prepend path string to a buffer
2441 * @path: the dentry/vfsmount to report 2441 * @path: the dentry/vfsmount to report
2442 * @root: root vfsmnt/dentry (may be modified by this function) 2442 * @root: root vfsmnt/dentry
2443 * @buffer: pointer to the end of the buffer 2443 * @buffer: pointer to the end of the buffer
2444 * @buflen: pointer to buffer length 2444 * @buflen: pointer to buffer length
2445 * 2445 *
2446 * Caller holds the rename_lock. 2446 * Caller holds the rename_lock.
2447 *
2448 * If path is not reachable from the supplied root, then the value of
2449 * root is changed (without modifying refcounts).
2450 */ 2447 */
2451static int prepend_path(const struct path *path, struct path *root, 2448static int prepend_path(const struct path *path,
2449 const struct path *root,
2452 char **buffer, int *buflen) 2450 char **buffer, int *buflen)
2453{ 2451{
2454 struct dentry *dentry = path->dentry; 2452 struct dentry *dentry = path->dentry;
@@ -2483,10 +2481,10 @@ static int prepend_path(const struct path *path, struct path *root,
2483 dentry = parent; 2481 dentry = parent;
2484 } 2482 }
2485 2483
2486out:
2487 if (!error && !slash) 2484 if (!error && !slash)
2488 error = prepend(buffer, buflen, "/", 1); 2485 error = prepend(buffer, buflen, "/", 1);
2489 2486
2487out:
2490 br_read_unlock(vfsmount_lock); 2488 br_read_unlock(vfsmount_lock);
2491 return error; 2489 return error;
2492 2490
@@ -2500,15 +2498,17 @@ global_root:
2500 WARN(1, "Root dentry has weird name <%.*s>\n", 2498 WARN(1, "Root dentry has weird name <%.*s>\n",
2501 (int) dentry->d_name.len, dentry->d_name.name); 2499 (int) dentry->d_name.len, dentry->d_name.name);
2502 } 2500 }
2503 root->mnt = vfsmnt; 2501 if (!slash)
2504 root->dentry = dentry; 2502 error = prepend(buffer, buflen, "/", 1);
2503 if (!error)
2504 error = vfsmnt->mnt_ns ? 1 : 2;
2505 goto out; 2505 goto out;
2506} 2506}
2507 2507
2508/** 2508/**
2509 * __d_path - return the path of a dentry 2509 * __d_path - return the path of a dentry
2510 * @path: the dentry/vfsmount to report 2510 * @path: the dentry/vfsmount to report
2511 * @root: root vfsmnt/dentry (may be modified by this function) 2511 * @root: root vfsmnt/dentry
2512 * @buf: buffer to return value in 2512 * @buf: buffer to return value in
2513 * @buflen: buffer length 2513 * @buflen: buffer length
2514 * 2514 *
@@ -2519,10 +2519,10 @@ global_root:
2519 * 2519 *
2520 * "buflen" should be positive. 2520 * "buflen" should be positive.
2521 * 2521 *
2522 * If path is not reachable from the supplied root, then the value of 2522 * If the path is not reachable from the supplied root, return %NULL.
2523 * root is changed (without modifying refcounts).
2524 */ 2523 */
2525char *__d_path(const struct path *path, struct path *root, 2524char *__d_path(const struct path *path,
2525 const struct path *root,
2526 char *buf, int buflen) 2526 char *buf, int buflen)
2527{ 2527{
2528 char *res = buf + buflen; 2528 char *res = buf + buflen;
@@ -2533,7 +2533,28 @@ char *__d_path(const struct path *path, struct path *root,
2533 error = prepend_path(path, root, &res, &buflen); 2533 error = prepend_path(path, root, &res, &buflen);
2534 write_sequnlock(&rename_lock); 2534 write_sequnlock(&rename_lock);
2535 2535
2536 if (error) 2536 if (error < 0)
2537 return ERR_PTR(error);
2538 if (error > 0)
2539 return NULL;
2540 return res;
2541}
2542
2543char *d_absolute_path(const struct path *path,
2544 char *buf, int buflen)
2545{
2546 struct path root = {};
2547 char *res = buf + buflen;
2548 int error;
2549
2550 prepend(&res, &buflen, "\0", 1);
2551 write_seqlock(&rename_lock);
2552 error = prepend_path(path, &root, &res, &buflen);
2553 write_sequnlock(&rename_lock);
2554
2555 if (error > 1)
2556 error = -EINVAL;
2557 if (error < 0)
2537 return ERR_PTR(error); 2558 return ERR_PTR(error);
2538 return res; 2559 return res;
2539} 2560}
@@ -2541,8 +2562,9 @@ char *__d_path(const struct path *path, struct path *root,
2541/* 2562/*
2542 * same as __d_path but appends "(deleted)" for unlinked files. 2563 * same as __d_path but appends "(deleted)" for unlinked files.
2543 */ 2564 */
2544static int path_with_deleted(const struct path *path, struct path *root, 2565static int path_with_deleted(const struct path *path,
2545 char **buf, int *buflen) 2566 const struct path *root,
2567 char **buf, int *buflen)
2546{ 2568{
2547 prepend(buf, buflen, "\0", 1); 2569 prepend(buf, buflen, "\0", 1);
2548 if (d_unlinked(path->dentry)) { 2570 if (d_unlinked(path->dentry)) {
@@ -2579,7 +2601,6 @@ char *d_path(const struct path *path, char *buf, int buflen)
2579{ 2601{
2580 char *res = buf + buflen; 2602 char *res = buf + buflen;
2581 struct path root; 2603 struct path root;
2582 struct path tmp;
2583 int error; 2604 int error;
2584 2605
2585 /* 2606 /*
@@ -2594,9 +2615,8 @@ char *d_path(const struct path *path, char *buf, int buflen)
2594 2615
2595 get_fs_root(current->fs, &root); 2616 get_fs_root(current->fs, &root);
2596 write_seqlock(&rename_lock); 2617 write_seqlock(&rename_lock);
2597 tmp = root; 2618 error = path_with_deleted(path, &root, &res, &buflen);
2598 error = path_with_deleted(path, &tmp, &res, &buflen); 2619 if (error < 0)
2599 if (error)
2600 res = ERR_PTR(error); 2620 res = ERR_PTR(error);
2601 write_sequnlock(&rename_lock); 2621 write_sequnlock(&rename_lock);
2602 path_put(&root); 2622 path_put(&root);
@@ -2617,7 +2637,6 @@ char *d_path_with_unreachable(const struct path *path, char *buf, int buflen)
2617{ 2637{
2618 char *res = buf + buflen; 2638 char *res = buf + buflen;
2619 struct path root; 2639 struct path root;
2620 struct path tmp;
2621 int error; 2640 int error;
2622 2641
2623 if (path->dentry->d_op && path->dentry->d_op->d_dname) 2642 if (path->dentry->d_op && path->dentry->d_op->d_dname)
@@ -2625,9 +2644,8 @@ char *d_path_with_unreachable(const struct path *path, char *buf, int buflen)
2625 2644
2626 get_fs_root(current->fs, &root); 2645 get_fs_root(current->fs, &root);
2627 write_seqlock(&rename_lock); 2646 write_seqlock(&rename_lock);
2628 tmp = root; 2647 error = path_with_deleted(path, &root, &res, &buflen);
2629 error = path_with_deleted(path, &tmp, &res, &buflen); 2648 if (error > 0)
2630 if (!error && !path_equal(&tmp, &root))
2631 error = prepend_unreachable(&res, &buflen); 2649 error = prepend_unreachable(&res, &buflen);
2632 write_sequnlock(&rename_lock); 2650 write_sequnlock(&rename_lock);
2633 path_put(&root); 2651 path_put(&root);
@@ -2758,19 +2776,18 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
2758 write_seqlock(&rename_lock); 2776 write_seqlock(&rename_lock);
2759 if (!d_unlinked(pwd.dentry)) { 2777 if (!d_unlinked(pwd.dentry)) {
2760 unsigned long len; 2778 unsigned long len;
2761 struct path tmp = root;
2762 char *cwd = page + PAGE_SIZE; 2779 char *cwd = page + PAGE_SIZE;
2763 int buflen = PAGE_SIZE; 2780 int buflen = PAGE_SIZE;
2764 2781
2765 prepend(&cwd, &buflen, "\0", 1); 2782 prepend(&cwd, &buflen, "\0", 1);
2766 error = prepend_path(&pwd, &tmp, &cwd, &buflen); 2783 error = prepend_path(&pwd, &root, &cwd, &buflen);
2767 write_sequnlock(&rename_lock); 2784 write_sequnlock(&rename_lock);
2768 2785
2769 if (error) 2786 if (error < 0)
2770 goto out; 2787 goto out;
2771 2788
2772 /* Unreachable from current root */ 2789 /* Unreachable from current root */
2773 if (!path_equal(&tmp, &root)) { 2790 if (error > 0) {
2774 error = prepend_unreachable(&cwd, &buflen); 2791 error = prepend_unreachable(&cwd, &buflen);
2775 if (error) 2792 if (error)
2776 goto out; 2793 goto out;
diff --git a/fs/namespace.c b/fs/namespace.c
index 6d3a1963879b..cfc6d4448aa5 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1048,15 +1048,12 @@ static int show_mountinfo(struct seq_file *m, void *v)
1048 if (err) 1048 if (err)
1049 goto out; 1049 goto out;
1050 seq_putc(m, ' '); 1050 seq_putc(m, ' ');
1051 seq_path_root(m, &mnt_path, &root, " \t\n\\"); 1051
1052 if (root.mnt != p->root.mnt || root.dentry != p->root.dentry) { 1052 /* mountpoints outside of chroot jail will give SEQ_SKIP on this */
1053 /* 1053 err = seq_path_root(m, &mnt_path, &root, " \t\n\\");
1054 * Mountpoint is outside root, discard that one. Ugly, 1054 if (err)
1055 * but less so than trying to do that in iterator in a 1055 goto out;
1056 * race-free way (due to renames). 1056
1057 */
1058 return SEQ_SKIP;
1059 }
1060 seq_puts(m, mnt->mnt_flags & MNT_READONLY ? " ro" : " rw"); 1057 seq_puts(m, mnt->mnt_flags & MNT_READONLY ? " ro" : " rw");
1061 show_mnt_opts(m, mnt); 1058 show_mnt_opts(m, mnt);
1062 1059
@@ -2776,3 +2773,8 @@ void kern_unmount(struct vfsmount *mnt)
2776 } 2773 }
2777} 2774}
2778EXPORT_SYMBOL(kern_unmount); 2775EXPORT_SYMBOL(kern_unmount);
2776
2777bool our_mnt(struct vfsmount *mnt)
2778{
2779 return check_mnt(mnt);
2780}
diff --git a/fs/seq_file.c b/fs/seq_file.c
index 05d6b0e78c95..dba43c3ea3af 100644
--- a/fs/seq_file.c
+++ b/fs/seq_file.c
@@ -449,8 +449,6 @@ EXPORT_SYMBOL(seq_path);
449 449
450/* 450/*
451 * Same as seq_path, but relative to supplied root. 451 * Same as seq_path, but relative to supplied root.
452 *
453 * root may be changed, see __d_path().
454 */ 452 */
455int seq_path_root(struct seq_file *m, struct path *path, struct path *root, 453int seq_path_root(struct seq_file *m, struct path *path, struct path *root,
456 char *esc) 454 char *esc)
@@ -463,6 +461,8 @@ int seq_path_root(struct seq_file *m, struct path *path, struct path *root,
463 char *p; 461 char *p;
464 462
465 p = __d_path(path, root, buf, size); 463 p = __d_path(path, root, buf, size);
464 if (!p)
465 return SEQ_SKIP;
466 res = PTR_ERR(p); 466 res = PTR_ERR(p);
467 if (!IS_ERR(p)) { 467 if (!IS_ERR(p)) {
468 char *end = mangle_path(buf, p, esc); 468 char *end = mangle_path(buf, p, esc);
@@ -474,7 +474,7 @@ int seq_path_root(struct seq_file *m, struct path *path, struct path *root,
474 } 474 }
475 seq_commit(m, res); 475 seq_commit(m, res);
476 476
477 return res < 0 ? res : 0; 477 return res < 0 && res != -ENAMETOOLONG ? res : 0;
478} 478}
479 479
480/* 480/*
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 4df926199369..ed9f74f6c519 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -339,7 +339,8 @@ extern int d_validate(struct dentry *, struct dentry *);
339 */ 339 */
340extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...); 340extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...);
341 341
342extern char *__d_path(const struct path *path, struct path *root, char *, int); 342extern char *__d_path(const struct path *, const struct path *, char *, int);
343extern char *d_absolute_path(const struct path *, char *, int);
343extern char *d_path(const struct path *, char *, int); 344extern char *d_path(const struct path *, char *, int);
344extern char *d_path_with_unreachable(const struct path *, char *, int); 345extern char *d_path_with_unreachable(const struct path *, char *, int);
345extern char *dentry_path_raw(struct dentry *, char *, int); 346extern char *dentry_path_raw(struct dentry *, char *, int);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index e3130220ce3e..019dc558df1a 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1942,6 +1942,7 @@ extern int fd_statfs(int, struct kstatfs *);
1942extern int statfs_by_dentry(struct dentry *, struct kstatfs *); 1942extern int statfs_by_dentry(struct dentry *, struct kstatfs *);
1943extern int freeze_super(struct super_block *super); 1943extern int freeze_super(struct super_block *super);
1944extern int thaw_super(struct super_block *super); 1944extern int thaw_super(struct super_block *super);
1945extern bool our_mnt(struct vfsmount *mnt);
1945 1946
1946extern int current_umask(void); 1947extern int current_umask(void);
1947 1948
diff --git a/security/apparmor/path.c b/security/apparmor/path.c
index 36cc0cc39e78..b566eba4a65c 100644
--- a/security/apparmor/path.c
+++ b/security/apparmor/path.c
@@ -57,23 +57,44 @@ static int prepend(char **buffer, int buflen, const char *str, int namelen)
57static int d_namespace_path(struct path *path, char *buf, int buflen, 57static int d_namespace_path(struct path *path, char *buf, int buflen,
58 char **name, int flags) 58 char **name, int flags)
59{ 59{
60 struct path root, tmp;
61 char *res; 60 char *res;
62 int connected, error = 0; 61 int error = 0;
62 int connected = 1;
63
64 if (path->mnt->mnt_flags & MNT_INTERNAL) {
65 /* it's not mounted anywhere */
66 res = dentry_path(path->dentry, buf, buflen);
67 *name = res;
68 if (IS_ERR(res)) {
69 *name = buf;
70 return PTR_ERR(res);
71 }
72 if (path->dentry->d_sb->s_magic == PROC_SUPER_MAGIC &&
73 strncmp(*name, "/sys/", 5) == 0) {
74 /* TODO: convert over to using a per namespace
75 * control instead of hard coded /proc
76 */
77 return prepend(name, *name - buf, "/proc", 5);
78 }
79 return 0;
80 }
63 81
64 /* Get the root we want to resolve too, released below */ 82 /* resolve paths relative to chroot?*/
65 if (flags & PATH_CHROOT_REL) { 83 if (flags & PATH_CHROOT_REL) {
66 /* resolve paths relative to chroot */ 84 struct path root;
67 get_fs_root(current->fs, &root); 85 get_fs_root(current->fs, &root);
68 } else { 86 res = __d_path(path, &root, buf, buflen);
69 /* resolve paths relative to namespace */ 87 if (res && !IS_ERR(res)) {
70 root.mnt = current->nsproxy->mnt_ns->root; 88 /* everything's fine */
71 root.dentry = root.mnt->mnt_root; 89 *name = res;
72 path_get(&root); 90 path_put(&root);
91 goto ok;
92 }
93 path_put(&root);
94 connected = 0;
73 } 95 }
74 96
75 tmp = root; 97 res = d_absolute_path(path, buf, buflen);
76 res = __d_path(path, &tmp, buf, buflen);
77 98
78 *name = res; 99 *name = res;
79 /* handle error conditions - and still allow a partial path to 100 /* handle error conditions - and still allow a partial path to
@@ -84,7 +105,10 @@ static int d_namespace_path(struct path *path, char *buf, int buflen,
84 *name = buf; 105 *name = buf;
85 goto out; 106 goto out;
86 } 107 }
108 if (!our_mnt(path->mnt))
109 connected = 0;
87 110
111ok:
88 /* Handle two cases: 112 /* Handle two cases:
89 * 1. A deleted dentry && profile is not allowing mediation of deleted 113 * 1. A deleted dentry && profile is not allowing mediation of deleted
90 * 2. On some filesystems, newly allocated dentries appear to the 114 * 2. On some filesystems, newly allocated dentries appear to the
@@ -97,10 +121,7 @@ static int d_namespace_path(struct path *path, char *buf, int buflen,
97 goto out; 121 goto out;
98 } 122 }
99 123
100 /* Determine if the path is connected to the expected root */ 124 /* If the path is not connected to the expected root,
101 connected = tmp.dentry == root.dentry && tmp.mnt == root.mnt;
102
103 /* If the path is not connected,
104 * check if it is a sysctl and handle specially else remove any 125 * check if it is a sysctl and handle specially else remove any
105 * leading / that __d_path may have returned. 126 * leading / that __d_path may have returned.
106 * Unless 127 * Unless
@@ -112,17 +133,9 @@ static int d_namespace_path(struct path *path, char *buf, int buflen,
112 * namespace root. 133 * namespace root.
113 */ 134 */
114 if (!connected) { 135 if (!connected) {
115 /* is the disconnect path a sysctl? */ 136 if (!(flags & PATH_CONNECT_PATH) &&
116 if (tmp.dentry->d_sb->s_magic == PROC_SUPER_MAGIC &&
117 strncmp(*name, "/sys/", 5) == 0) {
118 /* TODO: convert over to using a per namespace
119 * control instead of hard coded /proc
120 */
121 error = prepend(name, *name - buf, "/proc", 5);
122 } else if (!(flags & PATH_CONNECT_PATH) &&
123 !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && 137 !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) &&
124 (tmp.mnt == current->nsproxy->mnt_ns->root && 138 our_mnt(path->mnt))) {
125 tmp.dentry == tmp.mnt->mnt_root))) {
126 /* disconnected path, don't return pathname starting 139 /* disconnected path, don't return pathname starting
127 * with '/' 140 * with '/'
128 */ 141 */
@@ -133,8 +146,6 @@ static int d_namespace_path(struct path *path, char *buf, int buflen,
133 } 146 }
134 147
135out: 148out:
136 path_put(&root);
137
138 return error; 149 return error;
139} 150}
140 151
diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c
index 738bbdf8d4c7..36fa7c9bedc4 100644
--- a/security/tomoyo/realpath.c
+++ b/security/tomoyo/realpath.c
@@ -101,9 +101,8 @@ static char *tomoyo_get_absolute_path(struct path *path, char * const buffer,
101{ 101{
102 char *pos = ERR_PTR(-ENOMEM); 102 char *pos = ERR_PTR(-ENOMEM);
103 if (buflen >= 256) { 103 if (buflen >= 256) {
104 struct path ns_root = { };
105 /* go to whatever namespace root we are under */ 104 /* go to whatever namespace root we are under */
106 pos = __d_path(path, &ns_root, buffer, buflen - 1); 105 pos = d_absolute_path(path, buffer, buflen - 1);
107 if (!IS_ERR(pos) && *pos == '/' && pos[1]) { 106 if (!IS_ERR(pos) && *pos == '/' && pos[1]) {
108 struct inode *inode = path->dentry->d_inode; 107 struct inode *inode = path->dentry->d_inode;
109 if (inode && S_ISDIR(inode->i_mode)) { 108 if (inode && S_ISDIR(inode->i_mode)) {