19d4baed57
PR go/60406 runtime: Check callers in can_recover if return address doesn't match. Also use __builtin_extract_return_address and tighten up the checks in FFI code. Fixes PR 60406. From-SVN: r216003
233 lines
4.9 KiB
C
233 lines
4.9 KiB
C
// Copyright 2012 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
#include "runtime.h"
|
|
#include "malloc.h"
|
|
#include "go-defer.h"
|
|
#include "go-panic.h"
|
|
|
|
// Code related to defer, panic and recover.
|
|
|
|
uint32 runtime_panicking;
|
|
static Lock paniclk;
|
|
|
|
// Allocate a Defer, usually using per-P pool.
|
|
// Each defer must be released with freedefer.
|
|
Defer*
|
|
runtime_newdefer()
|
|
{
|
|
Defer *d;
|
|
P *p;
|
|
|
|
d = nil;
|
|
p = runtime_m()->p;
|
|
d = p->deferpool;
|
|
if(d)
|
|
p->deferpool = d->__next;
|
|
if(d == nil) {
|
|
// deferpool is empty
|
|
d = runtime_malloc(sizeof(Defer));
|
|
}
|
|
return d;
|
|
}
|
|
|
|
// Free the given defer.
|
|
// The defer cannot be used after this call.
|
|
void
|
|
runtime_freedefer(Defer *d)
|
|
{
|
|
P *p;
|
|
|
|
if(d->__special)
|
|
return;
|
|
p = runtime_m()->p;
|
|
d->__next = p->deferpool;
|
|
p->deferpool = d;
|
|
// No need to wipe out pointers in argp/pc/fn/args,
|
|
// because we empty the pool before GC.
|
|
}
|
|
|
|
// Run all deferred functions for the current goroutine.
|
|
// This is noinline for go_can_recover.
|
|
static void __go_rundefer (void) __attribute__ ((noinline));
|
|
static void
|
|
__go_rundefer(void)
|
|
{
|
|
G *g;
|
|
Defer *d;
|
|
|
|
g = runtime_g();
|
|
while((d = g->defer) != nil) {
|
|
void (*pfn)(void*);
|
|
|
|
g->defer = d->__next;
|
|
pfn = d->__pfn;
|
|
d->__pfn = nil;
|
|
if (pfn != nil)
|
|
(*pfn)(d->__arg);
|
|
runtime_freedefer(d);
|
|
}
|
|
}
|
|
|
|
void
|
|
runtime_startpanic(void)
|
|
{
|
|
M *m;
|
|
|
|
m = runtime_m();
|
|
if(runtime_mheap.cachealloc.size == 0) { // very early
|
|
runtime_printf("runtime: panic before malloc heap initialized\n");
|
|
m->mallocing = 1; // tell rest of panic not to try to malloc
|
|
} else if(m->mcache == nil) // can happen if called from signal handler or throw
|
|
m->mcache = runtime_allocmcache();
|
|
switch(m->dying) {
|
|
case 0:
|
|
m->dying = 1;
|
|
if(runtime_g() != nil)
|
|
runtime_g()->writebuf = nil;
|
|
runtime_xadd(&runtime_panicking, 1);
|
|
runtime_lock(&paniclk);
|
|
if(runtime_debug.schedtrace > 0 || runtime_debug.scheddetail > 0)
|
|
runtime_schedtrace(true);
|
|
runtime_freezetheworld();
|
|
return;
|
|
case 1:
|
|
// Something failed while panicing, probably the print of the
|
|
// argument to panic(). Just print a stack trace and exit.
|
|
m->dying = 2;
|
|
runtime_printf("panic during panic\n");
|
|
runtime_dopanic(0);
|
|
runtime_exit(3);
|
|
case 2:
|
|
// This is a genuine bug in the runtime, we couldn't even
|
|
// print the stack trace successfully.
|
|
m->dying = 3;
|
|
runtime_printf("stack trace unavailable\n");
|
|
runtime_exit(4);
|
|
default:
|
|
// Can't even print! Just exit.
|
|
runtime_exit(5);
|
|
}
|
|
}
|
|
|
|
void
|
|
runtime_dopanic(int32 unused __attribute__ ((unused)))
|
|
{
|
|
G *g;
|
|
static bool didothers;
|
|
bool crash;
|
|
int32 t;
|
|
|
|
g = runtime_g();
|
|
if(g->sig != 0)
|
|
runtime_printf("[signal %x code=%p addr=%p]\n",
|
|
g->sig, (void*)g->sigcode0, (void*)g->sigcode1);
|
|
|
|
if((t = runtime_gotraceback(&crash)) > 0){
|
|
if(g != runtime_m()->g0) {
|
|
runtime_printf("\n");
|
|
runtime_goroutineheader(g);
|
|
runtime_traceback();
|
|
runtime_printcreatedby(g);
|
|
} else if(t >= 2 || runtime_m()->throwing > 0) {
|
|
runtime_printf("\nruntime stack:\n");
|
|
runtime_traceback();
|
|
}
|
|
if(!didothers) {
|
|
didothers = true;
|
|
runtime_tracebackothers(g);
|
|
}
|
|
}
|
|
runtime_unlock(&paniclk);
|
|
if(runtime_xadd(&runtime_panicking, -1) != 0) {
|
|
// Some other m is panicking too.
|
|
// Let it print what it needs to print.
|
|
// Wait forever without chewing up cpu.
|
|
// It will exit when it's done.
|
|
static Lock deadlock;
|
|
runtime_lock(&deadlock);
|
|
runtime_lock(&deadlock);
|
|
}
|
|
|
|
if(crash)
|
|
runtime_crash();
|
|
|
|
runtime_exit(2);
|
|
}
|
|
|
|
bool
|
|
runtime_canpanic(G *gp)
|
|
{
|
|
M *m = runtime_m();
|
|
byte g;
|
|
|
|
USED(&g); // don't use global g, it points to gsignal
|
|
|
|
// Is it okay for gp to panic instead of crashing the program?
|
|
// Yes, as long as it is running Go code, not runtime code,
|
|
// and not stuck in a system call.
|
|
if(gp == nil || gp != m->curg)
|
|
return false;
|
|
if(m->locks-m->softfloat != 0 || m->mallocing != 0 || m->throwing != 0 || m->gcing != 0 || m->dying != 0)
|
|
return false;
|
|
if(gp->status != Grunning)
|
|
return false;
|
|
#ifdef GOOS_windows
|
|
if(m->libcallsp != 0)
|
|
return false;
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
void
|
|
runtime_throw(const char *s)
|
|
{
|
|
M *mp;
|
|
|
|
mp = runtime_m();
|
|
if(mp->throwing == 0)
|
|
mp->throwing = 1;
|
|
runtime_startpanic();
|
|
runtime_printf("fatal error: %s\n", s);
|
|
runtime_dopanic(0);
|
|
*(int32*)0 = 0; // not reached
|
|
runtime_exit(1); // even more not reached
|
|
}
|
|
|
|
void
|
|
runtime_panicstring(const char *s)
|
|
{
|
|
Eface err;
|
|
|
|
if(runtime_m()->mallocing) {
|
|
runtime_printf("panic: %s\n", s);
|
|
runtime_throw("panic during malloc");
|
|
}
|
|
if(runtime_m()->gcing) {
|
|
runtime_printf("panic: %s\n", s);
|
|
runtime_throw("panic during gc");
|
|
}
|
|
if(runtime_m()->locks) {
|
|
runtime_printf("panic: %s\n", s);
|
|
runtime_throw("panic holding locks");
|
|
}
|
|
runtime_newErrorCString(s, &err);
|
|
runtime_panic(err);
|
|
}
|
|
|
|
void runtime_Goexit (void) __asm__ (GOSYM_PREFIX "runtime.Goexit");
|
|
|
|
void
|
|
runtime_Goexit(void)
|
|
{
|
|
__go_rundefer();
|
|
runtime_goexit();
|
|
}
|
|
|
|
void
|
|
runtime_panicdivide(void)
|
|
{
|
|
runtime_panicstring("integer divide by zero");
|
|
}
|