@@ -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+
214265int 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