swapcontext bug: not preserve R14,RBX correctly
| Originator: | mikewei08 | ||
| Number: | rdar://12581792 | Date Originated: | 27-Oct-2012 12:06 AM |
| Status: | Open | Resolved: | |
| Product: | Mac OS X | Product Version: | 10.7.5 |
| Classification: | Other Bug | Reproducible: | YES |
I am porting my coroutine implemetation (https://github.com/mikewei/micoro) to MacOS and found swapcontext(3) in MacOS(10.7.5) (with Libc-763.13) does not work correctly.
Then I wrote a simple test program and the problem is clear: swapcontext does not SAVE-RESTORE some registers (R14, RBX) correctly. The testing code listed below will abort for assert failure:
#define _XOPEN_SOURCE
#include <ucontext.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
static ucontext_t uctx_main, uctx_func2;
static void func2(void)
{
swapcontext(&uctx_func2, &uctx_main);
}
static void func1(void)
{
#define REG "r14"
long i;
asm volatile ("movq $1234, %%" REG : :);
swapcontext(&uctx_main, &uctx_func2);
asm volatile ("movq %%" REG ", %0" :"=m"(i) :);
/* assert will fail here */
assert(i == 1234);
}
int main(int argc, char *argv[])
{
char func2_stack[1024*256];
getcontext(&uctx_func2);
uctx_func2.uc_stack.ss_sp = func2_stack;
uctx_func2.uc_stack.ss_size = sizeof(func2_stack);
uctx_func2.uc_link = NULL;
makecontext(&uctx_func2, func2, 1, 0);
func1();
return 0;
}
I debug and analyze the assembly code of swapcontext and I think the bug is found:
1. (00000000000286b2~00000000000286b4) swapcontext save R14 and RBX in its stack-frame (note this should be preserved accross ucontext switch)
2. (00000000000286fb~000000000002870e) setcontext call is *tail-call-optimized* by compiler and the stack-frame-saved-regs is released wrongly
3. (000000000002870e, 00000000000a2867) previous saved R14 and RBX (in step 1) is overwrited by push and call
4. after swapcontext the old ucontext data is already incorrect, so when swapcontext back it get a wrong R14,RBX value and the following code will crash
otool -tV /usr/lib/system/libsystem_c.dylib
_swapcontext:
00000000000286b2 pushq %r14
00000000000286b4 pushq %rbx
00000000000286b5 subq $0x08,%rsp
00000000000286b9 testq %rdi,%rdi
00000000000286bc je 0x000286c6
00000000000286be movq %rsi,%rbx
00000000000286c1 testq %rbx,%rbx
00000000000286c4 jne 0x000286d8
00000000000286c6 callq ___error
00000000000286cb movl $0x00000016,(%rax)
00000000000286d1 movl $0xffffffff,%eax
00000000000286d6 jmp 0x000286f3
00000000000286d8 movq %rdi,%r14
00000000000286db andb $0x7f,0x03(%r14)
00000000000286e0 movq %r14,%rdi
00000000000286e3 callq _getcontext
00000000000286e8 testl %eax,%eax
00000000000286ea jne 0x000286f3
00000000000286ec movl (%r14),%ecx
00000000000286ef testl %ecx,%ecx
00000000000286f1 jns 0x000286fb
00000000000286f3 addq $0x08,%rsp
00000000000286f7 popq %rbx
00000000000286f8 popq %r14
00000000000286fa ret
00000000000286fb orl $0x80000000,%ecx
0000000000028701 movl %ecx,(%r14)
0000000000028704 movq %rbx,%rdi
0000000000028707 addq $0x08,%rsp
000000000002870b popq %rbx
000000000002870c popq %r14
000000000002870e jmp _setcontext
_setcontext:
00000000000a2855 pushq %rbx
00000000000a2856 leaq 0x38(%rdi),%rbx
00000000000a285a cmpq 0x30(%rdi),%rbx
00000000000a285e je 0x000a2864
00000000000a2860 movq %rbx,0x30(%rdi)
00000000000a2864 movl 0x04(%rdi),%edi
00000000000a2867 callq _sigsetmask
00000000000a286c movq %rbx,%rdi
00000000000a286f popq %rbx
00000000000a2870 jmp __setcontext
I think the bug can be fixed by disable tail call optimization (-foptimize-sibling-calls).
Comments
Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!
Same bug found and reported at # 265446874