Commit 95100344 authored by Russ Cox's avatar Russ Cox

fix runtime stack overflow bug that gri ran into:

160 - 75 was just barely not enough for deferproc + morestack.

added enum names and bumped to 256 - 128.
added explanation.

changed a few mal() (garbage-collected) to
malloc()/free() (manually collected).

R=ken
OCL=26981
CL=26981
parent c025cf64
...@@ -30,6 +30,13 @@ ...@@ -30,6 +30,13 @@
#include "l.h" #include "l.h"
// see ../../runtime/proc.c:/StackGuard
enum
{
StackSmall = 128,
StackBig = 4096,
};
void void
dodata(void) dodata(void)
{ {
...@@ -602,8 +609,8 @@ dostkoff(void) ...@@ -602,8 +609,8 @@ dostkoff(void)
p->from.offset = 3; p->from.offset = 3;
} }
if(autoffset < 4096) { // do we need to call morestack if(autoffset < StackBig) { // do we need to call morestack?
if(autoffset <= 75) { if(autoffset <= StackSmall) {
// small stack // small stack
p = appendp(p); p = appendp(p);
p->as = ACMPQ; p->as = ACMPQ;
...@@ -618,7 +625,7 @@ dostkoff(void) ...@@ -618,7 +625,7 @@ dostkoff(void)
p = appendp(p); p = appendp(p);
p->as = ALEAQ; p->as = ALEAQ;
p->from.type = D_INDIR+D_SP; p->from.type = D_INDIR+D_SP;
p->from.offset = -(autoffset-75); p->from.offset = -(autoffset-StackSmall);
p->to.type = D_AX; p->to.type = D_AX;
if(q1) { if(q1) {
q1->pcond = p; q1->pcond = p;
......
...@@ -30,6 +30,13 @@ ...@@ -30,6 +30,13 @@
#include "l.h" #include "l.h"
// see ../../runtime/proc.c:/StackGuard
enum
{
StackSmall = 128,
StackBig = 4096,
};
void void
dodata(void) dodata(void)
{ {
...@@ -575,8 +582,8 @@ dostkoff(void) ...@@ -575,8 +582,8 @@ dostkoff(void)
p->from.offset = 3; p->from.offset = 3;
} }
if(autoffset < 4096) { // do we need to call morestack if(autoffset < StackBig) { // do we need to call morestack
if(autoffset <= 75) { if(autoffset <= StackSmall) {
// small stack // small stack
p = appendp(p); p = appendp(p);
p->as = ACMPL; p->as = ACMPL;
...@@ -591,7 +598,7 @@ dostkoff(void) ...@@ -591,7 +598,7 @@ dostkoff(void)
p = appendp(p); p = appendp(p);
p->as = ALEAL; p->as = ALEAL;
p->from.type = D_INDIR+D_SP; p->from.type = D_INDIR+D_SP;
p->from.offset = -(autoffset-75); p->from.offset = -(autoffset-StackSmall);
p->to.type = D_AX; p->to.type = D_AX;
if(q1) { if(q1) {
q1->pcond = p; q1->pcond = p;
......
...@@ -178,7 +178,7 @@ itype(Sigi *si, Sigt *st, int32 canfail) ...@@ -178,7 +178,7 @@ itype(Sigi *si, Sigt *st, int32 canfail)
} }
ni = si->size; ni = si->size;
m = mal(sizeof(*m) + ni*sizeof(m->fun[0])); m = malloc(sizeof(*m) + ni*sizeof(m->fun[0]));
m->sigi = si; m->sigi = si;
m->sigt = st; m->sigt = st;
...@@ -692,8 +692,8 @@ fakesigt(string type, bool indir) ...@@ -692,8 +692,8 @@ fakesigt(string type, bool indir)
} }
} }
sigt = mal(sizeof(*sigt)); sigt = malloc(sizeof(*sigt));
sigt->name = mal(type->len + 1); sigt->name = malloc(type->len + 1);
mcpy(sigt->name, type->str, type->len); mcpy(sigt->name, type->str, type->len);
sigt->alg = AFAKE; sigt->alg = AFAKE;
......
...@@ -140,108 +140,6 @@ sys·Goexit(void) ...@@ -140,108 +140,6 @@ sys·Goexit(void)
sys·Gosched(); sys·Gosched();
} }
G*
malg(int32 stacksize)
{
G *g;
byte *stk;
// 160 is the slop amount known to the stack growth code
g = malloc(sizeof(G));
stk = stackalloc(160 + stacksize);
g->stack0 = stk;
g->stackguard = stk + 160;
g->stackbase = stk + 160 + stacksize;
return g;
}
#pragma textflag 7
void
sys·newproc(int32 siz, byte* fn, byte* arg0)
{
byte *stk, *sp;
G *newg;
//printf("newproc siz=%d fn=%p", siz, fn);
siz = (siz+7) & ~7;
if(siz > 1024)
throw("sys·newproc: too many args");
lock(&sched);
if((newg = gfget()) != nil){
newg->status = Gwaiting;
} else {
newg = malg(4096);
newg->status = Gwaiting;
newg->alllink = allg;
allg = newg;
}
stk = newg->stack0;
newg->stackguard = stk+160;
sp = stk + 4096 - 4*8;
newg->stackbase = sp;
sp -= siz;
mcpy(sp, (byte*)&arg0, siz);
sp -= sizeof(uintptr);
*(byte**)sp = (byte*)sys·Goexit;
sp -= sizeof(uintptr); // retpc used by gogo
newg->sched.SP = sp;
newg->sched.PC = fn;
sched.gcount++;
goidgen++;
newg->goid = goidgen;
readylocked(newg);
unlock(&sched);
//printf(" goid=%d\n", newg->goid);
}
#pragma textflag 7
void
sys·deferproc(int32 siz, byte* fn, byte* arg0)
{
Defer *d;
d = mal(sizeof(*d) + siz - sizeof(d->args));
d->fn = fn;
d->sp = (byte*)&arg0;
d->siz = siz;
mcpy(d->args, d->sp, d->siz);
d->link = g->defer;
g->defer = d;
}
#pragma textflag 7
void
sys·deferreturn(int32 arg0)
{
// warning: jmpdefer knows the frame size
// of this routine. dont change anything
// that might change the frame size
Defer *d;
byte *sp;
d = g->defer;
if(d == nil)
return;
sp = (byte*)&arg0;
if(d->sp != sp)
return;
mcpy(d->sp, d->args, d->siz);
g->defer = d->link;
jmpdefer(d->fn);
}
void void
tracebackothers(G *me) tracebackothers(G *me)
{ {
...@@ -634,29 +532,70 @@ sys·exitsyscall(void) ...@@ -634,29 +532,70 @@ sys·exitsyscall(void)
sys·Gosched(); sys·Gosched();
} }
/*
// * stack layout parameters.
// the calling sequence for a routine tha * known to linkers.
// needs N bytes stack, A args. *
// * g->stackguard is set to point StackGuard bytes
// N1 = (N+160 > 4096)? N+160: 0 * above the bottom of the stack. each function
// A1 = A * compares its stack pointer against g->stackguard
// * to check for overflow. to cut one instruction from
// if N <= 75 * the check sequence for functions with tiny frames,
// CMPQ SP, 0(R15) * the stack is allowed to protrude StackSmall bytes
// JHI 4(PC) * below the stack guard. functions with large frames
// MOVQ $(N1<<0) | (A1<<32)), AX * don't bother with the check and always call morestack.
// MOVQ AX, 0(R14) * the sequences are:
// CALL sys·morestack(SB) *
// * stack frame size <= StackSmall:
// if N > 75 * CMPQ guard, SP
// LEAQ (-N-75)(SP), AX * JHI 3(PC)
// CMPQ AX, 0(R15) * MOVQ m->morearg, $((frame << 32) | argsize)
// JHI 4(PC) * CALL sys.morestack(SB)
// MOVQ $(N1<<0) | (A1<<32)), AX *
// MOVQ AX, 0(R14) * stack frame size > StackSmall but < StackBig
// CALL sys·morestack(SB) * LEAQ (frame-StackSmall)(SP), R0
// * CMPQ guard, R0
* JHI 3(PC)
* MOVQ m->morearg, $((frame << 32) | argsize)
* CALL sys.morestack(SB)
*
* stack frame size >= StackBig:
* MOVQ m->morearg, $((frame << 32) | argsize)
* CALL sys.morestack(SB)
*
* the bottom StackGuard - StackSmall bytes are important:
* there has to be enough room to execute functions that
* refuse to check for stack overflow, either because they
* need to be adjacent to the actual caller's frame (sys.deferproc)
* or because they handle the imminent stack overflow (sys.morestack).
*
* for example, sys.deferproc might call malloc,
* which does one of the above checks (without allocating a full frame),
* which might trigger a call to sys.morestack.
* this sequence needs to fit in the bottom section of the stack.
* on amd64, sys.morestack's frame is 40 bytes, and
* sys.deferproc's frame is 56 bytes. that fits well within
* the StackGuard - StackSmall = 128 bytes at the bottom.
* there may be other sequences lurking or yet to be written
* that require more stack. sys.morestack checks to make sure
* the stack has not completely overflowed and should
* catch such sequences.
*/
enum
{
// byte offset of stack guard (g->stackguard) above bottom of stack.
StackGuard = 256,
// checked frames are allowed to protrude below the guard by
// this many bytes. this saves an instruction in the checking
// sequence when the stack frame is tiny.
StackSmall = 128,
// extra space in the frame (beyond the function for which
// the frame is allocated) is assumed not to be much bigger
// than this amount. it may not be used efficiently if it is.
StackBig = 4096,
};
void void
oldstack(void) oldstack(void)
...@@ -684,7 +623,7 @@ oldstack(void) ...@@ -684,7 +623,7 @@ oldstack(void)
oldbase = (uint64)top->oldbase; oldbase = (uint64)top->oldbase;
oldguard = (uint64)top->oldguard; oldguard = (uint64)top->oldguard;
stackfree((byte*)m->curg->stackguard - 512 - 160); stackfree((byte*)m->curg->stackguard - StackGuard);
m->curg->stackbase = (byte*)oldbase; m->curg->stackbase = (byte*)oldbase;
m->curg->stackguard = (byte*)oldguard; m->curg->stackguard = (byte*)oldguard;
...@@ -712,28 +651,22 @@ lessstack(void) ...@@ -712,28 +651,22 @@ lessstack(void)
void void
newstack(void) newstack(void)
{ {
int32 siz1, siz2; int32 frame, args;
Stktop *top; Stktop *top;
byte *stk, *sp; byte *stk, *sp;
void (*fn)(void); void (*fn)(void);
siz1 = m->morearg & 0xffffffffLL; frame = m->morearg & 0xffffffffLL;
siz2 = (m->morearg>>32) & 0xffffLL; args = (m->morearg>>32) & 0xffffLL;
// prints("newstack siz1="); // printf("newstack frame=%d args=%d moresp=%p\n", frame, args, m->moresp);
// sys·printint(siz1);
// prints(" siz2=");
// sys·printint(siz2);
// prints(" moresp=");
// sys·printpointer(m->moresp);
// prints("\n");
if(siz1 < 4096) if(frame < StackBig)
siz1 = 4096; frame = StackBig;
stk = stackalloc(siz1 + 1024); frame += 1024; // for more functions, Stktop.
stk += 512; stk = stackalloc(frame);
top = (Stktop*)(stk+siz1-sizeof(*top)); top = (Stktop*)(stk+frame-sizeof(*top));
top->oldbase = m->curg->stackbase; top->oldbase = m->curg->stackbase;
top->oldguard = m->curg->stackguard; top->oldguard = m->curg->stackguard;
...@@ -741,22 +674,25 @@ newstack(void) ...@@ -741,22 +674,25 @@ newstack(void)
top->magic = m->morearg; top->magic = m->morearg;
m->curg->stackbase = (byte*)top; m->curg->stackbase = (byte*)top;
m->curg->stackguard = stk + 160; m->curg->stackguard = stk + StackGuard;
sp = (byte*)top; sp = (byte*)top;
if(siz2 > 0) { if(args > 0) {
siz2 = (siz2+7) & ~7; // Copy args. There have been two function calls
sp -= siz2; // since they got pushed, so skip over those return
mcpy(sp, m->moresp+16, siz2); // addresses.
args = (args+7) & ~7;
sp -= args;
mcpy(sp, m->moresp+2*sizeof(uintptr), args);
} }
g = m->curg; g = m->curg;
fn = (void(*)(void))(*(uint64*)m->moresp);
// prints("fn="); // sys.morestack's return address
// sys·printpointer(fn); fn = (void(*)(void))(*(uintptr*)m->moresp);
// prints("\n");
// printf("fn=%p\n", fn);
setspgoto(sp, fn, retfromnewstack); setspgoto(sp, fn, retfromnewstack);
...@@ -769,13 +705,133 @@ sys·morestack(uint64 u) ...@@ -769,13 +705,133 @@ sys·morestack(uint64 u)
{ {
while(g == m->g0) { while(g == m->g0) {
// very bad news // very bad news
*(int32*)123 = 123; *(int32*)0x1001 = 123;
}
// Morestack's frame is about 0x30 bytes on amd64.
// If that the frame ends below the stack bottom, we've already
// overflowed. Stop right now.
while((byte*)&u - 0x30 < m->curg->stackguard - StackGuard) {
// very bad news
*(int32*)0x1002 = 123;
} }
g = m->g0; g = m->g0;
m->moresp = (byte*)(&u-1); m->moresp = (byte*)(&u-1);
setspgoto(m->sched.SP, newstack, nil); setspgoto(m->sched.SP, newstack, nil);
*(int32*)234 = 123; // never return *(int32*)0x1003 = 123; // never return
}
G*
malg(int32 stacksize)
{
G *g;
byte *stk;
g = malloc(sizeof(G));
stk = stackalloc(stacksize + StackGuard);
g->stack0 = stk;
g->stackguard = stk + StackGuard;
g->stackbase = stk + StackGuard + stacksize;
return g;
}
/*
* Newproc and deferproc need to be textflag 7
* (no possible stack split when nearing overflow)
* because they assume that the arguments to fn
* are available sequentially beginning at &arg0.
* If a stack split happened, only the one word
* arg0 would be copied. It's okay if any functions
* they call split the stack below the newproc frame.
*/
#pragma textflag 7
void
sys·newproc(int32 siz, byte* fn, byte* arg0)
{
byte *stk, *sp;
G *newg;
//printf("newproc siz=%d fn=%p", siz, fn);
siz = (siz+7) & ~7;
if(siz > 1024)
throw("sys·newproc: too many args");
lock(&sched);
if((newg = gfget()) != nil){
newg->status = Gwaiting;
} else {
newg = malg(4096);
newg->status = Gwaiting;
newg->alllink = allg;
allg = newg;
}
stk = newg->stack0;
newg->stackguard = stk+StackGuard;
sp = stk + 4096 - 4*8;
newg->stackbase = sp;
sp -= siz;
mcpy(sp, (byte*)&arg0, siz);
sp -= sizeof(uintptr);
*(byte**)sp = (byte*)sys·Goexit;
sp -= sizeof(uintptr); // retpc used by gogo
newg->sched.SP = sp;
newg->sched.PC = fn;
sched.gcount++;
goidgen++;
newg->goid = goidgen;
readylocked(newg);
unlock(&sched);
//printf(" goid=%d\n", newg->goid);
}
#pragma textflag 7
void
sys·deferproc(int32 siz, byte* fn, byte* arg0)
{
Defer *d;
d = malloc(sizeof(*d) + siz - sizeof(d->args));
d->fn = fn;
d->sp = (byte*)&arg0;
d->siz = siz;
mcpy(d->args, d->sp, d->siz);
d->link = g->defer;
g->defer = d;
}
#pragma textflag 7
void
sys·deferreturn(int32 arg0)
{
// warning: jmpdefer knows the frame size
// of this routine. dont change anything
// that might change the frame size
Defer *d;
byte *sp;
d = g->defer;
if(d == nil)
return;
sp = (byte*)&arg0;
if(d->sp != sp)
return;
mcpy(d->sp, d->args, d->siz);
g->defer = d->link;
sp = d->fn;
free(d);
jmpdefer(sp);
} }
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