Skip to content

Commit 76289dc

Browse files
committed
[ELFLOADER] handle PT_LOAD host-page overlaps on NON4KPAGE hosts
Signed-off-by: Zewei Yang <yangzewei@loongson.cn>
1 parent a22ab35 commit 76289dc

1 file changed

Lines changed: 86 additions & 12 deletions

File tree

src/elfs/elfloader.c

Lines changed: 86 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,57 @@ const char* ElfPath(elfheader_t* head)
211211
return head->path;
212212
}
213213

214+
static uintptr_t ElfPageStart(uintptr_t addr)
215+
{
216+
return addr & ~(box64_pagesize-1);
217+
}
218+
219+
static uintptr_t ElfPageEnd(uintptr_t addr, size_t size)
220+
{
221+
return ALIGN(addr + size);
222+
}
223+
224+
static uint32_t ElfFlagsToProt(uint32_t flags)
225+
{
226+
uint32_t prot = 0;
227+
if(flags & PF_R) prot |= PROT_READ;
228+
if((flags & PF_W) || box64_unittest_mode) prot |= PROT_WRITE;
229+
if(flags & PF_X) prot |= PROT_EXEC;
230+
return prot;
231+
}
232+
233+
static int ElfPhdrNeedsCopyLoad64(elfheader_t* head, int phidx, uintptr_t offs)
234+
{
235+
Elf64_Phdr* e = &head->PHEntries._64[phidx];
236+
if(box64_pagesize <= 4096)
237+
return 0;
238+
239+
uintptr_t seg_addr = e->p_paddr + offs;
240+
uintptr_t seg_addr_end = seg_addr + e->p_memsz;
241+
uintptr_t host_start = ElfPageStart(seg_addr);
242+
uintptr_t host_end = ElfPageEnd(seg_addr, e->p_memsz);
243+
244+
// Partial host-page coverage must be copy-loaded so adjacent PT_LOADs can
245+
// share the host page safely while we defer the final protection fixup.
246+
if(seg_addr != host_start || seg_addr_end != host_end)
247+
return 1;
248+
249+
for(size_t i = 0; i < head->numPHEntries; ++i) {
250+
Elf64_Phdr* other = &head->PHEntries._64[i];
251+
if(i == phidx || other->p_type != PT_LOAD || !other->p_flags || !other->p_memsz)
252+
continue;
253+
254+
uintptr_t other_addr = other->p_paddr + offs;
255+
uintptr_t other_host_start = ElfPageStart(other_addr);
256+
uintptr_t other_host_end = ElfPageEnd(other_addr, other->p_memsz);
257+
258+
if(other_host_end > host_start && other_host_start < host_end)
259+
return 1;
260+
}
261+
262+
return 0;
263+
}
264+
214265
int AllocLoadElfMemory32(box64context_t* context, elfheader_t* head, int mainbin)
215266
#ifndef BOX32
216267
{ return 1; }
@@ -300,7 +351,7 @@ int AllocLoadElfMemory(box64context_t* context, elfheader_t* head, int mainbin)
300351
head->multiblocks[n].size = e->p_filesz;
301352
head->multiblocks[n].align = e->p_align;
302353
// HACK: Mark all the code pages writable in unittest mode because some tests mix code and (writable) data...
303-
uint8_t prot = ((e->p_flags & PF_R)?PROT_READ:0)|(((e->p_flags & PF_W) || box64_unittest_mode)?PROT_WRITE:0)|((e->p_flags & PF_X)?PROT_EXEC:0);
354+
uint8_t prot = ElfFlagsToProt(e->p_flags);
304355
// check if alignment is correct
305356
uintptr_t balign = head->multiblocks[n].align-1;
306357
if (balign < (box64_pagesize - 1)) balign = box64_pagesize - 1;
@@ -316,6 +367,8 @@ int AllocLoadElfMemory(box64context_t* context, elfheader_t* head, int mainbin)
316367
try_mmap = 0;
317368
if(e->p_align<box64_pagesize)
318369
try_mmap = 0;
370+
if(ElfPhdrNeedsCopyLoad64(head, i, offs))
371+
try_mmap = 0;
319372
if(try_mmap) {
320373
printf_dump(log_level, "Mmaping 0x%lx(0x%lx) bytes @%p with prot %x for Elf \"%s\"\n", head->multiblocks[n].size, head->multiblocks[n].asize, (void*)head->multiblocks[n].paddr, prot, head->name);
321374
void* p = InternalMmap(
@@ -426,17 +479,38 @@ int AllocLoadElfMemory(box64context_t* context, elfheader_t* head, int mainbin)
426479
memset(dest+e->p_filesz, 0, e->p_memsz - e->p_filesz);
427480
}
428481
}
429-
// deferred mprotect: apply final protections after all segments are loaded
430-
// this avoids the case where mprotect on a shared host page (e.g. 64KB) strips
431-
// PROT_WRITE before a later segment that shares the same page has been read into memory
432-
for (int j = 0; j < n; j++) {
433-
if(!(head->multiblocks[j].flags & PF_W)) {
434-
uintptr_t start = head->multiblocks[j].paddr & ~(box64_pagesize-1);
435-
uintptr_t end = ALIGN(head->multiblocks[j].paddr + head->multiblocks[j].asize);
436-
for(uintptr_t page = start; page < end; page += box64_pagesize) {
437-
uint32_t prot = getProtection(page);
438-
if(prot && !(prot & PROT_WRITE))
439-
mprotect((void*)page, box64_pagesize, prot & ~PROT_CUSTOM);
482+
483+
// Perform final page protection after all PT_LOAD segments have been loaded.
484+
// Recalculate from the loaded blocks instead of calling getProtection(page),
485+
// Maintain the cumulative combination of all segment flags for overlapping pages.
486+
if(n) {
487+
uintptr_t min_hostpage = UINTPTR_MAX;
488+
uintptr_t max_hostpage = 0;
489+
for(int j = 0; j < n; ++j) {
490+
uintptr_t seg_page_start = ElfPageStart(head->multiblocks[j].paddr);
491+
uintptr_t seg_page_end = seg_page_start + head->multiblocks[j].asize;
492+
if(seg_page_start < min_hostpage)
493+
min_hostpage = seg_page_start;
494+
if(seg_page_end > max_hostpage)
495+
max_hostpage = seg_page_end;
496+
}
497+
for(uintptr_t page = min_hostpage; page < max_hostpage; page += box64_pagesize) {
498+
uint32_t final_prot = 0;
499+
int covered = 0;
500+
for (int j = 0; j < n; ++j) {
501+
uintptr_t seg_page_start = ElfPageStart(head->multiblocks[j].paddr);
502+
uintptr_t seg_page_end = seg_page_start + head->multiblocks[j].asize;
503+
if(seg_page_end <= page || seg_page_start >= page + box64_pagesize)
504+
continue;
505+
uint32_t seg_prot = ElfFlagsToProt(head->multiblocks[j].flags);
506+
final_prot |= seg_prot;
507+
covered = 1;
508+
}
509+
if(!covered) continue;
510+
setProtection_elf(page, box64_pagesize, final_prot);
511+
if(mprotect((void*)page, box64_pagesize, final_prot & ~PROT_CUSTOM) != 0) {
512+
printf_log(LOG_INFO, "Warning, mprotect failed on ELF host page %p (%s)\n",
513+
(void*)page, strerror(errno));
440514
}
441515
}
442516
}

0 commit comments

Comments
 (0)