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.
This commit is contained in:
Augustin Cavalier 2022-06-03 16:32:11 -04:00
parent e52da6c73b
commit 00f1e7c5e4
4 changed files with 57 additions and 45 deletions

View File

@ -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 <KernelExport.h>
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

View File

@ -23,6 +23,7 @@
#include <syscall_restart.h>
#include <slab/Slab.h>
#include <util/AutoLock.h>
#include <util/iovec_support.h>
#include <vfs.h>
#include <wait_for_objects.h>
@ -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) {

View File

@ -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 <lock.h>
#include <syscall_restart.h>
#include <util/AutoLock.h>
#include <util/iovec_support.h>
#include <vfs.h>
#include <net_stack_interface.h>
@ -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;

View File

@ -33,6 +33,7 @@
#include <tracing.h>
#include <util/AutoLock.h>
#include <util/list.h>
#include <util/iovec_support.h>
#include <vm/vm.h>
#include <wait_for_objects.h>
@ -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,