kernel/vm: check if page is in area
Checks if a `vm_page` is part of a `VMArea` before doing work with it, as pages in a `VMCache` that an area is a part of might not belong to that area. This fixes a bug for copy-on-write areas when an application is `fork`ing. Change-Id: Ic5683c67865b41bf3708bb7ea4104502ddf31a19 Reviewed-on: https://review.haiku-os.org/c/haiku/+/6496 Tested-by: Commit checker robot <[email protected]> Reviewed-by: Jérôme Duval <[email protected]> Reviewed-by: Axel Dörfler <[email protected]>
This commit is contained in:
committed by
Jérôme Duval
parent
bdcc293fa8
commit
79572316c4
@@ -443,6 +443,15 @@ virtual_page_address(VMArea* area, vm_page* page)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
is_page_in_area(VMArea* area, vm_page* page)
|
||||||
|
{
|
||||||
|
off_t pageCacheOffsetBytes = (off_t)(page->cache_offset << PAGE_SHIFT);
|
||||||
|
return pageCacheOffsetBytes >= area->cache_offset
|
||||||
|
&& pageCacheOffsetBytes < area->cache_offset + (off_t)area->Size();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//! You need to have the address space locked when calling this function
|
//! You need to have the address space locked when calling this function
|
||||||
static VMArea*
|
static VMArea*
|
||||||
lookup_area(VMAddressSpace* addressSpace, area_id id)
|
lookup_area(VMAddressSpace* addressSpace, area_id id)
|
||||||
@@ -924,15 +933,10 @@ cut_area(VMAddressSpace* addressSpace, VMArea* area, addr_t address,
|
|||||||
// Set the correct page protections for the second area.
|
// Set the correct page protections for the second area.
|
||||||
VMTranslationMap* map = addressSpace->TranslationMap();
|
VMTranslationMap* map = addressSpace->TranslationMap();
|
||||||
map->Lock();
|
map->Lock();
|
||||||
page_num_t firstPageOffset
|
|
||||||
= secondArea->cache_offset / B_PAGE_SIZE;
|
|
||||||
page_num_t lastPageOffset
|
|
||||||
= firstPageOffset + secondArea->Size() / B_PAGE_SIZE;
|
|
||||||
for (VMCachePagesTree::Iterator it
|
for (VMCachePagesTree::Iterator it
|
||||||
= secondArea->cache->pages.GetIterator();
|
= secondArea->cache->pages.GetIterator();
|
||||||
vm_page* page = it.Next();) {
|
vm_page* page = it.Next();) {
|
||||||
if (page->cache_offset >= firstPageOffset
|
if (is_page_in_area(secondArea, page)) {
|
||||||
&& page->cache_offset <= lastPageOffset) {
|
|
||||||
addr_t address = virtual_page_address(secondArea, page);
|
addr_t address = virtual_page_address(secondArea, page);
|
||||||
uint32 pageProtection
|
uint32 pageProtection
|
||||||
= get_area_page_protection(secondArea, address);
|
= get_area_page_protection(secondArea, address);
|
||||||
@@ -2641,6 +2645,9 @@ vm_copy_on_write_area(VMCache* lowerCache,
|
|||||||
// Change the protection of this page in all areas.
|
// Change the protection of this page in all areas.
|
||||||
for (VMArea* tempArea = upperCache->areas; tempArea != NULL;
|
for (VMArea* tempArea = upperCache->areas; tempArea != NULL;
|
||||||
tempArea = tempArea->cache_next) {
|
tempArea = tempArea->cache_next) {
|
||||||
|
if (!is_page_in_area(tempArea, page))
|
||||||
|
continue;
|
||||||
|
|
||||||
// The area must be readable in the same way it was
|
// The area must be readable in the same way it was
|
||||||
// previously writable.
|
// previously writable.
|
||||||
addr_t address = virtual_page_address(tempArea, page);
|
addr_t address = virtual_page_address(tempArea, page);
|
||||||
@@ -2671,6 +2678,9 @@ vm_copy_on_write_area(VMCache* lowerCache,
|
|||||||
map->Lock();
|
map->Lock();
|
||||||
for (VMCachePagesTree::Iterator it = lowerCache->pages.GetIterator();
|
for (VMCachePagesTree::Iterator it = lowerCache->pages.GetIterator();
|
||||||
vm_page* page = it.Next();) {
|
vm_page* page = it.Next();) {
|
||||||
|
if (!is_page_in_area(tempArea, page))
|
||||||
|
continue;
|
||||||
|
|
||||||
// The area must be readable in the same way it was
|
// The area must be readable in the same way it was
|
||||||
// previously writable.
|
// previously writable.
|
||||||
addr_t address = virtual_page_address(tempArea, page);
|
addr_t address = virtual_page_address(tempArea, page);
|
||||||
|
|||||||
@@ -4,9 +4,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <OS.h>
|
#include <OS.h>
|
||||||
|
|
||||||
|
|
||||||
@@ -72,6 +74,62 @@ map_protect_cut_test()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
map_cut_fork_test()
|
||||||
|
{
|
||||||
|
char name[24];
|
||||||
|
sprintf(name, "/shm-mmap-cut-fork-test-%d", getpid());
|
||||||
|
name[sizeof(name) - 1] = '\0';
|
||||||
|
shm_unlink(name);
|
||||||
|
int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
|
||||||
|
shm_unlink(name);
|
||||||
|
|
||||||
|
if (fd < 0) {
|
||||||
|
printf("failed to create temporary file!\n");
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
ftruncate(fd, B_PAGE_SIZE * 4);
|
||||||
|
|
||||||
|
uint8* ptr = (uint8*)mmap(NULL, B_PAGE_SIZE * 4, PROT_NONE, MAP_PRIVATE,
|
||||||
|
fd, 0);
|
||||||
|
|
||||||
|
// make the head accessible and also force the kernel to allocate the
|
||||||
|
// page_protections array
|
||||||
|
mprotect(ptr, B_PAGE_SIZE, PROT_READ | PROT_WRITE);
|
||||||
|
|
||||||
|
// store any value
|
||||||
|
ptr[0] = 'a';
|
||||||
|
|
||||||
|
// cut the area in the middle
|
||||||
|
mmap(ptr + B_PAGE_SIZE, B_PAGE_SIZE, PROT_NONE,
|
||||||
|
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
|
||||||
|
|
||||||
|
// validate that the fork does not crash the kernel
|
||||||
|
int pid = fork();
|
||||||
|
|
||||||
|
if (pid == 0)
|
||||||
|
{
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
else if (pid < 0)
|
||||||
|
{
|
||||||
|
printf("failed to fork the test process!\n");
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
int status;
|
||||||
|
waitpid(pid, &status, 0);
|
||||||
|
|
||||||
|
// validate that this does not crash
|
||||||
|
if (ptr[0] != 'a') {
|
||||||
|
printf("map-cut-fork test failed!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main()
|
main()
|
||||||
{
|
{
|
||||||
@@ -90,5 +148,8 @@ main()
|
|||||||
if ((status = map_protect_cut_test()) != 0)
|
if ((status = map_protect_cut_test()) != 0)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
|
if ((status = map_cut_fork_test()) != 0)
|
||||||
|
return status;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user