Print this page
12365 pwritev64 can't write at offsets between [2 GiB, 4 GiB)
Portions contributed by: John Levon <john.levon@joyent.com>
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>
Reviewed by: Robert Mustacchi <rm@joyent.com>

*** 20,30 **** */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. ! * Copyright 2015, Joyent, Inc. All rights reserved. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ --- 20,30 ---- */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. ! * Copyright 2017, Joyent, Inc. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */
*** 997,1019 **** int fflag, ioflag, rwflag; ssize_t count, bcount; int error = 0; int i; #if defined(_SYSCALL32_IMPL) || defined(_ILP32) u_offset_t fileoff = ((u_offset_t)extended_offset << 32) | (u_offset_t)offset; #else /* _SYSCALL32_IMPL || _ILP32 */ u_offset_t fileoff = (u_offset_t)(ulong_t)offset; #endif /* _SYSCALL32_IMPR || _ILP32 */ - #ifdef _SYSCALL32_IMPL - const u_offset_t maxoff = get_udatamodel() == DATAMODEL_ILP32 && - extended_offset == 0? - MAXOFF32_T : MAXOFFSET_T; - #else /* _SYSCALL32_IMPL */ - const u_offset_t maxoff = MAXOFF32_T; - #endif /* _SYSCALL32_IMPL */ int in_crit = 0; if (iovcnt <= 0 || iovcnt > IOV_MAX) return (set_errno(EINVAL)); --- 997,1024 ---- int fflag, ioflag, rwflag; ssize_t count, bcount; int error = 0; int i; + /* + * In a 64-bit kernel, this interface supports native 64-bit + * applications as well as 32-bit applications using both standard and + * large-file access. For 32-bit large-file aware applications, the + * offset is passed as two parameters which are joined into the actual + * offset used. The 64-bit libc always passes 0 for the extended_offset. + * Note that off_t is a signed value, but the preadv/pwritev API treats + * the offset as a position in the file for the operation, so passing + * a negative value will likely fail the maximum offset checks below + * because we convert it to an unsigned value which will be larger than + * the maximum valid offset. + */ #if defined(_SYSCALL32_IMPL) || defined(_ILP32) u_offset_t fileoff = ((u_offset_t)extended_offset << 32) | (u_offset_t)offset; #else /* _SYSCALL32_IMPL || _ILP32 */ u_offset_t fileoff = (u_offset_t)(ulong_t)offset; #endif /* _SYSCALL32_IMPR || _ILP32 */ int in_crit = 0; if (iovcnt <= 0 || iovcnt > IOV_MAX) return (set_errno(EINVAL));
*** 1080,1090 **** kmem_free(aiov, aiovlen); return (set_errno(EINVAL)); } } ! if ((bcount = (ssize_t)count) < 0) { if (aiovlen != 0) kmem_free(aiov, aiovlen); return (set_errno(EINVAL)); } if ((fp = getf(fdes)) == NULL) { --- 1085,1095 ---- kmem_free(aiov, aiovlen); return (set_errno(EINVAL)); } } ! if ((bcount = count) < 0) { if (aiovlen != 0) kmem_free(aiov, aiovlen); return (set_errno(EINVAL)); } if ((fp = getf(fdes)) == NULL) {
*** 1096,1121 **** error = EBADF; goto out; } vp = fp->f_vnode; rwflag = 0; - if (vp->v_type == VREG) { if (bcount == 0) goto out; ! /* ! * return EINVAL for offsets that cannot be ! * represented in an off_t. ! */ ! if (fileoff > maxoff) { ! error = EINVAL; goto out; } ! if (fileoff + bcount > maxoff) ! bcount = (ssize_t)((u_offset_t)maxoff - fileoff); } else if (vp->v_type == VFIFO) { error = ESPIPE; goto out; } /* --- 1101,1140 ---- error = EBADF; goto out; } vp = fp->f_vnode; rwflag = 0; + /* + * Behaviour is same as read(2). Please see comments in read above. + */ + if (vp->v_type == VREG) { if (bcount == 0) goto out; ! /* Handle offset past maximum offset allowed for file. */ ! if (fileoff >= OFFSET_MAX(fp)) { ! struct vattr va; ! va.va_mask = AT_SIZE; ! ! error = VOP_GETATTR(vp, &va, 0, fp->f_cred, NULL); ! if (error == 0) { ! if (fileoff >= va.va_size) { ! count = 0; ! } else { ! error = EOVERFLOW; ! } ! } goto out; } ! ASSERT(bcount == count); ! ! /* Note: modified count used in nbl_conflict() call below. */ ! if ((fileoff + count) > OFFSET_MAX(fp)) ! count = (ssize_t)(OFFSET_MAX(fp) - fileoff); ! } else if (vp->v_type == VFIFO) { error = ESPIPE; goto out; } /*
*** 1128,1173 **** nbl_start_crit(vp, RW_READER); in_crit = 1; error = nbl_svmand(vp, fp->f_cred, &svmand); if (error != 0) goto out; ! if (nbl_conflict(vp, NBL_WRITE, fileoff, count, svmand, ! NULL)) { error = EACCES; goto out; } } (void) VOP_RWLOCK(vp, rwflag, NULL); - /* - * Behaviour is same as read(2). Please see comments in - * read(2). - */ - - if ((vp->v_type == VREG) && (fileoff >= OFFSET_MAX(fp))) { - struct vattr va; - va.va_mask = AT_SIZE; - if ((error = - VOP_GETATTR(vp, &va, 0, fp->f_cred, NULL))) { - VOP_RWUNLOCK(vp, rwflag, NULL); - goto out; - } - if (fileoff >= va.va_size) { - VOP_RWUNLOCK(vp, rwflag, NULL); - count = 0; - goto out; - } else { - VOP_RWUNLOCK(vp, rwflag, NULL); - error = EOVERFLOW; - goto out; - } - } - if ((vp->v_type == VREG) && - (fileoff + count > OFFSET_MAX(fp))) { - count = (ssize_t)(OFFSET_MAX(fp) - fileoff); - } auio.uio_loffset = fileoff; auio.uio_iov = aiov; auio.uio_iovcnt = iovcnt; auio.uio_resid = bcount = count; auio.uio_segflg = UIO_USERSPACE; --- 1147,1164 ---- nbl_start_crit(vp, RW_READER); in_crit = 1; error = nbl_svmand(vp, fp->f_cred, &svmand); if (error != 0) goto out; ! if (nbl_conflict(vp, NBL_WRITE, fileoff, count, svmand, NULL)) { error = EACCES; goto out; } } (void) VOP_RWLOCK(vp, rwflag, NULL); auio.uio_loffset = fileoff; auio.uio_iov = aiov; auio.uio_iovcnt = iovcnt; auio.uio_resid = bcount = count; auio.uio_segflg = UIO_USERSPACE;
*** 1216,1238 **** int fflag, ioflag, rwflag; ssize_t count, bcount; int error = 0; int i; #if defined(_SYSCALL32_IMPL) || defined(_ILP32) u_offset_t fileoff = ((u_offset_t)extended_offset << 32) | (u_offset_t)offset; #else /* _SYSCALL32_IMPL || _ILP32 */ u_offset_t fileoff = (u_offset_t)(ulong_t)offset; #endif /* _SYSCALL32_IMPR || _ILP32 */ - #ifdef _SYSCALL32_IMPL - const u_offset_t maxoff = get_udatamodel() == DATAMODEL_ILP32 && - extended_offset == 0? - MAXOFF32_T : MAXOFFSET_T; - #else /* _SYSCALL32_IMPL */ - const u_offset_t maxoff = MAXOFF32_T; - #endif /* _SYSCALL32_IMPL */ int in_crit = 0; if (iovcnt <= 0 || iovcnt > IOV_MAX) return (set_errno(EINVAL)); --- 1207,1225 ---- int fflag, ioflag, rwflag; ssize_t count, bcount; int error = 0; int i; + /* + * See the comment in preadv for how the offset is handled. + */ #if defined(_SYSCALL32_IMPL) || defined(_ILP32) u_offset_t fileoff = ((u_offset_t)extended_offset << 32) | (u_offset_t)offset; #else /* _SYSCALL32_IMPL || _ILP32 */ u_offset_t fileoff = (u_offset_t)(ulong_t)offset; #endif /* _SYSCALL32_IMPR || _ILP32 */ int in_crit = 0; if (iovcnt <= 0 || iovcnt > IOV_MAX) return (set_errno(EINVAL));
*** 1299,1309 **** kmem_free(aiov, aiovlen); return (set_errno(EINVAL)); } } ! if ((bcount = (ssize_t)count) < 0) { if (aiovlen != 0) kmem_free(aiov, aiovlen); return (set_errno(EINVAL)); } if ((fp = getf(fdes)) == NULL) { --- 1286,1296 ---- kmem_free(aiov, aiovlen); return (set_errno(EINVAL)); } } ! if ((bcount = count) < 0) { if (aiovlen != 0) kmem_free(aiov, aiovlen); return (set_errno(EINVAL)); } if ((fp = getf(fdes)) == NULL) {
*** 1315,1337 **** error = EBADF; goto out; } vp = fp->f_vnode; rwflag = 1; - if (vp->v_type == VREG) { if (bcount == 0) goto out; /* ! * return EINVAL for offsets that cannot be ! * represented in an off_t. */ ! if (fileoff > maxoff) { ! error = EINVAL; goto out; } /* * Take appropriate action if we are trying * to write above the resource limit. */ if (fileoff >= curproc->p_fsz_ctl) { --- 1302,1329 ---- error = EBADF; goto out; } vp = fp->f_vnode; rwflag = 1; + /* + * The kernel's write(2) code checks OFFSET_MAX and the rctl, and + * returns EFBIG when fileoff exceeds either limit. We do the same. + */ + if (vp->v_type == VREG) { if (bcount == 0) goto out; /* ! * Don't allow pwritev to cause file size to exceed the proper ! * offset limit. */ ! if (fileoff >= OFFSET_MAX(fp)) { ! error = EFBIG; goto out; } + /* * Take appropriate action if we are trying * to write above the resource limit. */ if (fileoff >= curproc->p_fsz_ctl) {
*** 1350,1370 **** mutex_exit(&curproc->p_lock); error = EFBIG; goto out; } - /* - * Don't allow pwritev to cause file sizes to exceed - * maxoff. - */ - if (fileoff == maxoff) { - error = EFBIG; - goto out; - } ! if (fileoff + bcount > maxoff) ! bcount = (ssize_t)((u_offset_t)maxoff - fileoff); } else if (vp->v_type == VFIFO) { error = ESPIPE; goto out; } /* --- 1342,1358 ---- mutex_exit(&curproc->p_lock); error = EFBIG; goto out; } ! ASSERT(bcount == count); ! ! /* Note: modified count used in nbl_conflict() call below. */ ! if ((fileoff + count) > OFFSET_MAX(fp)) ! count = (ssize_t)(OFFSET_MAX(fp) - fileoff); ! } else if (vp->v_type == VFIFO) { error = ESPIPE; goto out; } /*
*** 1377,1423 **** nbl_start_crit(vp, RW_READER); in_crit = 1; error = nbl_svmand(vp, fp->f_cred, &svmand); if (error != 0) goto out; ! if (nbl_conflict(vp, NBL_WRITE, fileoff, count, svmand, ! NULL)) { error = EACCES; goto out; } } (void) VOP_RWLOCK(vp, rwflag, NULL); - - /* - * Behaviour is same as write(2). Please see comments for - * write(2). - */ - - if (vp->v_type == VREG) { - if (fileoff >= curproc->p_fsz_ctl) { - VOP_RWUNLOCK(vp, rwflag, NULL); - mutex_enter(&curproc->p_lock); - /* see above rctl_action comment */ - (void) rctl_action( - rctlproc_legacy[RLIMIT_FSIZE], - curproc->p_rctls, - curproc, RCA_UNSAFE_SIGINFO); - mutex_exit(&curproc->p_lock); - error = EFBIG; - goto out; - } - if (fileoff >= OFFSET_MAX(fp)) { - VOP_RWUNLOCK(vp, rwflag, NULL); - error = EFBIG; - goto out; - } - if (fileoff + count > OFFSET_MAX(fp)) - count = (ssize_t)(OFFSET_MAX(fp) - fileoff); - } - auio.uio_loffset = fileoff; auio.uio_iov = aiov; auio.uio_iovcnt = iovcnt; auio.uio_resid = bcount = count; auio.uio_segflg = UIO_USERSPACE; --- 1365,1382 ---- nbl_start_crit(vp, RW_READER); in_crit = 1; error = nbl_svmand(vp, fp->f_cred, &svmand); if (error != 0) goto out; ! if (nbl_conflict(vp, NBL_WRITE, fileoff, count, svmand, NULL)) { error = EACCES; goto out; } } (void) VOP_RWLOCK(vp, rwflag, NULL); auio.uio_loffset = fileoff; auio.uio_iov = aiov; auio.uio_iovcnt = iovcnt; auio.uio_resid = bcount = count; auio.uio_segflg = UIO_USERSPACE;