[PATCH] Fix double decrement of mqueue_mnt->mnt_count in sys_mq_open

Fixed the refcounting on failure exits in sys_mq_open() and
cleaned the logics up. Rules are actually pretty simple - dentry_open()
expects vfsmount and dentry to be pinned down and it either transfers
them into created struct file or drops them. Old code had been very
confused in that area - if dentry_open() had failed either in do_open()
or do_create(), we ended up dentry and mqueue_mnt dropped twice, once
by dentry_open() cleanup and then by sys_mq_open().

Fix consists of making the rules for do_create() and do_open()
same as for dentry_open() and updating the sys_mq_open() accordingly;
that actually leads to more straightforward code and less work on
normal path.

Signed-off-by: Al Viro <aviro@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by Alexander Viro and committed by Linus Torvalds 7c7dce92 12dbf3fc

+33 -26
+33 -26
ipc/mqueue.c
··· 599 static struct file *do_create(struct dentry *dir, struct dentry *dentry, 600 int oflag, mode_t mode, struct mq_attr __user *u_attr) 601 { 602 - struct file *filp; 603 struct mq_attr attr; 604 int ret; 605 606 - if (u_attr != NULL) { 607 if (copy_from_user(&attr, u_attr, sizeof(attr))) 608 - return ERR_PTR(-EFAULT); 609 if (!mq_attr_ok(&attr)) 610 - return ERR_PTR(-EINVAL); 611 /* store for use during create */ 612 dentry->d_fsdata = &attr; 613 } ··· 617 ret = vfs_create(dir->d_inode, dentry, mode, NULL); 618 dentry->d_fsdata = NULL; 619 if (ret) 620 - return ERR_PTR(ret); 621 622 - filp = dentry_open(dentry, mqueue_mnt, oflag); 623 - if (!IS_ERR(filp)) 624 - dget(dentry); 625 626 - return filp; 627 } 628 629 /* Opens existing queue */ ··· 632 { 633 static int oflag2acc[O_ACCMODE] = { MAY_READ, MAY_WRITE, 634 MAY_READ | MAY_WRITE }; 635 - struct file *filp; 636 637 - if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY)) 638 return ERR_PTR(-EINVAL); 639 640 - if (permission(dentry->d_inode, oflag2acc[oflag & O_ACCMODE], NULL)) 641 return ERR_PTR(-EACCES); 642 643 - filp = dentry_open(dentry, mqueue_mnt, oflag); 644 - 645 - if (!IS_ERR(filp)) 646 - dget(dentry); 647 - 648 - return filp; 649 } 650 651 asmlinkage long sys_mq_open(const char __user *u_name, int oflag, mode_t mode, ··· 673 674 if (oflag & O_CREAT) { 675 if (dentry->d_inode) { /* entry already exists */ 676 - filp = (oflag & O_EXCL) ? ERR_PTR(-EEXIST) : 677 - do_open(dentry, oflag); 678 } else { 679 filp = do_create(mqueue_mnt->mnt_root, dentry, 680 oflag, mode, u_attr); 681 } 682 - } else 683 - filp = (dentry->d_inode) ? do_open(dentry, oflag) : 684 - ERR_PTR(-ENOENT); 685 - 686 - dput(dentry); 687 688 if (IS_ERR(filp)) { 689 error = PTR_ERR(filp); ··· 697 fd_install(fd, filp); 698 goto out_upsem; 699 700 - out_putfd: 701 mntput(mqueue_mnt); 702 put_unused_fd(fd); 703 out_err: 704 fd = error;
··· 599 static struct file *do_create(struct dentry *dir, struct dentry *dentry, 600 int oflag, mode_t mode, struct mq_attr __user *u_attr) 601 { 602 struct mq_attr attr; 603 int ret; 604 605 + if (u_attr) { 606 + ret = -EFAULT; 607 if (copy_from_user(&attr, u_attr, sizeof(attr))) 608 + goto out; 609 + ret = -EINVAL; 610 if (!mq_attr_ok(&attr)) 611 + goto out; 612 /* store for use during create */ 613 dentry->d_fsdata = &attr; 614 } ··· 616 ret = vfs_create(dir->d_inode, dentry, mode, NULL); 617 dentry->d_fsdata = NULL; 618 if (ret) 619 + goto out; 620 621 + return dentry_open(dentry, mqueue_mnt, oflag); 622 623 + out: 624 + dput(dentry); 625 + mntput(mqueue_mnt); 626 + return ERR_PTR(ret); 627 } 628 629 /* Opens existing queue */ ··· 630 { 631 static int oflag2acc[O_ACCMODE] = { MAY_READ, MAY_WRITE, 632 MAY_READ | MAY_WRITE }; 633 634 + if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY)) { 635 + dput(dentry); 636 + mntput(mqueue_mnt); 637 return ERR_PTR(-EINVAL); 638 + } 639 640 + if (permission(dentry->d_inode, oflag2acc[oflag & O_ACCMODE], NULL)) { 641 + dput(dentry); 642 + mntput(mqueue_mnt); 643 return ERR_PTR(-EACCES); 644 + } 645 646 + return dentry_open(dentry, mqueue_mnt, oflag); 647 } 648 649 asmlinkage long sys_mq_open(const char __user *u_name, int oflag, mode_t mode, ··· 671 672 if (oflag & O_CREAT) { 673 if (dentry->d_inode) { /* entry already exists */ 674 + error = -EEXIST; 675 + if (oflag & O_EXCL) 676 + goto out; 677 + filp = do_open(dentry, oflag); 678 } else { 679 filp = do_create(mqueue_mnt->mnt_root, dentry, 680 oflag, mode, u_attr); 681 } 682 + } else { 683 + error = -ENOENT; 684 + if (!dentry->d_inode) 685 + goto out; 686 + filp = do_open(dentry, oflag); 687 + } 688 689 if (IS_ERR(filp)) { 690 error = PTR_ERR(filp); ··· 692 fd_install(fd, filp); 693 goto out_upsem; 694 695 + out: 696 + dput(dentry); 697 mntput(mqueue_mnt); 698 + out_putfd: 699 put_unused_fd(fd); 700 out_err: 701 fd = error;