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
This commit is contained in:
Jérôme Duval
2018-06-12 17:56:55 +02:00
parent ce5eb94a82
commit 458e758f37
2 changed files with 279 additions and 39 deletions
+179
View File
@@ -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
+100 -39
View File
@@ -47,12 +47,17 @@
#include <team.h>
#include <tracing.h>
#include <util/AutoLock.h>
#include <util/syscall_args.h>
#include <vm/vm_page.h>
#include <vm/vm_priv.h>
#include <vm/VMAddressSpace.h>
#include <vm/VMArea.h>
#include <vm/VMCache.h>
#ifdef _COMPAT_MODE
# include <OS_compat.h>
#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;
}