diff options
author | Mateusz Guzik <mjg@FreeBSD.org> | 2021-05-14 11:26:56 +0000 |
---|---|---|
committer | Mateusz Guzik <mjg@FreeBSD.org> | 2021-05-14 14:22:22 +0000 |
commit | ca1ce50b2b5ef11d85841f3aead98b2a9ad18819 (patch) | |
tree | 8849ca1fefde846127ff8a26cbf895e1a9637464 | |
parent | b5fb9ae6872c499f1a02bec41f48b163a73a2aaa (diff) | |
download | src-ca1ce50b2b5ef11d85841f3aead98b2a9ad18819.tar.gz src-ca1ce50b2b5ef11d85841f3aead98b2a9ad18819.zip |
vfs: add more safety against concurrent forced unmount to vn_write
1. stop re-reading ->v_mount (can become NULL)
2. stop re-reading ->v_type (can change to VBAD)
-rw-r--r-- | sys/kern/vfs_vnops.c | 21 |
1 files changed, 14 insertions, 7 deletions
diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c index 8f583329f067..6bf798cd73c5 100644 --- a/sys/kern/vfs_vnops.c +++ b/sys/kern/vfs_vnops.c @@ -1107,6 +1107,7 @@ vn_write(struct file *fp, struct uio *uio, struct ucred *active_cred, int flags, off_t orig_offset; int error, ioflag, lock_flags; int advice; + bool need_finished_write; KASSERT(uio->uio_td == td, ("uio_td %p is not td %p", uio->uio_td, td)); @@ -1121,9 +1122,11 @@ vn_write(struct file *fp, struct uio *uio, struct ucred *active_cred, int flags, ioflag |= IO_NDELAY; if (fp->f_flag & O_DIRECT) ioflag |= IO_DIRECT; - if ((fp->f_flag & O_FSYNC) || - (vp->v_mount && (vp->v_mount->mnt_flag & MNT_SYNCHRONOUS))) - ioflag |= IO_SYNC; + if (fp->f_flag & O_FSYNC) { + mp = atomic_load_ptr(&vp->v_mount); + if (mp != NULL && mp->mnt_flag & MNT_SYNCHRONOUS) + ioflag |= IO_SYNC; + } /* * For O_DSYNC we set both IO_SYNC and IO_DATASYNC, so that VOP_WRITE() * implementations that don't understand IO_DATASYNC fall back to full @@ -1132,9 +1135,13 @@ vn_write(struct file *fp, struct uio *uio, struct ucred *active_cred, int flags, if (fp->f_flag & O_DSYNC) ioflag |= IO_SYNC | IO_DATASYNC; mp = NULL; - if (vp->v_type != VCHR && - (error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) - goto unlock; + need_finished_write = false; + if (vp->v_type != VCHR) { + error = vn_start_write(vp, &mp, V_WAIT | PCATCH); + if (error != 0) + goto unlock; + need_finished_write = true; + } advice = get_advice(fp, uio); @@ -1165,7 +1172,7 @@ vn_write(struct file *fp, struct uio *uio, struct ucred *active_cred, int flags, error = VOP_WRITE(vp, uio, ioflag, fp->f_cred); fp->f_nextoff[UIO_WRITE] = uio->uio_offset; VOP_UNLOCK(vp); - if (vp->v_type != VCHR) + if (need_finished_write) vn_finished_write(mp); if (error == 0 && advice == POSIX_FADV_NOREUSE && orig_offset != uio->uio_offset) |