diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2010-06-06 23:16:34 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2010-08-09 16:48:14 -0400 |
commit | e9193059b1b3733695d5b80e667778311695aa73 (patch) | |
tree | 658862931873ef6155ebe2172235bb408632a603 /fs/hostfs | |
parent | c103135c14e03fc9a9e5f0adc01df9ad272cf2a1 (diff) |
hostfs: fix races in dentry_name() and inode_name()
calculating size, then doing allocation, then filling the
path is a Bad Idea(tm), since the ancestors can be renamed,
leading to buffer overrun.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/hostfs')
-rw-r--r-- | fs/hostfs/hostfs_kern.c | 106 |
1 files changed, 60 insertions, 46 deletions
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 10bb71b1548b..79783a0b2f4d 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c | |||
@@ -90,44 +90,58 @@ __uml_setup("hostfs=", hostfs_args, | |||
90 | ); | 90 | ); |
91 | #endif | 91 | #endif |
92 | 92 | ||
93 | static char *dentry_name(struct dentry *dentry) | 93 | static char *__dentry_name(struct dentry *dentry, char *name) |
94 | { | 94 | { |
95 | struct dentry *parent; | 95 | char *p = __dentry_path(dentry, name, PATH_MAX); |
96 | char *root, *name; | 96 | char *root; |
97 | int len; | 97 | size_t len; |
98 | 98 | ||
99 | len = 0; | 99 | spin_unlock(&dcache_lock); |
100 | parent = dentry; | ||
101 | while (parent->d_parent != parent) { | ||
102 | len += parent->d_name.len + 1; | ||
103 | parent = parent->d_parent; | ||
104 | } | ||
105 | 100 | ||
106 | root = parent->d_sb->s_fs_info; | 101 | root = dentry->d_sb->s_fs_info; |
107 | len += strlen(root); | 102 | len = strlen(root); |
108 | name = kmalloc(len + 1, GFP_KERNEL); | 103 | if (IS_ERR(p)) { |
109 | if (name == NULL) | 104 | __putname(name); |
110 | return NULL; | 105 | return NULL; |
111 | |||
112 | name[len] = '\0'; | ||
113 | parent = dentry; | ||
114 | while (parent->d_parent != parent) { | ||
115 | len -= parent->d_name.len + 1; | ||
116 | name[len] = '/'; | ||
117 | strncpy(&name[len + 1], parent->d_name.name, | ||
118 | parent->d_name.len); | ||
119 | parent = parent->d_parent; | ||
120 | } | 106 | } |
121 | strncpy(name, root, strlen(root)); | 107 | strncpy(name, root, PATH_MAX); |
108 | if (len > p - name) { | ||
109 | __putname(name); | ||
110 | return NULL; | ||
111 | } | ||
112 | if (p > name + len) { | ||
113 | char *s = name + len; | ||
114 | while ((*s++ = *p++) != '\0') | ||
115 | ; | ||
116 | } | ||
122 | return name; | 117 | return name; |
123 | } | 118 | } |
124 | 119 | ||
120 | static char *dentry_name(struct dentry *dentry) | ||
121 | { | ||
122 | char *name = __getname(); | ||
123 | if (!name) | ||
124 | return NULL; | ||
125 | |||
126 | spin_lock(&dcache_lock); | ||
127 | return __dentry_name(dentry, name); /* will unlock */ | ||
128 | } | ||
129 | |||
125 | static char *inode_name(struct inode *ino) | 130 | static char *inode_name(struct inode *ino) |
126 | { | 131 | { |
127 | struct dentry *dentry; | 132 | struct dentry *dentry; |
133 | char *name = __getname(); | ||
134 | if (!name) | ||
135 | return NULL; | ||
128 | 136 | ||
129 | dentry = list_entry(ino->i_dentry.next, struct dentry, d_alias); | 137 | spin_lock(&dcache_lock); |
130 | return dentry_name(dentry); | 138 | if (list_empty(&ino->i_dentry)) { |
139 | spin_unlock(&dcache_lock); | ||
140 | __putname(name); | ||
141 | return NULL; | ||
142 | } | ||
143 | dentry = list_first_entry(&ino->i_dentry, struct dentry, d_alias); | ||
144 | return __dentry_name(dentry, name); /* will unlock */ | ||
131 | } | 145 | } |
132 | 146 | ||
133 | static char *follow_link(char *link) | 147 | static char *follow_link(char *link) |
@@ -272,7 +286,7 @@ int hostfs_readdir(struct file *file, void *ent, filldir_t filldir) | |||
272 | if (name == NULL) | 286 | if (name == NULL) |
273 | return -ENOMEM; | 287 | return -ENOMEM; |
274 | dir = open_dir(name, &error); | 288 | dir = open_dir(name, &error); |
275 | kfree(name); | 289 | __putname(name); |
276 | if (dir == NULL) | 290 | if (dir == NULL) |
277 | return -error; | 291 | return -error; |
278 | next = file->f_pos; | 292 | next = file->f_pos; |
@@ -318,7 +332,7 @@ int hostfs_file_open(struct inode *ino, struct file *file) | |||
318 | return -ENOMEM; | 332 | return -ENOMEM; |
319 | 333 | ||
320 | fd = open_file(name, r, w, append); | 334 | fd = open_file(name, r, w, append); |
321 | kfree(name); | 335 | __putname(name); |
322 | if (fd < 0) | 336 | if (fd < 0) |
323 | return fd; | 337 | return fd; |
324 | FILE_HOSTFS_I(file)->fd = fd; | 338 | FILE_HOSTFS_I(file)->fd = fd; |
@@ -532,7 +546,7 @@ int hostfs_create(struct inode *dir, struct dentry *dentry, int mode, | |||
532 | else | 546 | else |
533 | error = read_name(inode, name); | 547 | error = read_name(inode, name); |
534 | 548 | ||
535 | kfree(name); | 549 | __putname(name); |
536 | if (error) | 550 | if (error) |
537 | goto out_put; | 551 | goto out_put; |
538 | 552 | ||
@@ -567,7 +581,7 @@ struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry, | |||
567 | 581 | ||
568 | err = read_name(inode, name); | 582 | err = read_name(inode, name); |
569 | 583 | ||
570 | kfree(name); | 584 | __putname(name); |
571 | if (err == -ENOENT) { | 585 | if (err == -ENOENT) { |
572 | iput(inode); | 586 | iput(inode); |
573 | inode = NULL; | 587 | inode = NULL; |
@@ -594,12 +608,12 @@ int hostfs_link(struct dentry *to, struct inode *ino, struct dentry *from) | |||
594 | return -ENOMEM; | 608 | return -ENOMEM; |
595 | to_name = dentry_name(to); | 609 | to_name = dentry_name(to); |
596 | if (to_name == NULL) { | 610 | if (to_name == NULL) { |
597 | kfree(from_name); | 611 | __putname(from_name); |
598 | return -ENOMEM; | 612 | return -ENOMEM; |
599 | } | 613 | } |
600 | err = link_file(to_name, from_name); | 614 | err = link_file(to_name, from_name); |
601 | kfree(from_name); | 615 | __putname(from_name); |
602 | kfree(to_name); | 616 | __putname(to_name); |
603 | return err; | 617 | return err; |
604 | } | 618 | } |
605 | 619 | ||
@@ -614,7 +628,7 @@ int hostfs_unlink(struct inode *ino, struct dentry *dentry) | |||
614 | return -EPERM; | 628 | return -EPERM; |
615 | 629 | ||
616 | err = unlink_file(file); | 630 | err = unlink_file(file); |
617 | kfree(file); | 631 | __putname(file); |
618 | return err; | 632 | return err; |
619 | } | 633 | } |
620 | 634 | ||
@@ -626,7 +640,7 @@ int hostfs_symlink(struct inode *ino, struct dentry *dentry, const char *to) | |||
626 | if ((file = dentry_name(dentry)) == NULL) | 640 | if ((file = dentry_name(dentry)) == NULL) |
627 | return -ENOMEM; | 641 | return -ENOMEM; |
628 | err = make_symlink(file, to); | 642 | err = make_symlink(file, to); |
629 | kfree(file); | 643 | __putname(file); |
630 | return err; | 644 | return err; |
631 | } | 645 | } |
632 | 646 | ||
@@ -638,7 +652,7 @@ int hostfs_mkdir(struct inode *ino, struct dentry *dentry, int mode) | |||
638 | if ((file = dentry_name(dentry)) == NULL) | 652 | if ((file = dentry_name(dentry)) == NULL) |
639 | return -ENOMEM; | 653 | return -ENOMEM; |
640 | err = do_mkdir(file, mode); | 654 | err = do_mkdir(file, mode); |
641 | kfree(file); | 655 | __putname(file); |
642 | return err; | 656 | return err; |
643 | } | 657 | } |
644 | 658 | ||
@@ -650,7 +664,7 @@ int hostfs_rmdir(struct inode *ino, struct dentry *dentry) | |||
650 | if ((file = dentry_name(dentry)) == NULL) | 664 | if ((file = dentry_name(dentry)) == NULL) |
651 | return -ENOMEM; | 665 | return -ENOMEM; |
652 | err = do_rmdir(file); | 666 | err = do_rmdir(file); |
653 | kfree(file); | 667 | __putname(file); |
654 | return err; | 668 | return err; |
655 | } | 669 | } |
656 | 670 | ||
@@ -673,13 +687,13 @@ int hostfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) | |||
673 | 687 | ||
674 | init_special_inode(inode, mode, dev); | 688 | init_special_inode(inode, mode, dev); |
675 | err = do_mknod(name, mode, MAJOR(dev), MINOR(dev)); | 689 | err = do_mknod(name, mode, MAJOR(dev), MINOR(dev)); |
676 | if (err) | 690 | if (!err) |
677 | goto out_free; | 691 | goto out_free; |
678 | 692 | ||
679 | err = read_name(inode, name); | 693 | err = read_name(inode, name); |
694 | __putname(name); | ||
680 | if (err) | 695 | if (err) |
681 | goto out_put; | 696 | goto out_put; |
682 | kfree(name); | ||
683 | if (err) | 697 | if (err) |
684 | goto out_put; | 698 | goto out_put; |
685 | 699 | ||
@@ -687,7 +701,7 @@ int hostfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) | |||
687 | return 0; | 701 | return 0; |
688 | 702 | ||
689 | out_free: | 703 | out_free: |
690 | kfree(name); | 704 | __putname(name); |
691 | out_put: | 705 | out_put: |
692 | iput(inode); | 706 | iput(inode); |
693 | out: | 707 | out: |
@@ -703,12 +717,12 @@ int hostfs_rename(struct inode *from_ino, struct dentry *from, | |||
703 | if ((from_name = dentry_name(from)) == NULL) | 717 | if ((from_name = dentry_name(from)) == NULL) |
704 | return -ENOMEM; | 718 | return -ENOMEM; |
705 | if ((to_name = dentry_name(to)) == NULL) { | 719 | if ((to_name = dentry_name(to)) == NULL) { |
706 | kfree(from_name); | 720 | __putname(from_name); |
707 | return -ENOMEM; | 721 | return -ENOMEM; |
708 | } | 722 | } |
709 | err = rename_file(from_name, to_name); | 723 | err = rename_file(from_name, to_name); |
710 | kfree(from_name); | 724 | __putname(from_name); |
711 | kfree(to_name); | 725 | __putname(to_name); |
712 | return err; | 726 | return err; |
713 | } | 727 | } |
714 | 728 | ||
@@ -729,7 +743,7 @@ int hostfs_permission(struct inode *ino, int desired) | |||
729 | err = 0; | 743 | err = 0; |
730 | else | 744 | else |
731 | err = access_file(name, r, w, x); | 745 | err = access_file(name, r, w, x); |
732 | kfree(name); | 746 | __putname(name); |
733 | if (!err) | 747 | if (!err) |
734 | err = generic_permission(ino, desired, NULL); | 748 | err = generic_permission(ino, desired, NULL); |
735 | return err; | 749 | return err; |
@@ -790,7 +804,7 @@ int hostfs_setattr(struct dentry *dentry, struct iattr *attr) | |||
790 | if (name == NULL) | 804 | if (name == NULL) |
791 | return -ENOMEM; | 805 | return -ENOMEM; |
792 | err = set_attr(name, &attrs, fd); | 806 | err = set_attr(name, &attrs, fd); |
793 | kfree(name); | 807 | __putname(name); |
794 | if (err) | 808 | if (err) |
795 | return err; | 809 | return err; |
796 | 810 | ||
@@ -845,7 +859,7 @@ static void *hostfs_follow_link(struct dentry *dentry, struct nameidata *nd) | |||
845 | int err = hostfs_do_readlink(path, link, PATH_MAX); | 859 | int err = hostfs_do_readlink(path, link, PATH_MAX); |
846 | if (err == PATH_MAX) | 860 | if (err == PATH_MAX) |
847 | err = -E2BIG; | 861 | err = -E2BIG; |
848 | kfree(path); | 862 | __putname(path); |
849 | } | 863 | } |
850 | if (err < 0) { | 864 | if (err < 0) { |
851 | __putname(link); | 865 | __putname(link); |