Commit 2f0cae46 authored by Russ Cox's avatar Russ Cox

runtime: work around kernel bug in Snow Leopard signal handling

Could not take a signal on threads other than the main thread.
If you look at the spinning binary with dtrace, you can see a
fault happening over and over:

    $ dtrace -n '
    fbt::user_trap:entry /execname=="boot32" && self->count < 10/
    {
        self->count++;
        printf("%s %x %x %x %x", probefunc, arg1, arg2, arg3, arg4);
        stack();
        tracemem(arg4, 256);
    }'

    dtrace: description 'fbt::user_trap:entry ' matched 1 probe
    CPU     ID                    FUNCTION:NAME
      1  17015                  user_trap:entry user_trap 0 10 79af0a0 79af0a0
                  mach_kernel`lo_alltraps+0x12a

             0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f  0123456789abcdef
         0: 0e 00 00 00 37 00 00 00 00 00 00 00 1f 00 00 00  ....7...........
        10: 1f 00 00 00 a8 33 00 00 00 00 00 01 00 00 00 00  .....3..........
        20: 98 ba dc fe 07 09 00 00 00 00 00 00 98 ba dc fe  ................
        30: 06 00 00 00 0d 00 00 00 34 00 00 00 9e 1c 00 00  ........4.......
        40: 17 00 00 00 00 02 00 00 ac 30 00 00 1f 00 00 00  .........0......
        50: 00 00 00 00 00 00 00 00 0d 00 00 00 e0 e6 29 00  ..............).
        60: 34 00 00 00 00 00 00 00 9e 1c 00 00 00 00 00 00  4...............
        70: 17 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00  ................
        80: ac 30 00 00 00 00 00 00 1f 00 00 00 00 00 00 00  .0..............
        90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
        a0: 48 00 00 00 10 00 00 00 85 00 00 00 a0 f2 29 00  H.............).
        b0: 69 01 00 02 00 00 00 00 e6 93 04 82 ff 7f 00 00  i...............
        c0: 2f 00 00 00 00 00 00 00 06 02 00 00 00 00 00 00  /...............
        d0: 78 ee 42 01 01 00 00 00 1f 00 00 00 00 00 00 00  x.B.............
        e0: 00 ed 9a 07 00 00 00 00 00 00 00 00 00 00 00 00  ................
        f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

    ...

The memory dump shows a 32-bit exception frame:

    x86_saved_state32

    gs = 0x37
    fs = 0
    es = 0x1f
    ds = 0x1f
    edi = 0x33a8
    esi = 0x01000000
    ebp = 0
    cr2 = 0xfedcba98
    ebx = 0x0907
    edx = 0
    ecx = 0xfedcba98
    eax = 0x06
    trapno = 0x0d
    err = 0x34
    eip = 0x1c9e
    cs = 0x17
    efl = 0x0200
    uesp = 0x30ac
    ss = 0x1f

The cr2 of 0xfedcba98 is the address that the new thread read
to cause the fault, but note that the trap is now a GP fault with
error code 0x34, meaning it's moved past the cr2 problem and on
to an invaild segment selector.  The 0x34 is suspiciously similar
to the 0x37 in gs, and sure enough, OS X forces gs to have
that value in the signal handler, and if your thread hasn't set
up that segment (known as USER_CTHREAD), you'll fault on the IRET
into the signal handler and never be able to handle a signal.

The kernel bug is that it forces segment 0x37 without making sure
it is a valid segment.  Leopard also forced 0x37 but had the courtesy
to set it up first.

Since OS X requires us to set up that segment (using the
thread_fast_set_cthread_self system call), we might as well
use it instead of the more complicated i386_set_ldt call to
set up our per-OS thread storage.

Also add some more zeros to bsdthread_register for new arguments
in Snow Leopard (apparently unnecessary, but being careful).

Fixes #510.

R=r
CC=golang-dev
https://golang.org/cl/824046
parent 9aa8f95b
...@@ -74,11 +74,6 @@ TEXT sigaction(SB),7,$0 ...@@ -74,11 +74,6 @@ TEXT sigaction(SB),7,$0
// 16(FP) siginfo // 16(FP) siginfo
// 20(FP) context // 20(FP) context
TEXT sigtramp(SB),7,$40 TEXT sigtramp(SB),7,$40
// Darwin sets GS to 0x37 on entry.
// The original GS is at 0x70(FP).
MOVL oldgs+0x70(FP), BX
MOVW BX, GS
// g = m->gsignal // g = m->gsignal
get_tls(CX) get_tls(CX)
MOVL m(CX), BP MOVL m(CX), BP
...@@ -186,9 +181,9 @@ TEXT bsdthread_register(SB),7,$40 ...@@ -186,9 +181,9 @@ TEXT bsdthread_register(SB),7,$40
MOVL $bsdthread_start(SB), 4(SP) // threadstart MOVL $bsdthread_start(SB), 4(SP) // threadstart
MOVL $0, 8(SP) // wqthread, not used by us MOVL $0, 8(SP) // wqthread, not used by us
MOVL $0, 12(SP) // pthsize, not used by us MOVL $0, 12(SP) // pthsize, not used by us
MOVL $0, 16(SP) // paranoia MOVL $0, 16(SP) // dummy_value [sic]
MOVL $0, 20(SP) MOVL $0, 20(SP) // targetconc_ptr
MOVL $0, 24(SP) MOVL $0, 24(SP) // dispatchqueue_offset
INT $0x80 INT $0x80
JAE 2(PC) JAE 2(PC)
CALL notok(SB) CALL notok(SB)
...@@ -253,26 +248,10 @@ TEXT mach_semaphore_signal_all(SB),7,$0 ...@@ -253,26 +248,10 @@ TEXT mach_semaphore_signal_all(SB),7,$0
CALL sysenter(SB) CALL sysenter(SB)
RET RET
/*
descriptor entry format for system call
is the native machine format, ugly as it is:
2-byte limit
3-byte base
1-byte: 0x80=present, 0x60=dpl<<5, 0x1F=type
1-byte: 0x80=limit is *4k, 0x40=32-bit operand size,
0x0F=4 more bits of limit
1 byte: 8 more bits of base
int i386_get_ldt(int, union ldt_entry *, int);
int i386_set_ldt(int, const union ldt_entry *, int);
*/
// setldt(int entry, int address, int limit) // setldt(int entry, int address, int limit)
// entry and limit are ignored.
TEXT setldt(SB),7,$32 TEXT setldt(SB),7,$32
MOVL address+4(FP), BX // aka base MOVL address+4(FP), BX // aka base
MOVL limit+8(FP), CX
/* /*
* When linking against the system libraries, * When linking against the system libraries,
...@@ -288,44 +267,20 @@ TEXT setldt(SB),7,$32 ...@@ -288,44 +267,20 @@ TEXT setldt(SB),7,$32
* of the constant. * of the constant.
*/ */
SUBL $0x468, BX SUBL $0x468, BX
ADDL $0x468, CX
// set up data_desc
LEAL 16(SP), AX // struct data_desc
MOVL $0, 0(AX)
MOVL $0, 4(AX)
MOVW BX, 2(AX)
SHRL $16, BX
MOVB BX, 4(AX)
SHRL $8, BX
MOVB BX, 7(AX)
MOVW CX, 0(AX)
SHRL $16, CX
ANDL $0x0F, CX
ORL $0x40, CX // 32-bit operand size
MOVB CX, 6(AX)
MOVB $0xF2, 5(AX) // r/w data descriptor, dpl=3, present
// call i386_set_ldt(entry, desc, 1) /*
MOVL $0xffffffff, 0(SP) // auto-allocate entry and return in AX * Must set up as USER_CTHREAD segment because
MOVL AX, 4(SP) * Darwin forces that value into %gs for signal handlers,
MOVL $1, 8(SP) * and if we don't set one up, we'll get a recursive
CALL i386_set_ldt(SB) * fault trying to get into the signal handler.
* Since we have to set one up anyway, it might as
// compute segment selector - (entry*8+7) * well be the value we want. So don't bother with
SHLL $3, AX * i386_set_ldt.
ADDL $7, AX */
MOVW AX, GS MOVL BX, 4(SP)
RET MOVL $3, AX // thread_fast_set_cthread_self - machdep call #3
INT $0x82 // sic: 0x82, not 0x80, for machdep call
TEXT i386_set_ldt(SB),7,$0 XORL AX, AX
MOVL $5, AX MOVW GS, AX
INT $0x82 // sic
JAE 2(PC)
CALL notok(SB)
RET RET
GLOBL tlsoffset(SB),$4
...@@ -113,6 +113,7 @@ TEXT bsdthread_create(SB),7,$0 ...@@ -113,6 +113,7 @@ TEXT bsdthread_create(SB),7,$0
MOVQ gg+24(SP), R10 // "pthread" MOVQ gg+24(SP), R10 // "pthread"
// TODO(rsc): why do we get away with 0 flags here but not on 386? // TODO(rsc): why do we get away with 0 flags here but not on 386?
MOVQ $0, R8 // flags MOVQ $0, R8 // flags
MOVQ $0, R9 // paranoia
MOVQ $(0x2000000+360), AX // bsdthread_create MOVQ $(0x2000000+360), AX // bsdthread_create
SYSCALL SYSCALL
JCC 2(PC) JCC 2(PC)
...@@ -146,6 +147,9 @@ TEXT bsdthread_register(SB),7,$0 ...@@ -146,6 +147,9 @@ TEXT bsdthread_register(SB),7,$0
MOVQ $bsdthread_start(SB), DI // threadstart MOVQ $bsdthread_start(SB), DI // threadstart
MOVQ $0, SI // wqthread, not used by us MOVQ $0, SI // wqthread, not used by us
MOVQ $0, DX // pthsize, not used by us MOVQ $0, DX // pthsize, not used by us
MOVQ $0, R10 // dummy_value [sic]
MOVQ $0, R8 // targetconc_ptr
MOVQ $0, R9 // dispatchqueue_offset
MOVQ $(0x2000000+366), AX // bsdthread_register MOVQ $(0x2000000+366), AX // bsdthread_register
SYSCALL SYSCALL
JCC 2(PC) JCC 2(PC)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment