Martin Natano wrote:
> Stefan Kempf wrote:
> > I'm a bit uneasy though with passing signed values as-is to uiomove().
> > Can we somehow make it explicit that we know that the uiomove() argument is
> > >= 0?
> >
> > Changing types all over the place would be too much churn though.
> >
> > I'm leaning towards an explicit size_t cast for signed size arguments as
> > an annotation, e.g.
> > uiomove(buf, (size_t)signed_value, uio);
>
> I agree, a cast like that would make the intent clear to the reader. See
> the regenerated diff below.
>
>
> > And a check in uiomove(). If a negative value gets passed in by
> > accident, it will be caught.
> >
> > Thoughts?
> >
> > Index: kern_subr.c
> > ===================================================================
> > RCS file: /cvs/src/sys/kern/kern_subr.c,v
> > retrieving revision 1.45
> > diff -u -p -U10 -r1.45 kern_subr.c
> > --- kern_subr.c 11 Dec 2015 16:07:02 -0000 1.45
> > +++ kern_subr.c 17 Jan 2016 10:56:11 -0000
> > @@ -46,20 +46,23 @@
> > #include <sys/resourcevar.h>
> >
> > int
> > uiomove(void *cp, size_t n, struct uio *uio)
> > {
> > struct iovec *iov;
> > size_t cnt;
> > int error = 0;
> > struct proc *p;
> >
> > + if (n > SSIZE_MAX)
> > + return (EINVAL);
> > +
> > p = uio->uio_procp;
> >
> > #ifdef DIAGNOSTIC
> > if (uio->uio_rw != UIO_READ && uio->uio_rw != UIO_WRITE)
> > panic("uiomove: mode");
> > if (uio->uio_segflg == UIO_USERSPACE && p != curproc)
> > panic("uiomove: proc");
> > #endif
> > while (n > 0 && uio->uio_resid) {
> > iov = uio->uio_iov;
>
> I don't think this will fix the underlying problem. It is not possible
> to detect all types of overflow inside of uiomove(). Let's take a look
> at our tmpfs example: An off_t value is passed to uimove(). On i386,
> size_t is an unsigned 32 bit integer and off_t is a signed 64 bit
> integer. When the off_t value is truncated on conversion, the
> 'if (n > SSIZE_MAX)' condition might trigger, or not, depending on the
> exact value of the original off_t value, resulting in unreliable
> behaviour.
D'oh. Yes, you are right. It's too late for such a check in uiomove().
Let's go with your second diff then.
> Unfortunately, I think the best we can do is to audit and fix all
> uiomove() callers, which we already do for the purpose of the
> conversion.
>
> cheers,
> natano
>
>
> Index: tmpfs_subr.c
> ===================================================================
> RCS file: /cvs/src/sys/tmpfs/tmpfs_subr.c,v
> retrieving revision 1.14
> diff -u -p -u -r1.14 tmpfs_subr.c
> --- tmpfs_subr.c 17 Apr 2015 04:43:21 -0000 1.14
> +++ tmpfs_subr.c 17 Jan 2016 12:05:59 -0000
> @@ -740,7 +740,7 @@ tmpfs_dir_getdotents(tmpfs_node_t *node,
> return EJUSTRETURN;
> }
>
> - if ((error = uiomovei(dp, dp->d_reclen, uio)) != 0) {
> + if ((error = uiomove(dp, dp->d_reclen, uio)) != 0) {
> return error;
> }
>
> @@ -837,7 +837,7 @@ tmpfs_dir_getdents(tmpfs_node_t *node, s
> }
>
> /* Copy out the directory entry and continue. */
> - error = uiomovei(&dent, dent.d_reclen, uio);
> + error = uiomove(&dent, dent.d_reclen, uio);
> if (error) {
> break;
> }
> @@ -1225,7 +1225,7 @@ tmpfs_uiomove(tmpfs_node_t *node, struct
> if (pgoff + len < PAGE_SIZE) {
> va = tmpfs_uio_lookup(node, pgnum);
> if (va != (vaddr_t)NULL)
> - return uiomovei((void *)va + pgoff, len, uio);
> + return uiomove((void *)va + pgoff, len, uio);
> }
>
> if (len >= TMPFS_UIO_MAXBYTES) {
> @@ -1249,7 +1249,7 @@ tmpfs_uiomove(tmpfs_node_t *node, struct
> return error;
> }
>
> - error = uiomovei((void *)va + pgoff, sz, uio);
> + error = uiomove((void *)va + pgoff, sz, uio);
> if (error == 0 && pgoff + sz < PAGE_SIZE)
> tmpfs_uio_cache(node, pgnum, va);
> else
> Index: tmpfs_vnops.c
> ===================================================================
> RCS file: /cvs/src/sys/tmpfs/tmpfs_vnops.c,v
> retrieving revision 1.23
> diff -u -p -u -r1.23 tmpfs_vnops.c
> --- tmpfs_vnops.c 8 Dec 2015 15:26:25 -0000 1.23
> +++ tmpfs_vnops.c 17 Jan 2016 12:06:12 -0000
> @@ -1017,8 +1017,8 @@ tmpfs_readlink(void *v)
> KASSERT(vp->v_type == VLNK);
>
> node = VP_TO_TMPFS_NODE(vp);
> - error = uiomovei(node->tn_spec.tn_lnk.tn_link,
> - MIN(node->tn_size, uio->uio_resid), uio);
> + error = uiomove(node->tn_spec.tn_lnk.tn_link,
> + MIN((size_t)node->tn_size, uio->uio_resid), uio);
> tmpfs_update(node, TMPFS_NODE_ACCESSED);
>
> return error;