vm: fix off by one errors
Various functions would return an error if trying to use them on the last page of userspace, as they tested the permissions for the first byte out of the requested range. Also, the code was duplicated in several places - Rename validate_user_range to validate_memory_range. The "user" in the name was to mean that the range is provided by the user calling the syscall, but it is a bit confusing, as the function accepts any range that is either in kernel or in user memory. - Add a new function validate_user_memory_range that only accepts userspace memory, and use this one where appropriate. Change-Id: I135f8d584340f0ba4ae7e4b8cb6f8600fbf3ef2d Reviewed-on: https://review.haiku-os.org/c/haiku/+/4212 Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org> Reviewed-by: Jérôme Duval <jerome.duval@gmail.com>
This commit is contained in:
parent
a94ee3f35a
commit
71f3ec460f
@ -5420,8 +5420,10 @@ vm_debug_copy_page_memory(team_id teamID, void* unsafeMemory, void* buffer,
|
||||
}
|
||||
|
||||
|
||||
/** Validate that a memory range is either fully in kernel space, or fully in
|
||||
* userspace */
|
||||
static inline bool
|
||||
validate_user_range(const void* addr, size_t size)
|
||||
validate_memory_range(const void* addr, size_t size)
|
||||
{
|
||||
addr_t address = (addr_t)addr;
|
||||
|
||||
@ -5429,11 +5431,23 @@ validate_user_range(const void* addr, size_t size)
|
||||
if ((address + size) < address)
|
||||
return false;
|
||||
|
||||
// Validate that the address does not cross the kernel/user boundary.
|
||||
if (IS_USER_ADDRESS(address))
|
||||
return IS_USER_ADDRESS(address + size);
|
||||
else
|
||||
return !IS_USER_ADDRESS(address + size);
|
||||
// Validate that the address range does not cross the kernel/user boundary.
|
||||
return IS_USER_ADDRESS(address) == IS_USER_ADDRESS(address + size - 1);
|
||||
}
|
||||
|
||||
|
||||
/** Validate that a memory range is fully in userspace. */
|
||||
static inline bool
|
||||
validate_user_memory_range(const void* addr, size_t size)
|
||||
{
|
||||
addr_t address = (addr_t)addr;
|
||||
|
||||
// Check for overflows on all addresses.
|
||||
if ((address + size) < address)
|
||||
return false;
|
||||
|
||||
// Validate that both the start and end address are in userspace
|
||||
return IS_USER_ADDRESS(address) && IS_USER_ADDRESS(address + size - 1);
|
||||
}
|
||||
|
||||
|
||||
@ -5443,7 +5457,7 @@ validate_user_range(const void* addr, size_t size)
|
||||
status_t
|
||||
user_memcpy(void* to, const void* from, size_t size)
|
||||
{
|
||||
if (!validate_user_range(to, size) || !validate_user_range(from, size))
|
||||
if (!validate_memory_range(to, size) || !validate_memory_range(from, size))
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
if (arch_cpu_user_memcpy(to, from, size) < B_OK)
|
||||
@ -5477,7 +5491,7 @@ user_strlcpy(char* to, const char* from, size_t size)
|
||||
if (IS_USER_ADDRESS(from) && !IS_USER_ADDRESS((addr_t)from + maxSize))
|
||||
maxSize = USER_TOP - (addr_t)from;
|
||||
|
||||
if (!validate_user_range(to, maxSize))
|
||||
if (!validate_memory_range(to, maxSize))
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
ssize_t result = arch_cpu_user_strlcpy(to, from, maxSize);
|
||||
@ -5495,7 +5509,7 @@ user_strlcpy(char* to, const char* from, size_t size)
|
||||
status_t
|
||||
user_memset(void* s, char c, size_t count)
|
||||
{
|
||||
if (!validate_user_range(s, count))
|
||||
if (!validate_memory_range(s, count))
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
if (arch_cpu_user_memset(s, c, count) < B_OK)
|
||||
@ -6587,8 +6601,10 @@ _user_unmap_memory(void* _address, size_t size)
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
if (!IS_USER_ADDRESS(address) || !IS_USER_ADDRESS((addr_t)address + size))
|
||||
if (!IS_USER_ADDRESS(address)
|
||||
|| !IS_USER_ADDRESS((addr_t)address + size - 1)) {
|
||||
return B_BAD_ADDRESS;
|
||||
}
|
||||
|
||||
// Write lock the address space and ensure the address range is not wired.
|
||||
AddressSpaceWriteLocker locker;
|
||||
@ -6613,8 +6629,7 @@ _user_set_memory_protection(void* _address, size_t size, uint32 protection)
|
||||
|
||||
if ((address % B_PAGE_SIZE) != 0)
|
||||
return B_BAD_VALUE;
|
||||
if ((addr_t)address + size < (addr_t)address || !IS_USER_ADDRESS(address)
|
||||
|| !IS_USER_ADDRESS((addr_t)address + size)) {
|
||||
if (!validate_user_memory_range(_address, size)) {
|
||||
// weird error code required by POSIX
|
||||
return ENOMEM;
|
||||
}
|
||||
@ -6758,8 +6773,7 @@ _user_sync_memory(void* _address, size_t size, uint32 flags)
|
||||
// check params
|
||||
if ((address % B_PAGE_SIZE) != 0)
|
||||
return B_BAD_VALUE;
|
||||
if ((addr_t)address + size < (addr_t)address || !IS_USER_ADDRESS(address)
|
||||
|| !IS_USER_ADDRESS((addr_t)address + size)) {
|
||||
if (!validate_user_memory_range(_address, size)) {
|
||||
// weird error code required by POSIX
|
||||
return ENOMEM;
|
||||
}
|
||||
@ -6837,8 +6851,7 @@ _user_memory_advice(void* _address, size_t size, uint32 advice)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
if (address + size < address || !IS_USER_ADDRESS(address)
|
||||
|| !IS_USER_ADDRESS(address + size)) {
|
||||
if (!validate_user_memory_range(_address, size)) {
|
||||
// weird error code required by POSIX
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user