Skip to content

Commit 0a40523

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 0a40523

1 file changed

Lines changed: 98 additions & 12 deletions

File tree

src/elfs/elfloader.c

Lines changed: 98 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,50 @@ 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+
if(box64_pagesize > 4096) {
483+
uintptr_t min_hostpage = UINTPTR_MAX;
484+
uintptr_t max_hostpage = 0;
485+
for( int j =0;j<n;j++) {
486+
uintptr_t seg_page_start = ElfPageStart(head->multiblocks[j].paddr);
487+
uintptr_t seg_page_end = seg_page_start + head->multiblocks[j].asize;
488+
if(seg_page_start < min_hostpage)
489+
min_hostpage = seg_page_start;
490+
if(seg_page_end > max_hostpage)
491+
max_hostpage = seg_page_end;
492+
}
493+
for( uintptr_t page = min_hostpage; page < max_hostpage; page += box64_pagesize) {
494+
uint32_t final_prot = 0;
495+
int covered = 0;
496+
for (int j = 0; j < n; ++j) {
497+
uintptr_t seg_page_start = ElfPageStart(head->multiblocks[j].paddr);
498+
uintptr_t seg_page_end = seg_page_start + head->multiblocks[j].asize;
499+
if(seg_page_end <= page || seg_page_start >= page + box64_pagesize)
500+
continue;
501+
uint32_t seg_prot = ElfFlagsToProt(head->multiblocks[j].flags);
502+
final_prot |= seg_prot;
503+
covered = 1;
504+
}
505+
if(!covered) continue;
506+
setProtection_elf(page, box64_pagesize, final_prot);
507+
if(mprotect((void*)page, box64_pagesize, final_prot & ~PROT_CUSTOM) != 0) {
508+
printf_log(LOG_INFO, "Warning, mprotect failed on ELF host page %p (%s)\n",
509+
(void*)page, strerror(errno));
510+
}
511+
}
512+
513+
} else {
514+
// deferred mprotect: apply final protections after all segments are loaded
515+
// this avoids the case where mprotect on a shared host page (e.g. 64KB) strips
516+
// PROT_WRITE before a later segment that shares the same page has been read into memory
517+
for (int j = 0; j < n; j++) {
518+
if(!(head->multiblocks[j].flags & PF_W)) {
519+
uintptr_t start = head->multiblocks[j].paddr & ~(box64_pagesize-1);
520+
uintptr_t end = ALIGN(head->multiblocks[j].paddr + head->multiblocks[j].asize);
521+
for(uintptr_t page = start; page < end; page += box64_pagesize) {
522+
uint32_t prot = getProtection(page);
523+
if(prot && !(prot & PROT_WRITE))
524+
mprotect((void*)page, box64_pagesize, prot & ~PROT_CUSTOM);
525+
}
440526
}
441527
}
442528
}

0 commit comments

Comments
 (0)