diff --git a/kernel/memory.c b/kernel/memory.c index f022f96565..e279b9b970 100644 --- a/kernel/memory.c +++ b/kernel/memory.c @@ -276,15 +276,18 @@ void *mem_ptr(struct mem *mem, addr_t addr, int type) { asbestos_invalidate_page(mem->mmu.asbestos, page); // if page is cow, ~~milk~~ copy it if (entry->flags & P_COW) { - void *data = (char *) entry->data->data + entry->offset; - void *copy = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); - // copy/paste from above read_wrunlock(&mem->lock); write_wrlock(&mem->lock); - memcpy(copy, data, PAGE_SIZE); - pt_map(mem, page, 1, copy, 0, entry->flags &~ P_COW); + // re-fetch entry after lock upgrade, another thread may have COW'd it + entry = mem_pt(mem, page); + if (entry != NULL && (entry->flags & P_COW)) { + void *data = (char *) entry->data->data + entry->offset; + void *copy = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + memcpy(copy, data, PAGE_SIZE); + pt_map(mem, page, 1, copy, 0, entry->flags &~ P_COW); + } write_wrunlock(&mem->lock); read_wrlock(&mem->lock); }