Skip to content

Commit f7927c4

Browse files
fsvm88tonyhutter
authored andcommitted
Bugfix/fix uio partial copies
In zfs_write(), the loop continues to the next iteration without accounting for partial copies occurring in uiomove_iov when copy_from_user/__copy_from_user_inatomic return a non-zero status. This results in "zfs: accessing past end of object..." in the kernel log, and the write failing. Account for partial copies and update uio struct before returning EFAULT, leave a comment explaining the reason why this is done. Reviewed-by: Brian Behlendorf <[email protected]> Reviewed-by: ilbsmart <[email protected]> Signed-off-by: Fabio Scaccabarozzi <[email protected]> Closes #8673 Closes #10148
1 parent 5a89fca commit f7927c4

File tree

2 files changed

+26
-8
lines changed

2 files changed

+26
-8
lines changed

module/zcommon/zfs_uio.c

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -80,22 +80,31 @@ uiomove_iov(void *p, size_t n, enum uio_rw rw, struct uio *uio)
8080
if (copy_to_user(iov->iov_base+skip, p, cnt))
8181
return (EFAULT);
8282
} else {
83+
unsigned long b_left = 0;
8384
if (uio->uio_fault_disable) {
8485
if (!zfs_access_ok(VERIFY_READ,
8586
(iov->iov_base + skip), cnt)) {
8687
return (EFAULT);
8788
}
8889
pagefault_disable();
89-
if (__copy_from_user_inatomic(p,
90-
(iov->iov_base + skip), cnt)) {
91-
pagefault_enable();
92-
return (EFAULT);
93-
}
90+
b_left =
91+
__copy_from_user_inatomic(p,
92+
(iov->iov_base + skip), cnt);
9493
pagefault_enable();
9594
} else {
96-
if (copy_from_user(p,
97-
(iov->iov_base + skip), cnt))
98-
return (EFAULT);
95+
b_left =
96+
copy_from_user(p,
97+
(iov->iov_base + skip), cnt);
98+
}
99+
if (b_left > 0) {
100+
unsigned long c_bytes =
101+
cnt - b_left;
102+
uio->uio_skip += c_bytes;
103+
ASSERT3U(uio->uio_skip, <,
104+
iov->iov_len);
105+
uio->uio_resid -= c_bytes;
106+
uio->uio_loffset += c_bytes;
107+
return (EFAULT);
99108
}
100109
}
101110
break;

module/zfs/zfs_vnops.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,15 @@ zfs_write(struct inode *ip, uio_t *uio, int ioflag, cred_t *cr)
829829
uio->uio_fault_disable = B_FALSE;
830830
if (error == EFAULT) {
831831
dmu_tx_commit(tx);
832+
/*
833+
* Account for partial writes before
834+
* continuing the loop.
835+
* Update needs to occur before the next
836+
* uio_prefaultpages, or prefaultpages may
837+
* error, and we may break the loop early.
838+
*/
839+
if (tx_bytes != uio->uio_resid)
840+
n -= tx_bytes - uio->uio_resid;
832841
if (uio_prefaultpages(MIN(n, max_blksz), uio)) {
833842
break;
834843
}

0 commit comments

Comments
 (0)