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

Same bug found and reported at # 265446874


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!