summary refs log tree commit diff stats
path: root/results/classifier/zero-shot-user-mode/syscall/1886097
blob: 2fa678a19cc1c279d5073a62ea552b7356401b12 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
syscall: 0.384
instruction: 0.344
runtime: 0.271



Error in user-mode calculation of ELF program's brk

There's a discrepancy between the way QEMU user-mode and Linux calculate the initial program break for statically-linked binaries. I have a binary with the following segments:

  Program Headers:
    Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
    EXIDX          0x065a14 0x00075a14 0x00075a14 0x00588 0x00588 R   0x4
    PHDR           0x0a3000 0x000a3000 0x000a3000 0x00160 0x00160 R   0x1000
    LOAD           0x0a3000 0x000a3000 0x000a3000 0x00160 0x00160 R   0x1000
    LOAD           0x000000 0x00010000 0x00010000 0x65fa0 0x65fa0 R E 0x10000
    LOAD           0x066b7c 0x00086b7c 0x00086b7c 0x02384 0x02384 RW  0x10000
    NOTE           0x000114 0x00010114 0x00010114 0x00044 0x00044 R   0x4
    TLS            0x066b7c 0x00086b7c 0x00086b7c 0x00010 0x00030 R   0x4
    GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x8
    GNU_RELRO      0x066b7c 0x00086b7c 0x00086b7c 0x00484 0x00484 R   0x1
    LOAD           0x07e000 0x00089000 0x00089000 0x03ff4 0x03ff4 R E 0x1000
    LOAD           0x098000 0x00030000 0x00030000 0x01000 0x01000 RW  0x1000

The call to set_brk in Linux's binfmt_elf.c receives these arguments:

  set_brk(0xa3160, 0xa3160, 1)
  
Whereas in QEMU, info->brk gets set to 0x88f00. When the binary is run in QEMU, it crashes on the second call to brk, whereas it runs fine on real ARM hardware. I think the trouble is that the program break is set to an address lower than the virtual address of a LOAD segment (the program headers, in this case).

I believe that this discrepancy arises because in QEMU, info->brk is only incremented when the LOAD segment in question has PROT_WRITE. For this binary, the LOAD segment with write permissions and the highest virtual address is

  LOAD           0x066b7c 0x00086b7c 0x00086b7c 0x02384 0x02384 RW  0x10000
    
which overlaps with the TLS segment:

    TLS            0x066b7c 0x00086b7c 0x00086b7c 0x00010 0x00030 R   0x4
    
However, the Linux kernel puts the program break after the loadable segment with the highest virtual address, regardless of flags. So I think the fix is for QEMU to do the same.