Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'pull-bcachefs-fix' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull bcachefs locking fix from Al Viro:
"Fix broken locking in bch2_ioctl_subvolume_destroy()"

* tag 'pull-bcachefs-fix' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
bch2_ioctl_subvolume_destroy(): fix locking
new helper: user_path_locked_at()

+31 -17
+17 -14
fs/bcachefs/fs-ioctl.c
··· 443 443 static long bch2_ioctl_subvolume_destroy(struct bch_fs *c, struct file *filp, 444 444 struct bch_ioctl_subvolume arg) 445 445 { 446 + const char __user *name = (void __user *)(unsigned long)arg.dst_ptr; 446 447 struct path path; 447 448 struct inode *dir; 449 + struct dentry *victim; 448 450 int ret = 0; 449 451 450 452 if (arg.flags) 451 453 return -EINVAL; 452 454 453 - ret = user_path_at(arg.dirfd, 454 - (const char __user *)(unsigned long)arg.dst_ptr, 455 - LOOKUP_FOLLOW, &path); 456 - if (ret) 457 - return ret; 455 + victim = user_path_locked_at(arg.dirfd, name, &path); 456 + if (IS_ERR(victim)) 457 + return PTR_ERR(victim); 458 458 459 - if (path.dentry->d_sb->s_fs_info != c) { 459 + if (victim->d_sb->s_fs_info != c) { 460 460 ret = -EXDEV; 461 461 goto err; 462 462 } 463 - 464 - dir = path.dentry->d_parent->d_inode; 465 - 466 - ret = __bch2_unlink(dir, path.dentry, true); 467 - if (ret) 463 + if (!d_is_positive(victim)) { 464 + ret = -ENOENT; 468 465 goto err; 469 - 470 - fsnotify_rmdir(dir, path.dentry); 471 - d_delete(path.dentry); 466 + } 467 + dir = d_inode(path.dentry); 468 + ret = __bch2_unlink(dir, victim, true); 469 + if (!ret) { 470 + fsnotify_rmdir(dir, victim); 471 + d_delete(victim); 472 + } 473 + inode_unlock(dir); 472 474 err: 475 + dput(victim); 473 476 path_put(&path); 474 477 return ret; 475 478 }
+13 -3
fs/namei.c
··· 2572 2572 } 2573 2573 2574 2574 /* does lookup, returns the object with parent locked */ 2575 - static struct dentry *__kern_path_locked(struct filename *name, struct path *path) 2575 + static struct dentry *__kern_path_locked(int dfd, struct filename *name, struct path *path) 2576 2576 { 2577 2577 struct dentry *d; 2578 2578 struct qstr last; 2579 2579 int type, error; 2580 2580 2581 - error = filename_parentat(AT_FDCWD, name, 0, path, &last, &type); 2581 + error = filename_parentat(dfd, name, 0, path, &last, &type); 2582 2582 if (error) 2583 2583 return ERR_PTR(error); 2584 2584 if (unlikely(type != LAST_NORM)) { ··· 2597 2597 struct dentry *kern_path_locked(const char *name, struct path *path) 2598 2598 { 2599 2599 struct filename *filename = getname_kernel(name); 2600 - struct dentry *res = __kern_path_locked(filename, path); 2600 + struct dentry *res = __kern_path_locked(AT_FDCWD, filename, path); 2601 2601 2602 2602 putname(filename); 2603 2603 return res; 2604 2604 } 2605 + 2606 + struct dentry *user_path_locked_at(int dfd, const char __user *name, struct path *path) 2607 + { 2608 + struct filename *filename = getname(name); 2609 + struct dentry *res = __kern_path_locked(dfd, filename, path); 2610 + 2611 + putname(filename); 2612 + return res; 2613 + } 2614 + EXPORT_SYMBOL(user_path_locked_at); 2605 2615 2606 2616 int kern_path(const char *name, unsigned int flags, struct path *path) 2607 2617 {
+1
include/linux/namei.h
··· 66 66 extern struct dentry *user_path_create(int, const char __user *, struct path *, unsigned int); 67 67 extern void done_path_create(struct path *, struct dentry *); 68 68 extern struct dentry *kern_path_locked(const char *, struct path *); 69 + extern struct dentry *user_path_locked_at(int , const char __user *, struct path *); 69 70 int vfs_path_parent_lookup(struct filename *filename, unsigned int flags, 70 71 struct path *parent, struct qstr *last, int *type, 71 72 const struct path *root);