diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-12-07 11:14:42 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-12-07 11:14:42 -0500 |
commit | 3172f8fe1ca1d432b196efad453c0ceb89302075 (patch) | |
tree | 0b9e8af7ea9ee9883ff1a834357694254200335a /fs | |
parent | b835c0f47f725d864bf2545f10c733b754bb6d51 (diff) | |
parent | 02125a826459a6ad142f8d91c5b6357562f96615 (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
Diffstat (limited to 'fs')
-rw-r--r-- | fs/dcache.c | 71 | ||||
-rw-r--r-- | fs/namespace.c | 20 | ||||
-rw-r--r-- | fs/seq_file.c | 6 |
3 files changed, 58 insertions, 39 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 | */ |
2451 | static int prepend_path(const struct path *path, struct path *root, | 2448 | static 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 | ||
2486 | out: | ||
2487 | if (!error && !slash) | 2484 | if (!error && !slash) |
2488 | error = prepend(buffer, buflen, "/", 1); | 2485 | error = prepend(buffer, buflen, "/", 1); |
2489 | 2486 | ||
2487 | out: | ||
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 | */ |
2525 | char *__d_path(const struct path *path, struct path *root, | 2524 | char *__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 | |||
2543 | char *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 | */ |
2544 | static int path_with_deleted(const struct path *path, struct path *root, | 2565 | static 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 | } |
2778 | EXPORT_SYMBOL(kern_unmount); | 2775 | EXPORT_SYMBOL(kern_unmount); |
2776 | |||
2777 | bool 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 | */ |
455 | int seq_path_root(struct seq_file *m, struct path *path, struct path *root, | 453 | int 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 | /* |