sendfile: Ensure that sfio->npages is initialized

We initialize sfio->npages only when some I/O is required to satisfy the
request.  However, sendfile_iodone() contains an INVARIANTS-only check
that references sfio->npages, and this check is executed even if no I/O
is performed, so the check may use an uninitialized value.

Fix the problem by initializing sfio->npages earlier.  Note that
sendfile_swapin() always initializes the page array.  In some rare cases
we need to trim the page array so ensure that sfio->npages gets updated
accordingly.

Reported by:		syzkaller (with KASAN)
Reviewed by:		kib
Sponsored by:		The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D27726
This commit is contained in:
Mark Johnston 2020-12-26 16:07:40 -05:00
parent 5d58f959d3
commit 26b23f07fb

View File

@ -413,12 +413,13 @@ sendfile_iodone(void *arg, vm_page_t *pa, int count, int error)
*/
static int
sendfile_swapin(vm_object_t obj, struct sf_io *sfio, int *nios, off_t off,
off_t len, int npages, int rhpages, int flags)
off_t len, int rhpages, int flags)
{
vm_page_t *pa;
int a, count, count1, grabbed, i, j, rv;
int a, count, count1, grabbed, i, j, npages, rv;
pa = sfio->pa;
npages = sfio->npages;
*nios = 0;
flags = (flags & SF_NODISKIO) ? VM_ALLOC_NOWAIT : 0;
sfio->pindex0 = OFF_TO_IDX(off);
@ -905,6 +906,7 @@ vn_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio,
sfio->obj = obj;
sfio->error = 0;
sfio->m = NULL;
sfio->npages = npages;
#ifdef KERN_TLS
/*
* This doesn't use ktls_hold() because sfio->m will
@ -914,8 +916,8 @@ vn_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio,
sfio->tls = tls;
#endif
vm_object_pip_add(obj, 1);
error = sendfile_swapin(obj, sfio, &nios, off, space, npages,
rhpages, flags);
error = sendfile_swapin(obj, sfio, &nios, off, space, rhpages,
flags);
if (error != 0) {
if (vp != NULL)
VOP_UNLOCK(vp);
@ -963,7 +965,7 @@ vn_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio,
if (pa[i] == NULL) {
SFSTAT_INC(sf_busy);
fixspace(npages, i, off, &space);
npages = i;
sfio->npages = i;
softerr = EBUSY;
break;
}
@ -1042,12 +1044,14 @@ vn_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio,
if (sf == NULL) {
SFSTAT_INC(sf_allocfail);
sendfile_iowait(sfio, "sfnosf");
for (int j = i; j < npages; j++)
for (int j = i; j < npages; j++) {
vm_page_unwire(pa[j], PQ_INACTIVE);
pa[j] = NULL;
}
if (m == NULL)
softerr = ENOBUFS;
fixspace(npages, i, off, &space);
npages = i;
sfio->npages = i;
break;
}
@ -1152,7 +1156,6 @@ vn_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio,
} else {
sfio->so = so;
sfio->m = m0;
sfio->npages = npages;
soref(so);
error = (*so->so_proto->pr_usrreqs->pru_send)
(so, PRUS_NOTREADY, m, NULL, NULL, td);