When resizing an area that has individual page protections (set via mprotect),

we have to enlarge/shrink the array that holds them and assign a protection
value for the additional pages as necessary. Otherwise we'll access invalid
memory when looking up page protections for enlarged areas and get random
protection values.
Experienced with QEMU that sets page protections via mprotect on heap memory.
When the heap was later enlarged, write access to the additional memory would
result in permission denied errors and crashes.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@42330 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2011-06-27 23:24:17 +00:00
parent 4b722b91db
commit 3fb17998a7

View File

@ -4765,6 +4765,33 @@ vm_resize_area(area_id areaID, size_t newSize, bool kernel)
if (status == B_OK && newSize < oldSize)
status = cache->Resize(cache->virtual_base + newSize, priority);
if (status == B_OK) {
// Shrink or grow individual page protections if in use.
if (area->page_protections != NULL) {
uint32 bytes = (newSize / B_PAGE_SIZE + 1) / 2;
uint8* newProtections
= (uint8*)realloc(area->page_protections, bytes);
if (newProtections == NULL)
status = B_NO_MEMORY;
else {
area->page_protections = newProtections;
if (oldSize < newSize) {
// init the additional page protections to that of the area
uint32 offset = (oldSize / B_PAGE_SIZE + 1) / 2;
uint32 areaProtection = area->protection
& (B_READ_AREA | B_WRITE_AREA | B_EXECUTE_AREA);
memset(area->page_protections + offset,
areaProtection | (areaProtection << 4), bytes - offset);
if ((oldSize / B_PAGE_SIZE) % 2 != 0) {
uint8& entry = area->page_protections[offset - 1];
entry = (entry & 0x0f) | (areaProtection << 4);
}
}
}
}
}
if (status != B_OK) {
// Something failed -- resize the areas back to their original size.
// This can fail, too, in which case we're seriously screwed.