diff options
author | Russ Cox <rsc@golang.org> | 2014-09-16 10:36:38 -0400 |
---|---|---|
committer | Russ Cox <rsc@golang.org> | 2014-09-16 10:36:38 -0400 |
commit | c9afff3fcecc335ae25cef3ec5f4040fb30e2f9a (patch) | |
tree | 08460053cf605ffda28bdf07bda1879fd6160c17 /src/runtime/stack.c | |
parent | 5819f77545053174374fc02ff3e09b6a6591c22c (diff) | |
download | go-c9afff3fcecc335ae25cef3ec5f4040fb30e2f9a.tar.gz |
runtime: use traceback to traverse defer structures
This makes the GC and the stack copying agree about how
to interpret the defer structures. Previously, only the stack
copying treated them precisely.
This removes an untyped memory allocation and fixes
at least three copystack bugs.
To make sure the GC can find the deferred argument
frame until it has been copied, keep a Defer on the defer list
during its execution.
In addition to making it possible to remove the untyped
memory allocation, keeping the Defer on the list fixes
two races between copystack and execution of defers
(in both gopanic and Goexit). The problem is that once
the defer has been taken off the list, a stack copy that
happens before the deferred arguments have been copied
back to the stack will not update the arguments correctly.
The new tests TestDeferPtrsPanic and TestDeferPtrsGoexit
(variations on the existing TestDeferPtrs) pass now but
failed before this CL.
In addition to those fixes, keeping the Defer on the list
helps correct a dangling pointer error during copystack.
The traceback routines walk the Defer chain to provide
information about where a panic may resume execution.
When the executing Defer was not on the Defer chain
but instead linked from the Panic chain, the traceback
had to walk the Panic chain too. But Panic structs are
on the stack and being updated by copystack.
Traceback's use of the Panic chain while copystack is
updating those structs means that it can follow an
updated pointer and find itself reading from the new stack.
The new stack is usually all zeros, so it sees an incorrect
early end to the chain. The new TestPanicUseStack makes
this happen at tip and dies when adjustdefers finds an
unexpected argp. The new StackCopyPoison mode
causes an earlier bad dereference instead.
By keeping the Defer on the list, traceback can avoid
walking the Panic chain at all, making it okay for copystack
to update the Panics.
We'd have the same problem for any Defers on the stack.
There was only one: gopanic's dabort. Since we are not
taking the executing Defer off the chain, we can use it
to do what dabort was doing, and then there are no
Defers on the stack ever, so it is okay for traceback to use
the Defer chain even while copystack is executing:
copystack cannot modify the Defer chain.
LGTM=khr
R=khr
CC=dvyukov, golang-codereviews, iant, rlh
https://codereview.appspot.com/141490043
Diffstat (limited to 'src/runtime/stack.c')
-rw-r--r-- | src/runtime/stack.c | 136 |
1 files changed, 62 insertions, 74 deletions
diff --git a/src/runtime/stack.c b/src/runtime/stack.c index f29266eb6..143b645e4 100644 --- a/src/runtime/stack.c +++ b/src/runtime/stack.c @@ -23,6 +23,7 @@ enum StackDebug = 0, StackFromSystem = 0, // allocate stacks from system memory instead of the heap StackFaultOnFree = 0, // old stacks are mapped noaccess to detect use after free + StackPoisonCopy = 0, // fill stack that should not be accessed with garbage, to detect bad dereferences during copy StackCache = 1, }; @@ -353,6 +354,24 @@ struct AdjustInfo { uintptr delta; // ptr distance from old to new stack (newbase - oldbase) }; +// Adjustpointer checks whether *vpp is in the old stack described by adjinfo. +// If so, it rewrites *vpp to point into the new stack. +static void +adjustpointer(AdjustInfo *adjinfo, void *vpp) +{ + byte **pp, *p; + + pp = vpp; + p = *pp; + if(StackDebug >= 4) + runtime·printf(" %p:%p\n", pp, p); + if(adjinfo->old.lo <= (uintptr)p && (uintptr)p < adjinfo->old.hi) { + *pp = p + adjinfo->delta; + if(StackDebug >= 3) + runtime·printf(" adjust ptr %p: %p -> %p\n", pp, p, *pp); + } +} + // bv describes the memory starting at address scanp. // Adjust any pointers contained therein. static void @@ -447,6 +466,11 @@ adjustframe(Stkframe *frame, void *arg) uintptr targetpc; adjinfo = arg; + targetpc = frame->continpc; + if(targetpc == 0) { + // Frame is dead. + return true; + } f = frame->fn; if(StackDebug >= 2) runtime·printf(" adjusting %s frame=[%p,%p] pc=%p continpc=%p\n", runtime·funcname(f), frame->sp, frame->fp, frame->pc, frame->continpc); @@ -456,11 +480,6 @@ adjustframe(Stkframe *frame, void *arg) // have full GC info for it (because it is written in asm). return true; } - targetpc = frame->continpc; - if(targetpc == 0) { - // Frame is dead. - return true; - } if(targetpc != f->entry) targetpc--; pcdata = runtime·pcdatavalue(f, PCDATA_StackMapIndex, targetpc); @@ -495,103 +514,53 @@ adjustframe(Stkframe *frame, void *arg) runtime·printf(" args\n"); adjustpointers((byte**)frame->argp, &bv, adjinfo, nil); } + return true; } static void adjustctxt(G *gp, AdjustInfo *adjinfo) { - if(adjinfo->old.lo <= (uintptr)gp->sched.ctxt && (uintptr)gp->sched.ctxt < adjinfo->old.hi) - gp->sched.ctxt = (byte*)gp->sched.ctxt + adjinfo->delta; + adjustpointer(adjinfo, &gp->sched.ctxt); } static void adjustdefers(G *gp, AdjustInfo *adjinfo) { - Defer *d, **dp; - Func *f; - FuncVal *fn; - StackMap *stackmap; - BitVector bv; + Defer *d; + bool (*cb)(Stkframe*, void*); - for(dp = &gp->defer, d = *dp; d != nil; dp = &d->link, d = *dp) { - if(adjinfo->old.lo <= (uintptr)d && (uintptr)d < adjinfo->old.hi) { - // The Defer record is on the stack. Its fields will - // get adjusted appropriately. - // This only happens for runtime.main and runtime.gopanic now, - // but a compiler optimization could do more of this. - // If such an optimization were introduced, Defer.argp should - // change to have pointer type so that it will be updated by - // the stack copying. Today both of those on-stack defers - // set argp = NoArgs, so no adjustment is necessary. - *dp = (Defer*)((byte*)d + adjinfo->delta); - continue; - } - if(d->argp == NoArgs) - continue; - if(d->argp < adjinfo->old.lo || adjinfo->old.hi <= d->argp) { - runtime·printf("runtime: adjustdefers argp=%p stk=%p %p\n", d->argp, adjinfo->old.lo, adjinfo->old.hi); - runtime·throw("adjustdefers: unexpected argp"); - } - d->argp += adjinfo->delta; - fn = d->fn; - if(fn == nil) { - // Defer of nil function. It will panic when run. See issue 8047. - continue; - } - f = runtime·findfunc((uintptr)fn->fn); - if(f == nil) - runtime·throw("can't adjust unknown defer"); - if(StackDebug >= 4) - runtime·printf(" checking defer %s\n", runtime·funcname(f)); - // Defer's FuncVal might be on the stack - if(adjinfo->old.lo <= (uintptr)fn && (uintptr)fn < adjinfo->old.hi) { - if(StackDebug >= 3) - runtime·printf(" adjust defer fn %s\n", runtime·funcname(f)); - d->fn = (FuncVal*)((byte*)fn + adjinfo->delta); - } else { - // deferred function's args might point into the stack. - if(StackDebug >= 3) - runtime·printf(" adjust deferred args for %s\n", runtime·funcname(f)); - stackmap = runtime·funcdata(f, FUNCDATA_ArgsPointerMaps); - if(stackmap == nil) - runtime·throw("runtime: deferred function has no arg ptr map"); - bv = runtime·stackmapdata(stackmap, 0); - adjustpointers(d->args, &bv, adjinfo, f); - } - // The FuncVal may have pointers in it, but fortunately for us - // the compiler won't put pointers into the stack in a - // heap-allocated FuncVal. - // One day if we do need to check this, we can use the gc bits in the - // heap to do the right thing (although getting the size will be expensive). + // Adjust defer argument blocks the same way we adjust active stack frames. + cb = adjustframe; + runtime·tracebackdefers(gp, &cb, adjinfo); + + // Adjust pointers in the Defer structs. + // Defer structs themselves are never on the stack. + for(d = gp->defer; d != nil; d = d->link) { + adjustpointer(adjinfo, &d->fn); + adjustpointer(adjinfo, &d->argp); + adjustpointer(adjinfo, &d->panic); } } static void adjustpanics(G *gp, AdjustInfo *adjinfo) { - // Panic structs are all on the stack - // and are adjusted by stack copying. - // The only pointer we need to update is gp->panic, the head of the list. - if(adjinfo->old.lo <= (uintptr)gp->panic && (uintptr)gp->panic < adjinfo->old.hi) - gp->panic = (Panic*)((byte*)gp->panic + adjinfo->delta); + // Panics are on stack and already adjusted. + // Update pointer to head of list in G. + adjustpointer(adjinfo, &gp->panic); } static void adjustsudogs(G *gp, AdjustInfo *adjinfo) { SudoG *s; - byte *e; // the data elements pointed to by a SudoG structure // might be in the stack. for(s = gp->waiting; s != nil; s = s->waitlink) { - e = s->elem; - if(adjinfo->old.lo <= (uintptr)e && (uintptr)e < adjinfo->old.hi) - s->elem = e + adjinfo->delta; - e = (byte*)s->selectdone; - if(adjinfo->old.lo <= (uintptr)e && (uintptr)e < adjinfo->old.hi) - s->selectdone = (uint32*)(e + adjinfo->delta); + adjustpointer(adjinfo, &s->elem); + adjustpointer(adjinfo, &s->selectdone); } } @@ -604,6 +573,7 @@ copystack(G *gp, uintptr newsize) AdjustInfo adjinfo; uint32 oldstatus; bool (*cb)(Stkframe*, void*); + byte *p, *ep; if(gp->syscallsp != 0) runtime·throw("stack growth not allowed in system call"); @@ -614,6 +584,12 @@ copystack(G *gp, uintptr newsize) // allocate new stack new = runtime·stackalloc(newsize); + if(StackPoisonCopy) { + p = (byte*)new.lo; + ep = (byte*)new.hi; + while(p < ep) + *p++ = 0xfd; + } if(StackDebug >= 1) runtime·printf("copystack gp=%p [%p %p %p]/%d -> [%p %p %p]/%d\n", gp, old.lo, old.hi-used, old.hi, (int32)(old.hi-old.lo), new.lo, new.hi-used, new.hi, (int32)newsize); @@ -631,6 +607,12 @@ copystack(G *gp, uintptr newsize) adjustsudogs(gp, &adjinfo); // copy the stack to the new location + if(StackPoisonCopy) { + p = (byte*)new.lo; + ep = (byte*)new.hi; + while(p < ep) + *p++ = 0xfb; + } runtime·memmove((byte*)new.hi - used, (byte*)old.hi - used, used); oldstatus = runtime·readgstatus(gp); @@ -648,6 +630,12 @@ copystack(G *gp, uintptr newsize) runtime·casgstatus(gp, Gcopystack, oldstatus); // oldstatus is Gwaiting or Grunnable // free old stack + if(StackPoisonCopy) { + p = (byte*)old.lo; + ep = (byte*)old.hi; + while(p < ep) + *p++ = 0xfc; + } runtime·stackfree(old); } |