GCC untraceable stack corruption
Posted: Mon Jun 29, 2020 8:40 am
Hi everyone, I'm currently testing syscalls on my OS and have encountered an insane bug.
Heres the syscall handler code:
Here I have some dumb test code with a global variable. After this code gets called 4 times, it ends up corrupting the stack and the syscall pop gs instruction fails with a page fault
You would probably say like yeah sure your logger causes the stack corruption after trying to log a number >4. But nope. Changing abosolutely random pieces of this code fixes the stack corruption.
For example:
- adding << "\n" after ++index
- adding random code after g_logger <<, like if (index == 0xFFFFFFFF) index = 0;
- replacing g_logger with log()
- probably a million more things
I think that the actual reason is that GCC decides to do some clever optimization and does something weird with the stack, if that's so how do I make sure this doesn't happen?
I don't actually need this g_logger and it works without it but how many more potential cases of this possibly breaking are there. Also I should mention that dispatch() gets called from a function written in assembly:
I have many copies of this function for each IRQ and exception etc so im sure its fine.
Also if it's a stack corruption why on earth does it only break after the 4th digit...
And if I do static index = 4 it breaks after logging 5
Please help me I really wanna know whats up here
UPD: Heres the dissasembly of the function (broken version)
Working dissasembly (added << "\n" at the end)
Heres the syscall handler code:
Code: Select all
auto g_logger = log();
void SyscallDispatcher::dispatch(RegisterState registers)
{
static size_t index = 0;
(void)registers;
g_logger << "\rsyscalled: " << ++index;
}
You would probably say like yeah sure your logger causes the stack corruption after trying to log a number >4. But nope. Changing abosolutely random pieces of this code fixes the stack corruption.
For example:
- adding << "\n" after ++index
- adding random code after g_logger <<, like if (index == 0xFFFFFFFF) index = 0;
- replacing g_logger with log()
- probably a million more things
I think that the actual reason is that GCC decides to do some clever optimization and does something weird with the stack, if that's so how do I make sure this doesn't happen?
I don't actually need this g_logger and it works without it but how many more potential cases of this possibly breaking are there. Also I should mention that dispatch() gets called from a function written in assembly:
Code: Select all
extern "C" void syscall();
asm(".globl syscall\n"
"syscall:\n"
" pushl $0\n"
" pusha\n"
" pushl %ds\n"
" pushl %es\n"
" pushl %fs\n"
" pushl %gs\n"
" pushl %ss\n"
" mov $0x10, %ax\n"
" mov %ax, %ds\n"
" mov %ax, %es\n"
" cld\n"
" call _ZN6kernel17SyscallDispatcher8dispatchENS_13RegisterStateE\n"
" add $0x4, %esp \n"
" popl %gs\n"
" popl %fs\n"
" popl %es\n"
" popl %ds\n"
" popa\n"
" add $0x4, %esp\n"
" iret\n");
Also if it's a stack corruption why on earth does it only break after the 4th digit...
And if I do static index = 4 it breaks after logging 5
Please help me I really wanna know whats up here
UPD: Heres the dissasembly of the function (broken version)
Code: Select all
00000050 < _ZN6kernel17SyscallDispatcher8dispatchENS_13RegisterStateE > :
50 : 83 ec 0c sub $0xc, % esp
53 : 83 3d 04 00 00 00 02 cmpl $0x2, 0x4
5a : 74 64 je c0 < _ZN6kernel17SyscallDispatcher8dispatchENS_13RegisterStateE + 0x70>
5c : 8b 15 00 00 00 00 mov 0x0, % edx
62 : 8b 02 mov(% edx), % eax
64 : 8b 00 mov(% eax), % eax
66 : 3d 00 00 00 00 cmp $0x0, % eax
6b : 75 73 jne e0 < _ZN6kernel17SyscallDispatcher8dispatchENS_13RegisterStateE + 0x90>
6d : b9 73 00 00 00 mov $0x73, % ecx
72 : b8 0d 00 00 00 mov $0xd, % eax
77 : ba 00 00 00 00 mov $0x0, % edx
7c : eb 08 jmp 86 < _ZN6kernel17SyscallDispatcher8dispatchENS_13RegisterStateE + 0x36 >
7e: 66 90 xchg % ax, % ax
80 : 89 c8 mov % ecx, % eax
82 : 0f b6 4a 01 movzbl 0x1(% edx), % ecx
86 : 83 c2 01 add $0x1, % edx
89 : e6 e9 out % al, $0xe9
8b : 84 c9 test % cl, % cl
8d : 75 f1 jne 80 < _ZN6kernel17SyscallDispatcher8dispatchENS_13RegisterStateE + 0x30 >
8f : b8 00 00 00 00 mov $0x0, % eax
94 : 8b 0d 0c 00 00 00 mov 0xc, % ecx
9a : 89 44 24 10 mov % eax, 0x10(% esp)
9e : 8d 51 01 lea 0x1(% ecx), % edx
a1 : 89 15 0c 00 00 00 mov % edx, 0xc
a7 : 89 54 24 14 mov % edx, 0x14(% esp)
ab : 83 c4 0c add $0xc, % esp
ae : e9 fc ff ff ff jmp af <_ZN6kernel17SyscallDispatcher8dispatchENS_13RegisterStateE + 0x5f>
b3 : 8d b4 26 00 00 00 00 lea 0x0(% esi, % eiz, 1), % esi
ba : 8d b6 00 00 00 00 lea 0x0(% esi), % esi
c0 : 83 ec 08 sub $0x8, % esp
c3 : 68 00 00 00 00 push $0x0
c8 : 68 00 00 00 00 push $0x0
cd : e8 fc ff ff ff call ce <_ZN6kernel17SyscallDispatcher8dispatchENS_13RegisterStateE + 0x7e>
d2 : 83 c4 10 add $0x10, % esp
d5 : eb bd jmp 94 < _ZN6kernel17SyscallDispatcher8dispatchENS_13RegisterStateE + 0x44 >
d7 : 8d b4 26 00 00 00 00 lea 0x0(% esi, % eiz, 1), % esi
de : 66 90 xchg % ax, % ax
e0 : 83 ec 08 sub $0x8, % esp
e3 : 68 00 00 00 00 push $0x0
e8 : 52 push % edx
e9 : ff d0 call * %eax
eb : b8 00 00 00 00 mov $0x0, % eax
f0 : 83 c4 10 add $0x10, % esp
f3 : eb 9f jmp 94 < _ZN6kernel17SyscallDispatcher8dispatchENS_13RegisterStateE + 0x44 >
Code: Select all
00000050 <_ZN6kernel17SyscallDispatcher8dispatchENS_13RegisterStateE>:
50: 83 ec 0c sub $0xc,%esp
53: 83 3d 04 00 00 00 02 cmpl $0x2,0x4
5a: 0f 84 80 00 00 00 je e0 <_ZN6kernel17SyscallDispatcher8dispatchENS_13RegisterStateE+0x90>
60: 8b 15 00 00 00 00 mov 0x0,%edx
66: 8b 02 mov (%edx),%eax
68: 8b 00 mov (%eax),%eax
6a: 3d 00 00 00 00 cmp $0x0,%eax
6f: 0f 85 cb 00 00 00 jne 140 <_ZN6kernel17SyscallDispatcher8dispatchENS_13RegisterStateE+0xf0>
75: b9 73 00 00 00 mov $0x73,%ecx
7a: b8 0d 00 00 00 mov $0xd,%eax
7f: ba 00 00 00 00 mov $0x0,%edx
84: eb 10 jmp 96 <_ZN6kernel17SyscallDispatcher8dispatchENS_13RegisterStateE+0x46>
86: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
8d: 8d 76 00 lea 0x0(%esi),%esi
90: 89 c8 mov %ecx,%eax
92: 0f b6 4a 01 movzbl 0x1(%edx),%ecx
96: 83 c2 01 add $0x1,%edx
99: e6 e9 out %al,$0xe9
9b: 84 c9 test %cl,%cl
9d: 75 f1 jne 90 <_ZN6kernel17SyscallDispatcher8dispatchENS_13RegisterStateE+0x40>
9f: b8 00 00 00 00 mov $0x0,%eax
a4: 8b 0d 0c 00 00 00 mov 0xc,%ecx
aa: 83 ec 08 sub $0x8,%esp
ad: 8d 51 01 lea 0x1(%ecx),%edx
b0: 52 push %edx
b1: 50 push %eax
b2: 89 15 0c 00 00 00 mov %edx,0xc
b8: e8 fc ff ff ff call b9 <_ZN6kernel17SyscallDispatcher8dispatchENS_13RegisterStateE+0x69>
bd: 83 c4 10 add $0x10,%esp
c0: 83 78 04 02 cmpl $0x2,0x4(%eax)
c4: 74 3a je 100 <_ZN6kernel17SyscallDispatcher8dispatchENS_13RegisterStateE+0xb0>
c6: 8b 10 mov (%eax),%edx
c8: 8b 02 mov (%edx),%eax
ca: 8b 00 mov (%eax),%eax
cc: 3d 00 00 00 00 cmp $0x0,%eax
d1: 75 4d jne 120 <_ZN6kernel17SyscallDispatcher8dispatchENS_13RegisterStateE+0xd0>
d3: b8 0a 00 00 00 mov $0xa,%eax
d8: e6 e9 out %al,$0xe9
da: 83 c4 0c add $0xc,%esp
dd: c3 ret
de: 66 90 xchg %ax,%ax
e0: 83 ec 08 sub $0x8,%esp
e3: 68 00 00 00 00 push $0x0
e8: 68 00 00 00 00 push $0x0
ed: e8 fc ff ff ff call ee <_ZN6kernel17SyscallDispatcher8dispatchENS_13RegisterStateE+0x9e>
f2: 83 c4 10 add $0x10,%esp
f5: eb ad jmp a4 <_ZN6kernel17SyscallDispatcher8dispatchENS_13RegisterStateE+0x54>
f7: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
fe: 66 90 xchg %ax,%ax
100: c7 44 24 14 0d 00 00 movl $0xd,0x14(%esp)
107: 00
108: 89 44 24 10 mov %eax,0x10(%esp)
10c: 83 c4 0c add $0xc,%esp
10f: e9 fc ff ff ff jmp 110 <_ZN6kernel17SyscallDispatcher8dispatchENS_13RegisterStateE+0xc0>
114: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
11b: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
11f: 90 nop
120: c7 44 24 14 0d 00 00 movl $0xd,0x14(%esp)
127: 00
128: 89 54 24 10 mov %edx,0x10(%esp)
12c: 83 c4 0c add $0xc,%esp
12f: ff e0 jmp *%eax
131: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
138: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
13f: 90 nop
140: 83 ec 08 sub $0x8,%esp
143: 68 00 00 00 00 push $0x0
148: 52 push %edx
149: ff d0 call *%eax
14b: b8 00 00 00 00 mov $0x0,%eax
150: 83 c4 10 add $0x10,%esp
153: e9 4c ff ff ff jmp a4 <_ZN6kernel17SyscallDispatcher8dispatchENS_13RegisterStateE+0x54>