* Implemented mprotect(). A vm_area does now have an optional array
specifying the protection of each page (4 bits per page). * Added no-op implementation of posix_madvise(). * Replaced a few "addr_t size" parameters by "size_t size". git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@26871 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -9,7 +9,8 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
|
||||||
/* memory protection for mmap() and others */
|
/* memory protection for mmap() and others; assignment compatible with
|
||||||
|
B_{READ,WRITE,EXECUTE}_AREA */
|
||||||
#define PROT_READ 0x01
|
#define PROT_READ 0x01
|
||||||
#define PROT_WRITE 0x02
|
#define PROT_WRITE 0x02
|
||||||
#define PROT_EXEC 0x04
|
#define PROT_EXEC 0x04
|
||||||
@@ -30,6 +31,13 @@
|
|||||||
#define MS_SYNC 0x02
|
#define MS_SYNC 0x02
|
||||||
#define MS_INVALIDATE 0x04
|
#define MS_INVALIDATE 0x04
|
||||||
|
|
||||||
|
/* posix_madvise() values */
|
||||||
|
#define POSIX_MADV_NORMAL 1
|
||||||
|
#define POSIX_MADV_SEQUENTIAL 2
|
||||||
|
#define POSIX_MADV_RANDOM 3
|
||||||
|
#define POSIX_MADV_WILLNEED 4
|
||||||
|
#define POSIX_MADV_DONTNEED 5
|
||||||
|
|
||||||
|
|
||||||
__BEGIN_DECLS
|
__BEGIN_DECLS
|
||||||
|
|
||||||
@@ -37,8 +45,11 @@ void* mmap(void* address, size_t length, int protection, int flags,
|
|||||||
int fd, off_t offset);
|
int fd, off_t offset);
|
||||||
int munmap(void* address, size_t length);
|
int munmap(void* address, size_t length);
|
||||||
|
|
||||||
|
int mprotect(void* address, size_t length, int protection);
|
||||||
int msync(void* address, size_t length, int flags);
|
int msync(void* address, size_t length, int flags);
|
||||||
|
|
||||||
|
int posix_madvise(void* address, size_t length, int advice);
|
||||||
|
|
||||||
int shm_open(const char* name, int openMode, mode_t permissions);
|
int shm_open(const char* name, int openMode, mode_t permissions);
|
||||||
int shm_unlink(const char* name);
|
int shm_unlink(const char* name);
|
||||||
|
|
||||||
|
|||||||
@@ -93,10 +93,15 @@ off_t vm_available_not_needed_memory(void);
|
|||||||
area_id _user_create_area(const char *name, void **address, uint32 addressSpec,
|
area_id _user_create_area(const char *name, void **address, uint32 addressSpec,
|
||||||
size_t size, uint32 lock, uint32 protection);
|
size_t size, uint32 lock, uint32 protection);
|
||||||
status_t _user_delete_area(area_id area);
|
status_t _user_delete_area(area_id area);
|
||||||
|
|
||||||
area_id _user_map_file(const char *uname, void **uaddress, int addressSpec,
|
area_id _user_map_file(const char *uname, void **uaddress, int addressSpec,
|
||||||
addr_t size, int protection, int mapping, int fd, off_t offset);
|
size_t size, int protection, int mapping, int fd, off_t offset);
|
||||||
status_t _user_unmap_memory(void *address, addr_t size);
|
status_t _user_unmap_memory(void *address, size_t size);
|
||||||
status_t _user_sync_memory(void *address, addr_t size, int flags);
|
status_t _user_set_memory_protection(void* address, size_t size,
|
||||||
|
int protection);
|
||||||
|
status_t _user_sync_memory(void *address, size_t size, int flags);
|
||||||
|
status_t _user_memory_advice(void* address, size_t size, int advice);
|
||||||
|
|
||||||
area_id _user_area_for(void *address);
|
area_id _user_area_for(void *address);
|
||||||
area_id _user_find_area(const char *name);
|
area_id _user_find_area(const char *name);
|
||||||
status_t _user_get_area_info(area_id area, area_info *info);
|
status_t _user_get_area_info(area_id area, area_info *info);
|
||||||
|
|||||||
@@ -301,6 +301,7 @@ struct vm_area {
|
|||||||
off_t cache_offset;
|
off_t cache_offset;
|
||||||
uint32 cache_type;
|
uint32 cache_type;
|
||||||
vm_area_mappings mappings;
|
vm_area_mappings mappings;
|
||||||
|
uint8 *page_protections;
|
||||||
|
|
||||||
struct vm_address_space *address_space;
|
struct vm_address_space *address_space;
|
||||||
struct vm_area *address_space_next;
|
struct vm_area *address_space_next;
|
||||||
|
|||||||
@@ -327,10 +327,14 @@ extern status_t _kern_reserve_heap_address_range(addr_t* _address, uint32 addre
|
|||||||
addr_t size);
|
addr_t size);
|
||||||
|
|
||||||
extern area_id _kern_map_file(const char *name, void **address,
|
extern area_id _kern_map_file(const char *name, void **address,
|
||||||
int addressSpec, addr_t size, int protection,
|
int addressSpec, size_t size, int protection,
|
||||||
int mapping, int fd, off_t offset);
|
int mapping, int fd, off_t offset);
|
||||||
extern status_t _kern_unmap_memory(void *address, addr_t size);
|
extern status_t _kern_unmap_memory(void *address, size_t size);
|
||||||
extern status_t _kern_sync_memory(void *address, addr_t size, int flags);
|
extern status_t _kern_set_memory_protection(void *address, size_t size,
|
||||||
|
int protection);
|
||||||
|
extern status_t _kern_sync_memory(void *address, size_t size, int flags);
|
||||||
|
extern status_t _kern_memory_advice(void *address, size_t size,
|
||||||
|
int advice);
|
||||||
|
|
||||||
/* kernel port functions */
|
/* kernel port functions */
|
||||||
extern port_id _kern_create_port(int32 queue_length, const char *name);
|
extern port_id _kern_create_port(int32 queue_length, const char *name);
|
||||||
|
|||||||
+184
-6
@@ -878,6 +878,7 @@ create_area_struct(vm_address_space *addressSpace, const char *name,
|
|||||||
area->cache_next = area->cache_prev = NULL;
|
area->cache_next = area->cache_prev = NULL;
|
||||||
area->hash_next = NULL;
|
area->hash_next = NULL;
|
||||||
new (&area->mappings) vm_area_mappings;
|
new (&area->mappings) vm_area_mappings;
|
||||||
|
area->page_protections = NULL;
|
||||||
|
|
||||||
return area;
|
return area;
|
||||||
}
|
}
|
||||||
@@ -1226,6 +1227,37 @@ insert_area(vm_address_space *addressSpace, void **_address,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
set_area_page_protection(vm_area* area, addr_t pageAddress, uint32 protection)
|
||||||
|
{
|
||||||
|
protection &= B_READ_AREA | B_WRITE_AREA | B_EXECUTE_AREA;
|
||||||
|
uint32 pageIndex = (pageAddress - area->base) / B_PAGE_SIZE;
|
||||||
|
uint8& entry = area->page_protections[pageIndex / 2];
|
||||||
|
if (pageIndex % 2 == 0)
|
||||||
|
entry = entry & 0xf0 | protection;
|
||||||
|
else
|
||||||
|
entry = entry & 0x0f | (protection << 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline uint32
|
||||||
|
get_area_page_protection(vm_area* area, addr_t pageAddress)
|
||||||
|
{
|
||||||
|
if (area->page_protections == NULL)
|
||||||
|
return area->protection;
|
||||||
|
|
||||||
|
uint32 pageIndex = (pageAddress - area->base) / B_PAGE_SIZE;
|
||||||
|
uint32 protection = area->page_protections[pageIndex / 2];
|
||||||
|
if (pageIndex % 2 == 0)
|
||||||
|
protection &= 0x0f;
|
||||||
|
else
|
||||||
|
protection >>= 4;
|
||||||
|
|
||||||
|
return protection | B_KERNEL_READ_AREA
|
||||||
|
| (protection & B_WRITE_AREA ? B_KERNEL_WRITE_AREA : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Cuts a piece out of an area. If the given cut range covers the complete
|
/*! Cuts a piece out of an area. If the given cut range covers the complete
|
||||||
area, it is deleted. If it covers the beginning or the end, the area is
|
area, it is deleted. If it covers the beginning or the end, the area is
|
||||||
resized accordingly. If the range covers some part in the middle of the
|
resized accordingly. If the range covers some part in the middle of the
|
||||||
@@ -2340,6 +2372,7 @@ delete_area(vm_address_space *addressSpace, vm_area *area)
|
|||||||
area->cache->RemoveArea(area);
|
area->cache->RemoveArea(area);
|
||||||
area->cache->ReleaseRef();
|
area->cache->ReleaseRef();
|
||||||
|
|
||||||
|
free(area->page_protections);
|
||||||
free(area->name);
|
free(area->name);
|
||||||
free(area);
|
free(area);
|
||||||
}
|
}
|
||||||
@@ -4603,11 +4636,13 @@ vm_soft_fault(vm_address_space *addressSpace, addr_t originalAddress,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check permissions
|
// check permissions
|
||||||
if (isUser && (area->protection & B_USER_PROTECTION) == 0) {
|
uint32 protection = get_area_page_protection(area, address);
|
||||||
|
if (isUser && (protection & B_USER_PROTECTION) == 0) {
|
||||||
dprintf("user access on kernel area 0x%lx at %p\n", area->id, (void *)originalAddress);
|
dprintf("user access on kernel area 0x%lx at %p\n", area->id, (void *)originalAddress);
|
||||||
return B_PERMISSION_DENIED;
|
return B_PERMISSION_DENIED;
|
||||||
}
|
}
|
||||||
if (isWrite && (area->protection & (B_WRITE_AREA | (isUser ? 0 : B_KERNEL_WRITE_AREA))) == 0) {
|
if (isWrite && (protection
|
||||||
|
& (B_WRITE_AREA | (isUser ? 0 : B_KERNEL_WRITE_AREA))) == 0) {
|
||||||
dprintf("write access attempted on read-only area 0x%lx at %p\n",
|
dprintf("write access attempted on read-only area 0x%lx at %p\n",
|
||||||
area->id, (void *)originalAddress);
|
area->id, (void *)originalAddress);
|
||||||
return B_PERMISSION_DENIED;
|
return B_PERMISSION_DENIED;
|
||||||
@@ -4678,7 +4713,7 @@ vm_soft_fault(vm_address_space *addressSpace, addr_t originalAddress,
|
|||||||
|
|
||||||
// If the page doesn't reside in the area's cache, we need to make sure it's
|
// If the page doesn't reside in the area's cache, we need to make sure it's
|
||||||
// mapped in read-only, so that we cannot overwrite someone else's data (copy-on-write)
|
// mapped in read-only, so that we cannot overwrite someone else's data (copy-on-write)
|
||||||
uint32 newProtection = area->protection;
|
uint32 newProtection = protection;
|
||||||
if (page->cache != topCache && !isWrite)
|
if (page->cache != topCache && !isWrite)
|
||||||
newProtection &= ~(B_WRITE_AREA | B_KERNEL_WRITE_AREA);
|
newProtection &= ~(B_WRITE_AREA | B_KERNEL_WRITE_AREA);
|
||||||
|
|
||||||
@@ -5797,7 +5832,7 @@ _user_delete_area(area_id area)
|
|||||||
|
|
||||||
area_id
|
area_id
|
||||||
_user_map_file(const char *userName, void **userAddress, int addressSpec,
|
_user_map_file(const char *userName, void **userAddress, int addressSpec,
|
||||||
addr_t size, int protection, int mapping, int fd, off_t offset)
|
size_t size, int protection, int mapping, int fd, off_t offset)
|
||||||
{
|
{
|
||||||
char name[B_OS_NAME_LENGTH];
|
char name[B_OS_NAME_LENGTH];
|
||||||
void *address;
|
void *address;
|
||||||
@@ -5834,7 +5869,7 @@ _user_map_file(const char *userName, void **userAddress, int addressSpec,
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
_user_unmap_memory(void *_address, addr_t size)
|
_user_unmap_memory(void *_address, size_t size)
|
||||||
{
|
{
|
||||||
addr_t address = (addr_t)_address;
|
addr_t address = (addr_t)_address;
|
||||||
|
|
||||||
@@ -5857,7 +5892,142 @@ _user_unmap_memory(void *_address, addr_t size)
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
_user_sync_memory(void *_address, addr_t size, int flags)
|
_user_set_memory_protection(void* _address, size_t size, int protection)
|
||||||
|
{
|
||||||
|
// check address range
|
||||||
|
addr_t address = (addr_t)_address;
|
||||||
|
size = PAGE_ALIGN(size);
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
// weird error code required by POSIX
|
||||||
|
return ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
// extend and check protection
|
||||||
|
protection &= B_READ_AREA | B_WRITE_AREA | B_EXECUTE_AREA;
|
||||||
|
uint32 actualProtection = protection | B_KERNEL_READ_AREA
|
||||||
|
| (protection & B_WRITE_AREA ? B_KERNEL_WRITE_AREA : 0);
|
||||||
|
|
||||||
|
if (!arch_vm_supports_protection(actualProtection))
|
||||||
|
return B_NOT_SUPPORTED;
|
||||||
|
|
||||||
|
// We need to write lock the address space, since we're going to play with
|
||||||
|
// the areas.
|
||||||
|
AddressSpaceWriteLocker locker;
|
||||||
|
status_t status = locker.SetTo(team_get_current_team_id());
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
// First round: Check whether the whole range is covered by areas and we are
|
||||||
|
// allowed to modify them.
|
||||||
|
addr_t currentAddress = address;
|
||||||
|
size_t sizeLeft = size;
|
||||||
|
while (sizeLeft > 0) {
|
||||||
|
vm_area* area = vm_area_lookup(locker.AddressSpace(), currentAddress);
|
||||||
|
if (area == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
if ((area->protection & B_KERNEL_AREA) != 0)
|
||||||
|
return B_NOT_ALLOWED;
|
||||||
|
|
||||||
|
// TODO: For (shared) mapped files we should check whether the new
|
||||||
|
// protections are compatible with the file permissions. We don't have
|
||||||
|
// a way to do that yet, though.
|
||||||
|
|
||||||
|
addr_t offset = currentAddress - area->base;
|
||||||
|
size_t rangeSize = min_c(area->size - offset, sizeLeft);
|
||||||
|
|
||||||
|
currentAddress += rangeSize;
|
||||||
|
sizeLeft -= rangeSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second round: If the protections differ from that of the area, create a
|
||||||
|
// page protection array and re-map mapped pages.
|
||||||
|
vm_translation_map* map = &locker.AddressSpace()->translation_map;
|
||||||
|
currentAddress = address;
|
||||||
|
sizeLeft = size;
|
||||||
|
while (sizeLeft > 0) {
|
||||||
|
vm_area* area = vm_area_lookup(locker.AddressSpace(), currentAddress);
|
||||||
|
if (area == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
addr_t offset = currentAddress - area->base;
|
||||||
|
size_t rangeSize = min_c(area->size - offset, sizeLeft);
|
||||||
|
|
||||||
|
currentAddress += rangeSize;
|
||||||
|
sizeLeft -= rangeSize;
|
||||||
|
|
||||||
|
if (area->page_protections == NULL) {
|
||||||
|
if (area->protection == actualProtection)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// In the page protections we store only the three user protections,
|
||||||
|
// so we use 4 bits per page.
|
||||||
|
uint32 bytes = (area->size / B_PAGE_SIZE + 1) / 2;
|
||||||
|
area->page_protections = (uint8*)malloc(bytes);
|
||||||
|
if (area->page_protections == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
// init the page protections for all pages to that of the area
|
||||||
|
uint32 areaProtection = area->protection
|
||||||
|
& (B_READ_AREA | B_WRITE_AREA | B_EXECUTE_AREA);
|
||||||
|
memset(area->page_protections,
|
||||||
|
areaProtection | (areaProtection << 4), bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (addr_t pageAddress = area->base + offset;
|
||||||
|
pageAddress < currentAddress; pageAddress += B_PAGE_SIZE) {
|
||||||
|
map->ops->lock(map);
|
||||||
|
|
||||||
|
set_area_page_protection(area, pageAddress, protection);
|
||||||
|
|
||||||
|
addr_t physicalAddress;
|
||||||
|
uint32 flags;
|
||||||
|
|
||||||
|
status_t error = map->ops->query(map, pageAddress, &physicalAddress,
|
||||||
|
&flags);
|
||||||
|
if (error != B_OK || (flags & PAGE_PRESENT) == 0) {
|
||||||
|
map->ops->unlock(map);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
vm_page *page = vm_lookup_page(physicalAddress / B_PAGE_SIZE);
|
||||||
|
if (page == NULL) {
|
||||||
|
panic("area %p looking up page failed for pa 0x%lx\n", area,
|
||||||
|
physicalAddress);
|
||||||
|
map->ops->unlock(map);
|
||||||
|
return B_ERROR;;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the page is not in the topmost cache and write access is
|
||||||
|
// requested, we have to unmap it. Otherwise we can re-map it with
|
||||||
|
// the new protection.
|
||||||
|
bool unmapPage = page->cache != area->cache
|
||||||
|
&& (protection & B_WRITE_AREA) != 0;
|
||||||
|
|
||||||
|
if (!unmapPage) {
|
||||||
|
map->ops->unmap(map, pageAddress,
|
||||||
|
pageAddress + B_PAGE_SIZE - 1);
|
||||||
|
map->ops->map(map, pageAddress, physicalAddress,
|
||||||
|
actualProtection);
|
||||||
|
}
|
||||||
|
|
||||||
|
map->ops->unlock(map);
|
||||||
|
|
||||||
|
if (unmapPage)
|
||||||
|
vm_unmap_pages(area, pageAddress, B_PAGE_SIZE, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
_user_sync_memory(void *_address, size_t size, int flags)
|
||||||
{
|
{
|
||||||
addr_t address = (addr_t)_address;
|
addr_t address = (addr_t)_address;
|
||||||
size = PAGE_ALIGN(size);
|
size = PAGE_ALIGN(size);
|
||||||
@@ -5934,3 +6104,11 @@ _user_sync_memory(void *_address, addr_t size, int flags)
|
|||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
_user_memory_advice(void* address, size_t size, int advice)
|
||||||
|
{
|
||||||
|
// TODO: Implement!
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|||||||
@@ -142,6 +142,14 @@ munmap(void* address, size_t length)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
mprotect(void* address, size_t length, int protection)
|
||||||
|
{
|
||||||
|
RETURN_AND_SET_ERRNO(_kern_set_memory_protection(address, length,
|
||||||
|
protection));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
msync(void* address, size_t length, int flags)
|
msync(void* address, size_t length, int flags)
|
||||||
{
|
{
|
||||||
@@ -149,6 +157,13 @@ msync(void* address, size_t length, int flags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
posix_madvise(void* address, size_t length, int advice)
|
||||||
|
{
|
||||||
|
RETURN_AND_SET_ERRNO(_kern_memory_advice(address, length, advice));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
shm_open(const char* name, int openMode, mode_t permissions)
|
shm_open(const char* name, int openMode, mode_t permissions)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user