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