From b3fb473977c6bfb5294f229336a23d2071d565f8 Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Tue, 29 Jul 2025 01:41:19 -0400 Subject: kernel: uio: Add iovec copyin/copyout routines Introduce copyin() and copyout() type functions made for iovec/uio operation. Signed-off-by: Ian Moffett --- sys/kern/kern_uio.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) (limited to 'sys/kern/kern_uio.c') diff --git a/sys/kern/kern_uio.c b/sys/kern/kern_uio.c index dd533c2..2ec1532 100644 --- a/sys/kern/kern_uio.c +++ b/sys/kern/kern_uio.c @@ -28,11 +28,31 @@ */ #include +#include #include #include #include #include +/* + * Clean up after a UIO copyin() operation + * + * @iov: iovec copy to clean up + * @iovcnt: Number of iovec entries + */ +void +uio_copyin_clean(struct iovec *iov, int iovcnt) +{ + for (int i = 0; i < iovcnt; ++i) { + if (iov[i].iov_base == NULL) { + continue; + } + + dynfree(iov[i].iov_base); + iov[i].iov_base = NULL; + } +} + /* * Read data into POSIX.1‐2017 iovec * @@ -154,3 +174,99 @@ writev(int filedes, const struct iovec *iov, int iovcnt) return bytes_written; } + +/* + * Validate iovecs coming in from userland + * and copy it to a kernel buffer. + * + * XXX: A new buffer is allocated in k_iov[i]->iov_base + * and must be freed with dynfree() after use. + * + * @u_iov: Userspace source iovecs + * @k_iov: Kernel destination iovec + * @iovcnt: Number of iovecs to copy + */ +int +uio_copyin(const struct iovec *u_iov, struct iovec *k_iov, int iovcnt) +{ + struct iovec *iov_dest; + const struct iovec *iov_src; + size_t len; + void *old_base; + int error; + + if (u_iov == NULL || k_iov == NULL) { + return -EINVAL; + } + + for (int i = 0; i < iovcnt; ++i) { + iov_dest = &k_iov[i]; + iov_src = &u_iov[i]; + error = copyin(iov_src, iov_dest, sizeof(*iov_dest)); + + if (error < 0) { + uio_copyin_clean(iov_dest, i + 1); + return error; + } + + /* + * Save the old base so that we may copy the data to + * the new kernel buffer. First we'd need to allocate + * one of course. + */ + old_base = iov_dest->iov_base; + len = iov_dest->iov_len; + iov_dest->iov_base = dynalloc(len); + + /* Did it fail? */ + if (iov_dest->iov_base == NULL) { + uio_copyin_clean(iov_dest, i + 1); + return -ENOMEM; + } + + /* Copy actual data in */ + error = copyin(old_base, iov_dest->iov_base, len); + if (error < 0) { + uio_copyin_clean(iov_dest, i + 1); + return error; + } + } + + return 0; +} + + +/* + * Validate iovecs going out from kernel space (us) + * before actually sending it out. + * + * @k_iov: Kernel iovec to copyout + * @u_iov: Userspace destination + * @iovcnt: Number of iovecs + */ +int +uio_copyout(const struct iovec *k_iov, struct iovec *u_iov, int iovcnt) +{ + struct iovec iov_shadow, *iov_dest; + const struct iovec *iov_src; + int error; + + for (int i = 0; i < iovcnt; ++i) { + iov_dest = &u_iov[i]; + iov_src = &k_iov[i]; + + /* Grab a shadow copy */ + error = copyin(iov_src, &iov_shadow, sizeof(iov_shadow)); + if (error < 0) { + return error; + } + + /* Copy out actual data */ + error = copyout(iov_src->iov_base, iov_dest->iov_base, iov_dest->iov_len); + if (error < 0) { + return error; + } + } + + return 0; +} -- cgit v1.2.3