* 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:
Ingo Weinhold
2008-08-08 01:00:06 +00:00
parent f72ed717b4
commit a9d7be0708
6 changed files with 227 additions and 13 deletions
+12 -1
View File
@@ -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);
+8 -3
View File
@@ -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);
+1
View File
@@ -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;
+7 -3
View File
@@ -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
View File
@@ -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;
}
+15
View File
@@ -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)
{ {