qemu-devel
[Top][All Lists]
Advanced

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

Re: [Qemu-devel] qemu-arm SIGSEGV for self-modifying code


From: John Reiser
Subject: Re: [Qemu-devel] qemu-arm SIGSEGV for self-modifying code
Date: Wed, 20 Sep 2017 10:05:36 -0700
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.3.0

Thanks for your reply, Peter.  [I fixed my typo in the Subject: field of the 
header.]

[Moving here from  https://bugzilla.redhat.com/show_bug.cgi?id=1493304 ]

qemu-arm from qemu-user-2.10.0-1.fc27.x86_64 (thus emulating 32-bit ARM on
x86_64)
generates SIGSEGV when code modifies a never-previously executed instruction
that is on a writable page and is 848 bytes ahead of pc.
A real armv7l processor allows this and executes as desired.
Why the difference?  How can it be changed?  Where is the documentation?
The memory region in question is allocated via

mmap2(0xf7000000,228092,PROT_EXEC|PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED,-1,0)
= 0xf7000000
[and not changed via mprotect()] and written once to contain:
=====
0xf703704c:
         ldr r2,mflg_here  // pc+856
         orr r2,r2,r3  @ modify the instruction
=>      str r2,mflg_here  // pc+848    the faulting instruction

      [[snip about 848 bytes containing instructions only]]

0xf70373ac:
   mflg_here:  // The next instruction is re-written once.
         orr r3,r3,#0  @ flags |= MAP_{PRIVATE|ANON}  [QNX vs Linux]

Is your guest program correctly performing the necessary cache
maintenance operations

...wait, I think I misread your bug report. You get the SEGV
on the store to the code, before it even gets to trying to
execute it?

Yes, the SEGV occurs on the store, "long" before the re-written instruction 
ever is executed
(848 bytes in space, at least a thousand instructions in time.)
The region between the 'str' and the re-written instruction contains more than 
a dozen
branches (conditional, unconditional, subroutine calls) and several 'svc' 
system calls.

The execution sequence is:
  Use mmap2() to allocate 228092 bytes of new pages with read+write+execute 
permission.
  Generate code into those pages, writing each byte once in order, including at 
'mflg_here'.
  Jump to 0xf703704c which is 0x3704c into the new region.  This is the 
first-ever
    execution in the new region.
  Immediately read-modify-write the instruction at 'mflg_here', which is 
0x373ac bytes
    into the region and 848 bytes ahead of the pc.
  SIGSEGV at the 'str' of the read-modify-write.

The qemu-arm generated x86_64 code in the vicinity of the SIGSEGV is
=====
   0x55555599364c <static_code_gen_buffer+13308>: mov    $0xf70373ac,%ebp
   0x555555993651 <static_code_gen_buffer+13313>: mov    %gs:0x0(%ebp),%ebp
   0x555555993656 <static_code_gen_buffer+13318>: mov    0xc(%r14),%ebx
   0x55555599365a <static_code_gen_buffer+13322>: or     %ebx,%ebp
   0x55555599365c <static_code_gen_buffer+13324>: mov    %ebp,0x8(%r14)
   0x555555993660 <static_code_gen_buffer+13328>: mov    $0xf70373ac,%ebx
=> 0x555555993665 <static_code_gen_buffer+13333>:      mov    %ebp,%gs:(%ebx)
   0x555555993669 <static_code_gen_buffer+13337>: mov    0x34(%r14),%ebp
   0x55555599366d <static_code_gen_buffer+13341>: mov    %gs:0x0(%ebp),%ebx
   0x555555993672 <static_code_gen_buffer+13346>: mov    %ebx,0x10(%r14)
(gdb) info reg
rax            0x1      1
rbx            0xf70373ac       4144198572
rcx            0x0      0
rdx            0x55555597b880   93824996587648
rsi            0x555555993640   93824996685376
rdi            0x5555582b6290   93825039819408
rbp            0xe3833022       0xe3833022
rsp            0x7fffffffd2c0   0x7fffffffd2c0
r8             0x0      0
r9             0x0      0
r10            0x55555597b7f0   93824996587504
r11            0x206    518
r12            0x555555993640   93824996685376
r13            0xf703704c       4144197708
r14            0x5555582b6290   93825039819408
r15            0x5555582ae5b8   93825039787448
rip            0x555555993665   0x555555993665 <static_code_gen_buffer+13333>
eflags         0x10286  [ PF SF IF RF ]
cs             0x33     51
ss             0x2b     43
ds             0x0      0
es             0x0      0
fs             0x0      0
gs             0x0      0
=====
Unfortunately gdb does not show the correct value of segment register %gs.
/proc/{qemu-arm}/maps shows no mapping at any address less than 4GiB, yet the 
x86_64 code
   0x55555599364c <static_code_gen_buffer+13308>: mov    $0xf70373ac,%ebp
   0x555555993651 <static_code_gen_buffer+13313>: mov    %gs:0x0(%ebp),%ebp
did perform a successful fetch from %gs:0xf70373ac.  The value 0xe3833022 in 
%ebp
is the correct new (modified by the OR) contents to be stored into address 
0xf70373ac.


Environment variation:
   SIGSEGV with qemu-user-2.7.1-7.fc25.x86_64   under kernel 
4.12.11-200.fc25.x86_64
   SIGSEGV with qemu-user-2.9.1-1.fc26.x86_64   under kernel 
4.12.13-300.fc26.x86_64
   SIGSEGV with qemu-user-2.10.0-1.fc27.x86_64  under kernel 
4.13.2-300.fc27.x86_64
   SIGSEGV with qemu-user 1:2.1+dfsg-12+deb8u6  under kernel 3.16.43-2+deb8u3 
(x86_64)
      All SIGSEGV are cross-architecture emulation: running qemu-arm on x86_64.

   Success with qemu-user-2.10.0-1.fc27.armv7hl under kernel 
4.13.2-300.fc27.armv7hl
      This success is "native emulation": running qemu-arm on armv7hl.

--
John



reply via email to

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