qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

simple aarch64 binary can cause linux-user QEMU to segv in zero_bss()


From: Peter Maydell
Subject: simple aarch64 binary can cause linux-user QEMU to segv in zero_bss()
Date: Mon, 23 Nov 2020 19:52:44 +0000

Somebody reported this on stackoverflow. Before I spend too
much time thinking about how this ought to work, does anybody
have the elfload.c intended operation in their head still?
Bug description and analysis of what goes wrong below:

https://stackoverflow.com/questions/64956322/alignment-requirements-for-arm64-elf-executables-run-in-qemu-assembled-by-gas

Given this trivial asm:

===begin program.s===
// GNU Assembler, ARM64 Linux

.bss

.lcomm ARRAY, 16

.text

.global _start

_start:
    mov x8, 93 // exit sys num
    mov x0, 0 // success
    svc 0
===endit===

clang -nostdlib -fno-integrated-as -target aarch64-linux-gnu -s
program.s -o program.out

the resulting program.out works fine on a real kernel but makes
qemu-aarch64 SEGV:
$ ./build/x86/qemu-aarch64 /tmp/program.out
Segmentation fault (core dumped)

Looking at it with gdb, the segv is from QEMU itself, in zero_bss():

    if (host_start < host_map_start) {
        memset((void *)host_start, 0, host_map_start - host_start);
    }

We try to clear the memory from 'host_start' to 'host_map_start'
(the latter being the round-up-to-host-page version of the former),
but for this binary we never mmap()ed host_start as writeable,
so we segv inside the memset.

The guest-binary relevant bits of /proc/maps are:
00400000-00401000 r--p 00000000 08:15 5767333
  /tmp/program.out
00401000-01411000 ---p 00000000 00:00 0

The program header is:
Program Header:
    LOAD off    0x0000000000000000 vaddr 0x0000000000400000 paddr
0x0000000000400000 align 2**16
         filesz 0x00000000000000bc memsz 0x00000000000000bc flags r-x
    LOAD off    0x00000000000000c0 vaddr 0x00000000004100c0 paddr
0x00000000004100c0 align 2**16
         filesz 0x0000000000000000 memsz 0x0000000000000010 flags rw-

and in zero_bss() host_start == 0x4100c0, host_map_start == 0x411000,
last_bss == 0x411000. The code calls
 page_set_flags(start = 0x410000, end = 0x411000,
flags=PAGE_VALID|PAGE_READ|PAGE_WRITE)
with, I assume, the intention that that will make the memset() OK,
but page_set_flags() just sets up some data structure bits which will
mean that a subsequent SEGV will cause us to do a page_unprotect() that
sets the page as actually writeable. Unfortunately the SIGSEGV handler
is not installed until quite late in linux-user/main.c, so when the
memset() in zero_bss() SEGVs QEMU just dies.

Should we try to get the SEGV handler working earlier in initialization
(it's pretty hairy machinery so that could be tricky) or should
elfload.c be mprotect()ing things appropriately itself?

thanks
-- PMM



reply via email to

[Prev in Thread] Current Thread [Next in Thread]