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:
parent
e52da6c73b
commit
00f1e7c5e4
41
headers/private/kernel/util/iovec_support.h
Normal file
41
headers/private/kernel/util/iovec_support.h
Normal 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
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user