aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMateusz Guzik <mjg@FreeBSD.org>2021-05-14 11:26:56 +0000
committerMateusz Guzik <mjg@FreeBSD.org>2021-05-14 14:22:22 +0000
commitca1ce50b2b5ef11d85841f3aead98b2a9ad18819 (patch)
tree8849ca1fefde846127ff8a26cbf895e1a9637464
parentb5fb9ae6872c499f1a02bec41f48b163a73a2aaa (diff)
downloadsrc-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.c21
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)