From 00f1e7c5e40b41f892625b720c6a032b20f0c405 Mon Sep 17 00:00:00 2001 From: Augustin Cavalier Date: Fri, 3 Jun 2022 16:32:11 -0400 Subject: [PATCH] kernel: Rework iovec copying from userland. Create a utility function which performs all necessary checks, allocates memory, and copies the structures, and then make use of it in the three places in the kernel which did all this manually. None of them were previously complete: the fd and socket code only checked iov_base and not iov_len, while the port code did not check anything at all. Part of #14961. --- headers/private/kernel/util/iovec_support.h | 41 +++++++++++++++++++++ src/system/kernel/fs/fd.cpp | 26 +++---------- src/system/kernel/fs/socket.cpp | 21 +++-------- src/system/kernel/port.cpp | 14 +++---- 4 files changed, 57 insertions(+), 45 deletions(-) create mode 100644 headers/private/kernel/util/iovec_support.h diff --git a/headers/private/kernel/util/iovec_support.h b/headers/private/kernel/util/iovec_support.h new file mode 100644 index 0000000000..9482d10af5 --- /dev/null +++ b/headers/private/kernel/util/iovec_support.h @@ -0,0 +1,41 @@ +/* + * Copyright 2022, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT license. + */ +#ifndef _UTIL_IOVEC_SUPPORT_H +#define _UTIL_IOVEC_SUPPORT_H + + +#include + + +static inline status_t +get_iovecs_from_user(const iovec* userVecs, size_t vecCount, iovec*& vecs, + bool permitNull = false) +{ + // prevent integer overflow + if (vecCount > IOV_MAX) + return B_BAD_VALUE; + + if (!IS_USER_ADDRESS(userVecs)) + return B_BAD_ADDRESS; + + vecs = (iovec*)malloc(sizeof(iovec) * vecCount); + if (vecs == NULL) + return B_NO_MEMORY; + + if (user_memcpy(vecs, userVecs, sizeof(iovec) * vecCount) != B_OK) + return B_BAD_ADDRESS; + + for (size_t i = 0; i < vecCount; i++) { + if (permitNull && vecs[i].iov_base == NULL) + continue; + if (!is_user_address_range(vecs[i].iov_base, vecs[i].iov_len)) + return B_BAD_ADDRESS; + } + + return B_OK; +} + + +#endif // _UTIL_IOVEC_SUPPORT_H diff --git a/src/system/kernel/fs/fd.cpp b/src/system/kernel/fs/fd.cpp index 905414ff5b..7f528563b0 100644 --- a/src/system/kernel/fs/fd.cpp +++ b/src/system/kernel/fs/fd.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -785,15 +786,14 @@ static ssize_t common_user_vector_io(int fd, off_t pos, const iovec* userVecs, size_t count, bool write) { - if (!IS_USER_ADDRESS(userVecs)) - return B_BAD_ADDRESS; - if (pos < -1) return B_BAD_VALUE; - // prevent integer overflow exploit in malloc() - if (count > IOV_MAX) - return B_BAD_VALUE; + iovec* vecs; + status_t error = get_iovecs_from_user(userVecs, count, vecs, true); + if (error != B_OK) + return error; + MemoryDeleter _(vecs); FDGetter fdGetter; struct file_descriptor* descriptor = fdGetter.SetTo(fd, false); @@ -805,14 +805,6 @@ common_user_vector_io(int fd, off_t pos, const iovec* userVecs, size_t count, return B_FILE_ERROR; } - iovec* vecs = (iovec*)malloc(sizeof(iovec) * count); - if (vecs == NULL) - return B_NO_MEMORY; - MemoryDeleter _(vecs); - - if (user_memcpy(vecs, userVecs, sizeof(iovec) * count) != B_OK) - return B_BAD_ADDRESS; - bool movePosition = false; if (pos == -1) { pos = descriptor->pos; @@ -830,12 +822,6 @@ common_user_vector_io(int fd, off_t pos, const iovec* userVecs, size_t count, for (uint32 i = 0; i < count; i++) { if (vecs[i].iov_base == NULL) continue; - if (!IS_USER_ADDRESS(vecs[i].iov_base)) { - status = B_BAD_ADDRESS; - if (bytesTransferred == 0) - return status; - break; - } size_t length = vecs[i].iov_len; if (write) { diff --git a/src/system/kernel/fs/socket.cpp b/src/system/kernel/fs/socket.cpp index f234e92f93..a587eaa403 100644 --- a/src/system/kernel/fs/socket.cpp +++ b/src/system/kernel/fs/socket.cpp @@ -1,7 +1,6 @@ /* * Copyright 2009-2010, Axel Dörfler, axeld@pinc-software.de. * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. - * * Distributed under the terms of the MIT License. */ @@ -22,6 +21,7 @@ #include #include #include +#include #include #include @@ -161,22 +161,11 @@ prepare_userland_msghdr(const msghdr* userMessage, msghdr& message, if (message.msg_iovlen < 0 || message.msg_iovlen > IOV_MAX) return EMSGSIZE; if (userVecs != NULL && message.msg_iovlen > 0) { - iovec* vecs = (iovec*)malloc(sizeof(iovec) * message.msg_iovlen); - if (vecs == NULL) - return B_NO_MEMORY; + iovec* vecs; + status_t error = get_iovecs_from_user(message.msg_iov, message.msg_iovlen, vecs); + if (error != B_OK) + return error; vecsDeleter.SetTo(vecs); - - if (!IS_USER_ADDRESS(message.msg_iov) - || user_memcpy(vecs, message.msg_iov, - message.msg_iovlen * sizeof(iovec)) != B_OK) { - return B_BAD_ADDRESS; - } - - for (int i = 0; i < message.msg_iovlen; i++) { - if (!IS_USER_ADDRESS(vecs[i].iov_base)) - return B_BAD_ADDRESS; - } - message.msg_iov = vecs; } else { message.msg_iov = NULL; diff --git a/src/system/kernel/port.cpp b/src/system/kernel/port.cpp index 8e6d65cfd8..cacc36a330 100644 --- a/src/system/kernel/port.cpp +++ b/src/system/kernel/port.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -1923,15 +1924,10 @@ _user_writev_port_etc(port_id port, int32 messageCode, const iovec *userVecs, return B_BAD_ADDRESS; iovec *vecs = NULL; - if (userVecs && vecCount != 0) { - vecs = (iovec*)malloc(sizeof(iovec) * vecCount); - if (vecs == NULL) - return B_NO_MEMORY; - - if (user_memcpy(vecs, userVecs, sizeof(iovec) * vecCount) < B_OK) { - free(vecs); - return B_BAD_ADDRESS; - } + if (userVecs != NULL && vecCount != 0) { + status_t status = get_iovecs_from_user(userVecs, vecCount, vecs); + if (status != B_OK) + return status; } status_t status = writev_port_etc(port, messageCode, vecs, vecCount,