From 458e758f3792ef11ca26d6ff7e24600c88326e83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Sat, 19 May 2018 19:33:27 +0200 Subject: [PATCH] kernel/x86_64: compatibility syscalls for vm.cpp. * define compat_area_info to be used when applicable in compatibility mode. * handle 32-bit types in _user_reserve_address_range(), _user_get_area_info(), _user_get_next_area_info(), _user_transfer_area(), _user_clone_area(), _user_create_area(), _user_map_file(), other syscalls are compatible as is. * _get_next_area_info() doesn't work well with a 32-bit address cookie (address could be in 64-bit range). Instead use _compat_get_next_area_info() which uses the area id as cookie, though the areas are not ordered by address any more. Change-Id: Ic7519ca8824aa2d534b0f03ea75a1bf6ae321535 --- headers/private/kernel/compat/OS_compat.h | 179 ++++++++++++++++++++++ src/system/kernel/vm/vm.cpp | 139 ++++++++++++----- 2 files changed, 279 insertions(+), 39 deletions(-) diff --git a/headers/private/kernel/compat/OS_compat.h b/headers/private/kernel/compat/OS_compat.h index 8b32a1bc22..0fbc7a952a 100644 --- a/headers/private/kernel/compat/OS_compat.h +++ b/headers/private/kernel/compat/OS_compat.h @@ -95,4 +95,183 @@ copy_ref_var_to_user(system_info &info, system_info* userInfo) } +#define compat_size_t uint32 +#define compat_ptr_t uint32 +typedef struct compat_area_info { + area_id area; + char name[B_OS_NAME_LENGTH]; + compat_size_t size; + uint32 lock; + uint32 protection; + team_id team; + uint32 ram_size; + uint32 copy_count; + uint32 in_count; + uint32 out_count; + compat_ptr_t address; +} _PACKED compat_area_info; + + +static_assert(sizeof(compat_area_info) == 0x48, + "size of compat_area_info mismatch"); + + +inline status_t +copy_ref_var_to_user(area_info &info, area_info* userInfo) +{ + if (!IS_USER_ADDRESS(userInfo)) + return B_BAD_ADDRESS; + Thread* thread = thread_get_current_thread(); + bool compatMode = (thread->flags & THREAD_FLAGS_COMPAT_MODE) != 0; + if (compatMode) { + compat_area_info compatInfo; + compatInfo.area = info.area; + strlcpy(compatInfo.name, info.name, B_OS_NAME_LENGTH); + compatInfo.size = info.size; + compatInfo.lock = info.lock; + compatInfo.protection = info.protection; + compatInfo.team = info.team; + compatInfo.ram_size = info.ram_size; + compatInfo.copy_count = info.copy_count; + compatInfo.in_count = info.in_count; + compatInfo.out_count = info.out_count; + compatInfo.address = (compat_ptr_t)(addr_t)info.address; + if (user_memcpy(userInfo, &compatInfo, sizeof(compatInfo)) < B_OK) + return B_BAD_ADDRESS; + } else if (user_memcpy(userInfo, &info, sizeof(info)) < B_OK) { + return B_BAD_ADDRESS; + } + return B_OK; +} + + +typedef struct { + thread_id thread; + team_id team; + char name[B_OS_NAME_LENGTH]; + thread_state state; + int32 priority; + sem_id sem; + bigtime_t user_time; + bigtime_t kernel_time; + uint32 stack_base; + uint32 stack_end; +} _PACKED compat_thread_info; + + +static_assert(sizeof(compat_thread_info) == 76, + "size of compat_thread_info mismatch"); + + +inline status_t +copy_ref_var_to_user(void* &addr, void** userAddr) +{ + if (!IS_USER_ADDRESS(userAddr)) + return B_BAD_ADDRESS; + Thread* thread = thread_get_current_thread(); + bool compatMode = (thread->flags & THREAD_FLAGS_COMPAT_MODE) != 0; + if (compatMode) { + compat_ptr_t compatAddr = (uint32)(addr_t)addr; + if (user_memcpy(userAddr, &compatAddr, sizeof(compatAddr)) < B_OK) + return B_BAD_ADDRESS; + } else if (user_memcpy(userAddr, &addr, sizeof(addr)) < B_OK) { + return B_BAD_ADDRESS; + } + return B_OK; +} + + +inline status_t +copy_ref_var_from_user(void** userAddr, void* &addr) +{ + if (!IS_USER_ADDRESS(userAddr)) + return B_BAD_ADDRESS; + Thread* thread = thread_get_current_thread(); + bool compatMode = (thread->flags & THREAD_FLAGS_COMPAT_MODE) != 0; + if (compatMode) { + compat_ptr_t compatAddr; + if (user_memcpy(&compatAddr, userAddr, sizeof(compatAddr)) < B_OK) + return B_BAD_ADDRESS; + addr = (void*)(addr_t)compatAddr; + } else if (user_memcpy(&addr, userAddr, sizeof(addr)) < B_OK) { + return B_BAD_ADDRESS; + } + return B_OK; +} + + +inline status_t +copy_ref_var_to_user(addr_t &addr, addr_t* userAddr) +{ + if (!IS_USER_ADDRESS(userAddr)) + return B_BAD_ADDRESS; + Thread* thread = thread_get_current_thread(); + bool compatMode = (thread->flags & THREAD_FLAGS_COMPAT_MODE) != 0; + if (compatMode) { + uint32 compatAddr = (uint32)addr; + if (user_memcpy(userAddr, &compatAddr, sizeof(compatAddr)) < B_OK) + return B_BAD_ADDRESS; + } else if (user_memcpy(userAddr, &addr, sizeof(addr)) < B_OK) { + return B_BAD_ADDRESS; + } + return B_OK; +} + + +inline status_t +copy_ref_var_from_user(addr_t* userAddr, addr_t &addr) +{ + if (!IS_USER_ADDRESS(userAddr)) + return B_BAD_ADDRESS; + Thread* thread = thread_get_current_thread(); + bool compatMode = (thread->flags & THREAD_FLAGS_COMPAT_MODE) != 0; + if (compatMode) { + uint32 compatAddr; + if (user_memcpy(&compatAddr, userAddr, sizeof(compatAddr)) < B_OK) + return B_BAD_ADDRESS; + addr = (addr_t)compatAddr; + } else if (user_memcpy(&addr, userAddr, sizeof(addr)) < B_OK) { + return B_BAD_ADDRESS; + } + return B_OK; +} + + +inline status_t +copy_ref_var_to_user(ssize_t &size, ssize_t* userSize) +{ + if (!IS_USER_ADDRESS(userSize)) + return B_BAD_ADDRESS; + Thread* thread = thread_get_current_thread(); + bool compatMode = (thread->flags & THREAD_FLAGS_COMPAT_MODE) != 0; + if (compatMode) { + int32 compatSize = (int32)size; + if (user_memcpy(userSize, &compatSize, sizeof(compatSize)) < B_OK) + return B_BAD_ADDRESS; + } else if (user_memcpy(userSize, &size, sizeof(size)) < B_OK) { + return B_BAD_ADDRESS; + } + return B_OK; +} + + +inline status_t +copy_ref_var_from_user(ssize_t* userSize, ssize_t &size) +{ + if (!IS_USER_ADDRESS(userSize)) + return B_BAD_ADDRESS; + Thread* thread = thread_get_current_thread(); + bool compatMode = (thread->flags & THREAD_FLAGS_COMPAT_MODE) != 0; + if (compatMode) { + int32 compatSize; + if (user_memcpy(&compatSize, userSize, sizeof(compatSize)) < B_OK) + return B_BAD_ADDRESS; + size = (ssize_t)compatSize; + } else if (user_memcpy(&size, userSize, sizeof(size)) < B_OK) { + return B_BAD_ADDRESS; + } + return B_OK; +} + + #endif // _KERNEL_COMPAT_OS_H diff --git a/src/system/kernel/vm/vm.cpp b/src/system/kernel/vm/vm.cpp index 478a0e5ba7..a5b961b1e9 100644 --- a/src/system/kernel/vm/vm.cpp +++ b/src/system/kernel/vm/vm.cpp @@ -47,12 +47,17 @@ #include #include #include +#include #include #include #include #include #include +#ifdef _COMPAT_MODE +# include +#endif + #include "VMAddressSpaceLocking.h" #include "VMAnonymousCache.h" #include "VMAnonymousNoSwapCache.h" @@ -6003,6 +6008,44 @@ _get_next_area_info(team_id team, ssize_t* cookie, area_info* info, size_t size) } +#ifdef _COMPAT_MODE +status_t +_compat_get_next_area_info(team_id team, ssize_t* cookie, area_info* info, size_t size) +{ + area_id nextID = *(area_id*)cookie; + + // we're already through the list + if (nextID == (area_id)-1) + return B_ENTRY_NOT_FOUND; + + if (team == B_CURRENT_TEAM) + team = team_get_current_team_id(); + + AddressSpaceReadLocker locker(team); + if (!locker.IsLocked()) + return B_BAD_TEAM_ID; + + VMArea* area; + for (VMAddressSpace::AreaIterator it + = locker.AddressSpace()->GetAreaIterator(); + (area = it.Next()) != NULL;) { + if (area->id > nextID) + break; + } + + if (area == NULL) { + nextID = (area_id)-1; + return B_ENTRY_NOT_FOUND; + } + + fill_area_info(area, info, size); + *cookie = (ssize_t)(area->id); + + return B_OK; +} +#endif + + status_t set_area_protection(area_id area, uint32 newProtection) { @@ -6132,23 +6175,22 @@ _user_reserve_address_range(addr_t* userAddress, uint32 addressSpec, addr_t address; - if (!IS_USER_ADDRESS(userAddress) - || user_memcpy(&address, userAddress, sizeof(address)) != B_OK) - return B_BAD_ADDRESS; + status_t status = copy_ref_var_from_user(userAddress, address); + if (status != B_OK) + return status; - status_t status = vm_reserve_address_range( + status = vm_reserve_address_range( VMAddressSpace::CurrentID(), (void**)&address, addressSpec, size, RESERVED_AVOID_BASE); if (status != B_OK) return status; - if (user_memcpy(userAddress, &address, sizeof(address)) != B_OK) { + status = copy_ref_var_to_user(address, userAddress); + if (status != B_OK) { vm_unreserve_address_range(VMAddressSpace::CurrentID(), (void*)address, size); - return B_BAD_ADDRESS; } - - return B_OK; + return status; } @@ -6194,10 +6236,8 @@ _user_get_area_info(area_id area, area_info* userInfo) // TODO: do we want to prevent userland from seeing kernel protections? //info.protection &= B_USER_PROTECTION; - if (user_memcpy(userInfo, &info, sizeof(area_info)) < B_OK) - return B_BAD_ADDRESS; + return copy_ref_var_to_user(info, userInfo); - return status; } @@ -6207,21 +6247,28 @@ _user_get_next_area_info(team_id team, ssize_t* userCookie, area_info* userInfo) ssize_t cookie; if (!IS_USER_ADDRESS(userCookie) - || !IS_USER_ADDRESS(userInfo) - || user_memcpy(&cookie, userCookie, sizeof(ssize_t)) < B_OK) + || !IS_USER_ADDRESS(userInfo)) return B_BAD_ADDRESS; + status_t status = copy_ref_var_from_user(userCookie, cookie); + if (status != B_OK) + return status; + area_info info; - status_t status = _get_next_area_info(team, &cookie, &info, +#ifdef _COMPAT_MODE + status = _compat_get_next_area_info(team, &cookie, &info, +#else + status = _get_next_area_info(team, &cookie, &info, +#endif sizeof(area_info)); if (status != B_OK) return status; //info.protection &= B_USER_PROTECTION; - if (user_memcpy(userCookie, &cookie, sizeof(ssize_t)) < B_OK - || user_memcpy(userInfo, &info, sizeof(area_info)) < B_OK) - return B_BAD_ADDRESS; + status = copy_ref_var_to_user(cookie, userCookie); + if (status == B_OK) + status = copy_ref_var_to_user(info, userInfo); return status; } @@ -6259,16 +6306,17 @@ _user_transfer_area(area_id area, void** userAddress, uint32 addressSpec, } void* address; - if (!IS_USER_ADDRESS(userAddress) - || user_memcpy(&address, userAddress, sizeof(address)) < B_OK) - return B_BAD_ADDRESS; + status_t status = copy_ref_var_from_user(userAddress, address); + if (status != B_OK) + return status; area_id newArea = transfer_area(area, &address, addressSpec, target, false); if (newArea < B_OK) return newArea; - if (user_memcpy(userAddress, &address, sizeof(address)) < B_OK) - return B_BAD_ADDRESS; + status = copy_ref_var_to_user(address, userAddress); + if (status != B_OK) + return status; return newArea; } @@ -6291,11 +6339,13 @@ _user_clone_area(const char* userName, void** userAddress, uint32 addressSpec, return B_BAD_VALUE; if (!IS_USER_ADDRESS(userName) - || !IS_USER_ADDRESS(userAddress) - || user_strlcpy(name, userName, sizeof(name)) < B_OK - || user_memcpy(&address, userAddress, sizeof(address)) < B_OK) + || user_strlcpy(name, userName, sizeof(name)) < B_OK) return B_BAD_ADDRESS; + status_t status = copy_ref_var_from_user(userAddress, address); + if (status != B_OK) + return status; + fix_protection(&protection); area_id clonedArea = vm_clone_area(VMAddressSpace::CurrentID(), name, @@ -6304,9 +6354,10 @@ _user_clone_area(const char* userName, void** userAddress, uint32 addressSpec, if (clonedArea < B_OK) return clonedArea; - if (user_memcpy(userAddress, &address, sizeof(address)) < B_OK) { + status = copy_ref_var_to_user(address, userAddress); + if (status < B_OK) { delete_area(clonedArea); - return B_BAD_ADDRESS; + return status; } return clonedArea; @@ -6330,11 +6381,13 @@ _user_create_area(const char* userName, void** userAddress, uint32 addressSpec, return B_BAD_VALUE; if (!IS_USER_ADDRESS(userName) - || !IS_USER_ADDRESS(userAddress) - || user_strlcpy(name, userName, sizeof(name)) < B_OK - || user_memcpy(&address, userAddress, sizeof(address)) < B_OK) + || user_strlcpy(name, userName, sizeof(name)) < B_OK) return B_BAD_ADDRESS; + status_t status = copy_ref_var_from_user(userAddress, address); + if (status != B_OK) + return status; + if (addressSpec == B_EXACT_ADDRESS && IS_KERNEL_ADDRESS(address)) return B_BAD_VALUE; @@ -6354,10 +6407,12 @@ _user_create_area(const char* userName, void** userAddress, uint32 addressSpec, size, lock, protection, 0, 0, &virtualRestrictions, &physicalRestrictions, false, &address); - if (area >= B_OK - && user_memcpy(userAddress, &address, sizeof(address)) < B_OK) { - delete_area(area); - return B_BAD_ADDRESS; + if (area >= B_OK) { + status = copy_ref_var_to_user(address, userAddress); + if (status < B_OK) { + delete_area(area); + return status; + } } return area; @@ -6391,11 +6446,14 @@ _user_map_file(const char* userName, void** userAddress, uint32 addressSpec, fix_protection(&protection); - if (!IS_USER_ADDRESS(userName) || !IS_USER_ADDRESS(userAddress) - || user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK - || user_memcpy(&address, userAddress, sizeof(address)) < B_OK) + if (!IS_USER_ADDRESS(userName) + || user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK) return B_BAD_ADDRESS; + status_t status = copy_ref_var_from_user(userAddress, address); + if (status != B_OK) + return status; + if (addressSpec == B_EXACT_ADDRESS) { if ((addr_t)address + size < (addr_t)address || (addr_t)address % B_PAGE_SIZE != 0) { @@ -6413,8 +6471,11 @@ _user_map_file(const char* userName, void** userAddress, uint32 addressSpec, if (area < B_OK) return area; - if (user_memcpy(userAddress, &address, sizeof(address)) < B_OK) - return B_BAD_ADDRESS; + status = copy_ref_var_to_user(address, userAddress); + if (status < B_OK) { + delete_area(area); + return status; + } return area; }