From 515ddf65042e8eb772c3f38ed4556850a0c2fbf3 Mon Sep 17 00:00:00 2001 From: Brian Behlendorf Date: Wed, 8 May 2019 10:04:04 -0700 Subject: [PATCH] Fix errant EFAULT during writes (#8719) Commit 98bb45e resolved a deadlock which could occur when handling a page fault in zfs_write(). This change added the uio_fault_disable field to the uio structure but failed to initialize it to B_FALSE. This uninitialized field would cause uiomove_iov() to call __copy_from_user_inatomic() instead of copy_from_user() resulting in unexpected EFAULTs. Resolve the issue by fully initializing the uio, and clearing the uio_fault_disable flags after it's used in zfs_write(). Additionally, reorder the uio_t field assignments to match the order the fields are declared in the structure. Reviewed-by: Chunwei Chen Reviewed-by: Richard Laager Reviewed-by: Tim Chase Signed-off-by: Brian Behlendorf Closes #8640 Closes #8719 --- module/zfs/zfs_vnops.c | 1 + module/zfs/zpl_file.c | 16 ++++++++-------- module/zfs/zpl_inode.c | 5 ++--- module/zfs/zvol.c | 10 +++++----- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c index 281f58249..27f179d82 100644 --- a/module/zfs/zfs_vnops.c +++ b/module/zfs/zfs_vnops.c @@ -822,6 +822,7 @@ zfs_write(struct inode *ip, uio_t *uio, int ioflag, cred_t *cr) uio->uio_fault_disable = B_TRUE; error = dmu_write_uio_dbuf(sa_get_db(zp->z_sa_hdl), uio, nbytes, tx); + uio->uio_fault_disable = B_FALSE; if (error == EFAULT) { dmu_tx_commit(tx); if (uio_prefaultpages(MIN(n, max_blksz), uio)) { diff --git a/module/zfs/zpl_file.c b/module/zfs/zpl_file.c index 731836c2c..acad4670d 100644 --- a/module/zfs/zpl_file.c +++ b/module/zfs/zpl_file.c @@ -246,17 +246,17 @@ zpl_read_common_iovec(struct inode *ip, const struct iovec *iovp, size_t count, cred_t *cr, size_t skip) { ssize_t read; - uio_t uio; + uio_t uio = { { 0 }, 0 }; int error; fstrans_cookie_t cookie; uio.uio_iov = iovp; - uio.uio_skip = skip; - uio.uio_resid = count; uio.uio_iovcnt = nr_segs; uio.uio_loffset = *ppos; - uio.uio_limit = MAXOFFSET_T; uio.uio_segflg = segment; + uio.uio_limit = MAXOFFSET_T; + uio.uio_resid = count; + uio.uio_skip = skip; cookie = spl_fstrans_mark(); error = -zfs_read(ip, &uio, flags, cr); @@ -356,7 +356,7 @@ zpl_write_common_iovec(struct inode *ip, const struct iovec *iovp, size_t count, cred_t *cr, size_t skip) { ssize_t wrote; - uio_t uio; + uio_t uio = { { 0 }, 0 }; int error; fstrans_cookie_t cookie; @@ -364,12 +364,12 @@ zpl_write_common_iovec(struct inode *ip, const struct iovec *iovp, size_t count, *ppos = i_size_read(ip); uio.uio_iov = iovp; - uio.uio_skip = skip; - uio.uio_resid = count; uio.uio_iovcnt = nr_segs; uio.uio_loffset = *ppos; - uio.uio_limit = MAXOFFSET_T; uio.uio_segflg = segment; + uio.uio_limit = MAXOFFSET_T; + uio.uio_resid = count; + uio.uio_skip = skip; cookie = spl_fstrans_mark(); error = -zfs_write(ip, &uio, flags, cr); diff --git a/module/zfs/zpl_inode.c b/module/zfs/zpl_inode.c index 720330a8b..3f3b2e2dc 100644 --- a/module/zfs/zpl_inode.c +++ b/module/zfs/zpl_inode.c @@ -493,7 +493,7 @@ zpl_get_link_common(struct dentry *dentry, struct inode *ip, char **link) fstrans_cookie_t cookie; cred_t *cr = CRED(); struct iovec iov; - uio_t uio; + uio_t uio = { { 0 }, 0 }; int error; crhold(cr); @@ -503,9 +503,8 @@ zpl_get_link_common(struct dentry *dentry, struct inode *ip, char **link) uio.uio_iov = &iov; uio.uio_iovcnt = 1; - uio.uio_skip = 0; - uio.uio_resid = (MAXPATHLEN - 1); uio.uio_segflg = UIO_SYSSPACE; + uio.uio_resid = (MAXPATHLEN - 1); cookie = spl_fstrans_mark(); error = -zfs_readlink(ip, &uio, cr); diff --git a/module/zfs/zvol.c b/module/zfs/zvol.c index a77339d7f..c29f65f67 100644 --- a/module/zfs/zvol.c +++ b/module/zfs/zvol.c @@ -727,12 +727,12 @@ static void uio_from_bio(uio_t *uio, struct bio *bio) { uio->uio_bvec = &bio->bi_io_vec[BIO_BI_IDX(bio)]; - uio->uio_skip = BIO_BI_SKIP(bio); - uio->uio_resid = BIO_BI_SIZE(bio); uio->uio_iovcnt = bio->bi_vcnt - BIO_BI_IDX(bio); uio->uio_loffset = BIO_BI_SECTOR(bio) << 9; - uio->uio_limit = MAXOFFSET_T; uio->uio_segflg = UIO_BVEC; + uio->uio_limit = MAXOFFSET_T; + uio->uio_resid = BIO_BI_SIZE(bio); + uio->uio_skip = BIO_BI_SKIP(bio); } static void @@ -742,7 +742,7 @@ zvol_write(void *arg) zv_request_t *zvr = arg; struct bio *bio = zvr->bio; - uio_t uio; + uio_t uio = { { 0 }, 0 }; uio_from_bio(&uio, bio); zvol_state_t *zv = zvr->zv; @@ -897,7 +897,7 @@ zvol_read(void *arg) zv_request_t *zvr = arg; struct bio *bio = zvr->bio; - uio_t uio; + uio_t uio = { { 0 }, 0 }; uio_from_bio(&uio, bio); zvol_state_t *zv = zvr->zv; -- 2.40.0