Static x86 elf crash after sys_set_thread_area
felberj opened this issue · comments
My static hello world for x86 (src and binary below) crashes shortly after the syscall set_thread_area. I found this while trying to implement my own minimal kernel that communicates with unicorn, so I am more than willing to help you figure out what is happening here.
Unfortunately I have no clue what exactly this syscall should do, but I found some resouces:
For me it looks like the program crashes right after its next access to the stack after the syscall set_thread_area. Interesting to note here is that the report is invalid read at 0xf6dc, but the stack pointer is at 0x607ff6dc. This seems to be a bug in the MemHook of the unicorn engine, because the rust bindings also report a truncated value of the esp.
Questions while looking at your implementation at linux.go
- why did you choose the gdt entry number 2 instead of the next free slot?
- why did you choose the flags 0x12?
- Not sure whether the writing of X86_REG_GS is done correctly. Should't you do it like here?
static.c:
#include <stdio.h>
int main() {
printf("hello world\n");
}
and my Makefile:
all:
gcc -static -m32 -o static_x86 static.c
gcc -static -m64 -o static_x86_64 static.c
the binary:
static_x86.zip
crashdump
$ ./usercorn ../testdata/static/static_x86
invalid read: @0xf6dc, 0x4 = 0x0
[memory map]
0x60000000-0x60800000 rwx [stack]
0x60800000-0x60801000 ---
0x8048000-0x80ea000 r-x static_x86 [exe]
0x80ea000-0x80ed000 rw- static_x86 [exe]
0x80ed000-0x80ee000 rw- [brk]
0x100000-0x101000 rwx
[registers]
cs 0x00000000 ebx 0x607ff710 eflags 0x00000004 esp 0x607ff6dc
ds 0x00000000 ecx 0x00000000 eip 0x0806ffc2 fs 0x00000000
eax 0x00000002 edi 0x080ebaa0 es 0x00000000 gs 0x00000002
ebp 0x00000000 edx 0x00000040 esi 0x00000040 ss 0x00000000
[stacktrace]
0x806ffc2 _dl_sysinfo_int80+0x2
panic: Invalid memory read (UC_ERR_READ_UNMAPPED)
goroutine 1 [running]:
github.com/lunixbochs/usercorn/go/cmd.(*UsercornCmd).Run(0xc420096a50, 0xc420084080, 0x2, 0x2, 0xc4200b1800, 0x23, 0x40)
/Users/jonasfelber/dev/usercorn/.gopath/src/github.com/lunixbochs/usercorn/go/cmd/cmd.go:334 +0x1761
main.main()
/Users/jonasfelber/dev/usercorn/go/cmd/usercorn/main.go:12 +0x7f
.text:08049302 call _dl_sysinfo ; <-- syscall set_thread_area
.text:08049308 add esp, 10h
.text:0804930B test eax, eax
.text:0804930D jz short loc_8049320
.text:0804930F sub esp, 0Ch
.text:08049312 push offset aSet_thread_are ; "set_thread_area failed when setting up "...
.text:08049317 call __libc_fatal
.text:08049317 ; ---------------------------------------------------------------------------
.text:0804931C align 10h
.text:08049320
.text:08049320 loc_8049320: ; CODE XREF: __libc_setup_tls+14D�j
.text:08049320 mov eax, [esp+4Ch+var_2C]
.text:08049324 lea eax, ds:3[eax*8]
.text:0804932B mov gs, eax
.text:0804932D mov eax, [esp+4Ch+var_34]
.text:08049331 mov ecx, [esp+4Ch+var_40]
.text:08049335 mov [edi+238h], ebp
.text:0804933B mov dword ptr [edi+244h], 1
.text:08049345 mov ds:dword_80EBFB4, edi
.text:0804934B mov ds:static_slotinfo, 40h
.text:08049355 mov [edi+22Ch], eax
.text:0804935B mov eax, [esp+4Ch+var_38]
.text:0804935F mov [edi+234h], ecx
.text:08049365 mov ecx, [esp+4Ch+var_30]
.text:08049369 mov ds:_dl_tls_max_dtv_idx, 1
.text:08049373 mov ds:_dl_tls_dtv_slotinfo_list, offset static_slotinfo
.text:0804937D mov [edi+230h], eax
.text:08049383 mov eax, _dl_tls_static_size
.text:08049388 mov ds:_dl_tls_static_used, ecx
.text:0804938E mov ds:_dl_tls_static_nelem, 1
.text:08049398 lea eax, [ecx+eax+3Fh]
.text:0804939C and eax, 0FFFFFFC0h
.text:0804939F add eax, 4C0h
.text:080493A4 cmp esi, 40h
.text:080493A7 mov _dl_tls_static_size, eax
.text:080493AC mov eax, 40h
.text:080493B1 cmovb esi, eax
.text:080493B4 mov ds:_dl_tls_static_align, esi
.text:080493BA add esp, 3Ch
.text:080493BD pop ebx ; <-- crash
Actually, usercorn crashed even earlier at the first return after set_thread_area
.text:0806FFC0 _dl_sysinfo_int80 proc near ; CODE XREF: backtrace_and_maps+46�p
.text:0806FFC0 ; backtrace_and_maps+77�p ...
.text:0806FFC0 int 80h
.text:0806FFC2 retn ; <-- crash here
Not sure whether the writing of X86_REG_GS is done correctly. Should't you do it like here?
I thought x86-32 had no FS_base and GS_base MSRs. My GDT stuff isn't working yet on 32-bit and I don't remember why. I don't use a free GDT slot, because I provide the whole GDT myself.
I doubt there's a bug in mem hook, imo it's more likely you're seeing a GS offset where GS is zero.
Here's another ref on GDT with Unicorn https://gist.github.com/sparktrend/4e0f3234449a4524b5a6f30b38d5ebea
From memory, I think the problem was- when you set up the segment selectors, you need to set all of them up, but Unicorn longjumps when you change SS (stack selector), so I don't set that one. I did confirm from a custom Unicorn build that manually setting FS_base and GS_base works great, so the only problem is with the new GDT stuff.
GDT should now be fixed in gdt
branch and in unstable
branch
Edit: kinda... gdt loads but unicorn deadlocks. need to investigate.
Thank you very much! I will look into it tomorrow during my flight! Where does it deadlock?
Right after set_thread_area :) I might need to tweak the segment flags. I'm using https://github.com/unicorn-engine/unicorn/blob/master/samples/sample_x86_32_gdt_and_seg_regs.c#L186 for reference
Did you try the value 0x96
that apparently worked in unicorn-engine/unicorn#522 for the access flags of the stack segment?
I took the time to look into it again and I am not sure whether you have pushed the newest version onto the gdt branch, because when I run my sample file it does not give me any output.
By comparing your implementation with the sample implementation, it looks like you are setting the access and flag part wrong.
Imho should be like this (types removed so I can play with it in the playground)
entry |= (limit) & 0xFFFF
entry |= (((limit) >> 16) & 0xF) << 48
entry |= ((base) & 0xFFFFFF) << 16
entry |= ((base>>24) & 0xFF) << 56
entry |= ((access) & 0xFF) << 40
entry |= ((flags) & 0xF0) << 48
Actually never mind, you are setting it correctly!
Should be fixed (for static only) in gdt and unstable branches
fixed in unstable for both static and dynamic, finally merging all of unstable to master and closing this.