Hi,

If an unlink fails due to unveil, the reference count of the inode
is not decremented.  You cannot unmount the file system anymore.
I have added unveilleak.c that triggers the breakage.

dd if=/dev/zero of=diskimage bs=512 count=4k
vnconfig vnd0 diskimage
newfs vnd0c
mount /dev/vnd0c /mnt
unveilleak /mnt foo
umount /mnt
umount: /mnt: Device busy

Kenrel debug print shows that use count is not 0.

vflush: busy vnode: 0xd50bf2e8, type VDIR, use 1, write 0, hold 1, flags (VROOT)
        tag VT_UFS, ino 2, on dev 14, 2 flags 0x100, effnlink 2, nlink 2
        mode 040755, owner 0, group 0, size 512

This unveilleak.c program increases use by 1.

#include <sys/stat.h>

#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int
main(int argc, char *argv[])
{
        char *dir, *file, *path;
        int fd;

        if (argc != 3)
                errx(2, "missing dir file");

        dir = argv[1];
        file = argv[2];
        if (asprintf(&path, "%s/%s", dir, file) == -1)
                err(1, "asprintf");

        fd = open(path, O_WRONLY|O_CREAT, 0755);
        if (fd == -1)
                err(1, "open %s", dir);
        close(fd);

        if (unveil(dir, "r") == -1)
                err(1, "unveil %s", dir);
        if (unveil(NULL, NULL) == -1)
                err(1, "unveil NULL");
        if (unlink(path) == 0)
                errx(1, "unlink %s succeeded", path);

        return 0;
}

And this vput(9) in namei(9) fixes the bug.

ok?

bluhm

Index: kern/vfs_lookup.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/kern/vfs_lookup.c,v
retrieving revision 1.77
diff -u -p -r1.77 vfs_lookup.c
--- kern/vfs_lookup.c   13 May 2019 22:55:27 -0000      1.77
+++ kern/vfs_lookup.c   4 Jul 2019 20:16:24 -0000
@@ -262,7 +262,7 @@ fail:
                                if ((cnp->cn_flags & LOCKPARENT) &&
                                    (cnp->cn_flags & ISLASTCN) &&
                                    (ndp->ni_vp != ndp->ni_dvp))
-                                       VOP_UNLOCK(ndp->ni_dvp);
+                                       vput(ndp->ni_dvp);
                                if (ndp->ni_vp) {
                                        if ((cnp->cn_flags & LOCKLEAF))
                                                vput(ndp->ni_vp);

Reply via email to