From: Razvan Ghiorghe <[email protected]> zero_bss() incorrectly assumed that any PT_LOAD containing .bss must be writable, rejecting valid ELF binaries where .bss overlaps the tail of an RX file-backed page.
Instead of failing, temporarily enable write access on the overlapping page to zero the fractional bss range, then restore the original page permissions once initialization is complete. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3179 Signed-off-by: Razvan Ghiorghe <[email protected]> Reviewed-by: Helge Deller <[email protected]> Signed-off-by: Helge Deller <[email protected]> (cherry picked from commit 2ff529c6f64b706213339d4bbce76c7788243ddb) Signed-off-by: Michael Tokarev <[email protected]> diff --git a/linux-user/elfload.c b/linux-user/elfload.c index fa83d78667..0f05db4715 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -2377,12 +2377,6 @@ static bool zero_bss(abi_ulong start_bss, abi_ulong end_bss, { abi_ulong align_bss; - /* We only expect writable bss; the code segment shouldn't need this. */ - if (!(prot & PROT_WRITE)) { - error_setg(errp, "PT_LOAD with non-writable bss"); - return false; - } - align_bss = TARGET_PAGE_ALIGN(start_bss); end_bss = TARGET_PAGE_ALIGN(end_bss); @@ -2400,20 +2394,35 @@ static bool zero_bss(abi_ulong start_bss, abi_ulong end_bss, */ align_bss -= TARGET_PAGE_SIZE; } else { + abi_ulong start_page_aligned = start_bss & TARGET_PAGE_MASK; /* - * The start of the bss shares a page with something. - * The only thing that we expect is the data section, - * which would already be marked writable. - * Overlapping the RX code segment seems malformed. + * The logical OR between flags and PAGE_WRITE works because + * in include/exec/page-protection.h they are defined as PROT_* + * values, matching mprotect(). + * Temporarily enable write access to zero the fractional bss. + * target_mprotect() handles TB invalidation if needed. */ if (!(flags & PAGE_WRITE)) { - error_setg(errp, "PT_LOAD with bss overlapping " - "non-writable page"); - return false; + if (target_mprotect(start_page_aligned, + TARGET_PAGE_SIZE, + prot | PAGE_WRITE) == -1) { + error_setg_errno(errp, errno, + "Error enabling write access for bss"); + return false; + } } - /* The page is already mapped and writable. */ + /* The page is already mapped and now guaranteed writable. */ memset(g2h_untagged(start_bss), 0, align_bss - start_bss); + + if (!(flags & PAGE_WRITE)) { + if (target_mprotect(start_page_aligned, + TARGET_PAGE_SIZE, prot) == -1) { + error_setg_errno(errp, errno, + "Error restoring bss first permissions"); + return false; + } + } } } -- 2.47.3
