On Mon, 24 Mar 1997 17:29:29 EST, Ken Fox wrote:
>Gurusamy Sarathy wrote:
>> >> Ken Fox wrote:
>> >> > The trouble with die happens in the longjmp to top_env ...
>> Testcase?
>
>Here's a good example that demonstrates both of my longjmp related
>problems. I'm using 5.003_94 on Solaris 2.5.1 (more info at
>bottom). I think that the perl_call_* API is being used correctly.
>
>---------------------------------------- BEGIN crash.c
>#include <EXTERN.h>
>#include <perl.h>
>static PerlInterpreter *my_perl;
>
>int call_sub(SV *sub)
>{
> int result = -1;
> int count;
> dSP;
>
> ENTER;
> SAVETMPS;
>
> PUSHMARK(sp);
> XPUSHs(sv_2mortal(newSViv(1)));
> PUTBACK;
> count = perl_call_sv(sub, G_SCALAR);
> SPAGAIN;
> if (count == 1) result = POPi;
> PUTBACK;
>
> FREETMPS;
> LEAVE;
> return result;
>}
>
>int main(int argc, char *argv[], char *envp[])
>{
> char *perl_args[] = { 0, "-e",
> "sub ok { $_[0] + 1; } sub crash { die 'crash\n'; }", 0 };
> my_perl = perl_alloc();
> perl_construct(my_perl);
> perl_parse(my_perl, 0, 3, perl_args, 0);
> perl_run(my_perl);
>
> /* this call works fine -- no error */
> printf(" sub ok: return = %d\n", call_sub((SV *)perl_get_cv("ok", FALSE)));
> /* this call eventually inokes die() which mangles the C stack with long jump */
> printf("sub crash: return = %d\n", call_sub((SV *)perl_get_cv("crash", FALSE)));
> /* this call is bogus but perl mangles the C stack with long jump trying to
> tell me about it. */
> printf(" sv_undef: return = %d\n", call_sub(&sv_undef));
> perl_destruct(my_perl);
> perl_free(my_perl);
> return 0;
>}
>---------------------------------------- END crash.c
>
>I don't expect either of these cases to trap the error and go on -- I'd
>have used G_EVAL to do that. What I do expect is that the C stack isn't
>scrambled when the error occurs -- that makes it virtually impossible to
>catch in a debugger:
Here's a patch for the above problem, which is symptomatic of larger
problems with perl_call_*() calls that happen outside perl_run() or
perl_parse(). Perl invokes longjmp() without checking if an associated
setjmp() exists. This is likely to cause coredumps galore for all the
perl embedfellows out there.
Note the size of this patch is mostly due to the conversion of the
DOCATCH() business to macros in order to give it a semblance of
order. It also does away with redundant calls to setjmp() (the
:restart branch in perl_call_sv()), and uses the C stack instead of
Copy().
I finished this patch yesterday, and gave it a day with my production
embeded app, so I'm fairly sure it don't have no bugs. :-)
p5p-msgid:
199703280031.TAA05711@aatma.engin.umich.edu
dSP;
BINOP myop;
SV* res;
- bool oldmustcatch = mustcatch;
+ bool oldcatch = CATCH_GET;
+ CATCH_SET(TRUE);
Zero(&myop, 1, BINOP);
myop.op_last = (OP *) &myop;
myop.op_next = Nullop;
myop.op_flags = OPf_KNOW|OPf_STACKED;
- mustcatch = TRUE;
ENTER;
SAVESPTR(op);
op = (OP *) &myop;
res=POPs;
PUTBACK;
- mustcatch = oldmustcatch;
+ CATCH_SET(oldcatch);
if (postpr) {
int ans;
minus_n
minus_p
multiline
-mustcatch
mystack_base
mystack_mark
mystack_max
sortstack
sortstash
splitstr
+start_env
statcache
statgv
statname
init_ids();
+ start_env.je_prev = NULL;
+ start_env.je_ret = -1;
+ start_env.je_mustcatch = TRUE;
+ top_env = &start_env;
STATUS_ALL_SUCCESS;
SET_NUMERIC_STANDARD();
char *validarg = "";
I32 oldscope;
AV* comppadlist;
+ dJMPENV;
#ifdef SETUID_SCRIPTS_ARE_SECURE_NOW
#ifdef IAMSUID
time(&basetime);
oldscope = scopestack_ix;
- mustcatch = FALSE;
- switch (Sigsetjmp(top_env,1)) {
+ switch (JMPENV_PUSH) {
case 1:
STATUS_ALL_FAILURE;
/* FALL THROUGH */
curstash = defstash;
if (endav)
call_list(oldscope, endav);
+ JMPENV_POP;
return STATUS_NATIVE_EXPORT;
case 3:
- mustcatch = FALSE;
+ JMPENV_POP;
PerlIO_printf(PerlIO_stderr(), "panic: top_env\n");
return 1;
}
ENTER;
restartop = 0;
+ JMPENV_POP;
return 0;
}
perl_run(sv_interp)
PerlInterpreter *sv_interp;
{
+ dJMPENV;
I32 oldscope;
if (!(curinterp = sv_interp))
oldscope = scopestack_ix;
- switch (Sigsetjmp(top_env,1)) {
+ switch (JMPENV_PUSH) {
case 1:
cxstack_ix = -1; /* start context stack again */
break;
if (getenv("PERL_DEBUG_MSTATS"))
dump_mstats("after execution: ");
#endif
+ JMPENV_POP;
return STATUS_NATIVE_EXPORT;
case 3:
- mustcatch = FALSE;
if (!restartop) {
PerlIO_printf(PerlIO_stderr(), "panic: restartop\n");
FREETMPS;
+ JMPENV_POP;
return 1;
}
if (curstack != mainstack) {
}
my_exit(0);
+ /* NOTREACHED */
return 0;
}
SV** sp = stack_sp;
I32 oldmark;
I32 retval;
- Sigjmp_buf oldtop;
I32 oldscope;
static CV *DBcv;
- bool oldmustcatch = mustcatch;
+ bool oldcatch = CATCH_GET;
+ dJMPENV;
if (flags & G_DISCARD) {
ENTER;
op->op_private |= OPpENTERSUB_DB;
if (flags & G_EVAL) {
- Copy(top_env, oldtop, 1, Sigjmp_buf);
-
cLOGOP->op_other = op;
markstack_ptr--;
/* we're trying to emulate pp_entertry() here */
}
markstack_ptr++;
- restart:
- switch (Sigsetjmp(top_env,1)) {
+ switch (JMPENV_PUSH) {
case 0:
break;
case 1:
/* my_exit() was called */
curstash = defstash;
FREETMPS;
- Copy(oldtop, top_env, 1, Sigjmp_buf);
+ JMPENV_POP;
if (statusvalue)
croak("Callback called exit");
my_exit_jump();
/* NOTREACHED */
case 3:
- mustcatch = FALSE;
if (restartop) {
op = restartop;
restartop = 0;
- goto restart;
+ break;
}
stack_sp = stack_base + oldmark;
if (flags & G_ARRAY)
}
}
else
- mustcatch = TRUE;
+ CATCH_SET(TRUE);
if (op == (OP*)&myop)
op = pp_entersub();
curpm = newpm;
LEAVE;
}
- Copy(oldtop, top_env, 1, Sigjmp_buf);
+ JMPENV_POP;
}
else
- mustcatch = oldmustcatch;
+ CATCH_SET(oldcatch);
if (flags & G_DISCARD) {
stack_sp = stack_base + oldmark;
SV** sp = stack_sp;
I32 oldmark = sp - stack_base;
I32 retval;
- Sigjmp_buf oldtop;
I32 oldscope;
+ dJMPENV;
if (flags & G_DISCARD) {
ENTER;
if (flags & G_ARRAY)
myop.op_flags |= OPf_LIST;
- Copy(top_env, oldtop, 1, Sigjmp_buf);
-
-restart:
- switch (Sigsetjmp(top_env,1)) {
+ switch (JMPENV_PUSH) {
case 0:
break;
case 1:
/* my_exit() was called */
curstash = defstash;
FREETMPS;
- Copy(oldtop, top_env, 1, Sigjmp_buf);
+ JMPENV_POP;
if (statusvalue)
croak("Callback called exit");
my_exit_jump();
/* NOTREACHED */
case 3:
- mustcatch = FALSE;
if (restartop) {
op = restartop;
restartop = 0;
- goto restart;
+ break;
}
stack_sp = stack_base + oldmark;
if (flags & G_ARRAY)
sv_setpv(GvSV(errgv),"");
cleanup:
- Copy(oldtop, top_env, 1, Sigjmp_buf);
+ JMPENV_POP;
if (flags & G_DISCARD) {
stack_sp = stack_base + oldmark;
retval = 0;
I32 oldscope;
AV* list;
{
- Sigjmp_buf oldtop;
+ dJMPENV;
STRLEN len;
line_t oldline = curcop->cop_line;
- Copy(top_env, oldtop, 1, Sigjmp_buf);
-
while (AvFILL(list) >= 0) {
CV *cv = (CV*)av_shift(list);
SAVEFREESV(cv);
- switch (Sigsetjmp(top_env,1)) {
+ switch (JMPENV_PUSH) {
case 0: {
SV* atsv = GvSV(errgv);
PUSHMARK(stack_sp);
perl_call_sv((SV*)cv, G_EVAL|G_DISCARD);
(void)SvPV(atsv, len);
if (len) {
- Copy(oldtop, top_env, 1, Sigjmp_buf);
+ JMPENV_POP;
curcop = &compiling;
curcop->cop_line = oldline;
if (list == beginav)
if (endav)
call_list(oldscope, endav);
FREETMPS;
- Copy(oldtop, top_env, 1, Sigjmp_buf);
+ JMPENV_POP;
curcop = &compiling;
curcop->cop_line = oldline;
if (statusvalue) {
FREETMPS;
break;
}
- Copy(oldtop, top_env, 1, Sigjmp_buf);
+ JMPENV_POP;
curcop = &compiling;
curcop->cop_line = oldline;
- Siglongjmp(top_env, 3);
+ JMPENV_JUMP(3);
}
+ JMPENV_POP;
}
-
- Copy(oldtop, top_env, 1, Sigjmp_buf);
}
void
LEAVE;
}
- Siglongjmp(top_env, 2);
+ JMPENV_JUMP(2);
}
IEXT CONTEXT * Icxstack;
IEXT I32 Icxstack_ix IINIT(-1);
IEXT I32 Icxstack_max IINIT(128);
-IEXT Sigjmp_buf Itop_env;
+IEXT JMPENV Istart_env; /* empty startup sigjmp() environment */
+IEXT JMPENV * Itop_env; /* ptr. to current sigjmp() environment */
IEXT I32 Irunlevel;
-IEXT bool Imustcatch; /* doeval() must be caught locally */
/* stack stuff */
IEXT AV * Icurstack; /* THE STACK */
#define WORD_ALIGN sizeof(U16)
#endif
-#define DOCATCH(o) (mustcatch ? docatch(o) : (o))
+#define DOCATCH(o) ((CATCH_GET == TRUE) ? docatch(o) : (o))
static OP *docatch _((OP *o));
static OP *doeval _((int gimme));
AV *oldstack;
CONTEXT *cx;
SV** newsp;
- bool oldmustcatch = mustcatch;
+ bool oldcatch = CATCH_GET;
SAVETMPS;
SAVESPTR(op);
AvREAL_off(sortstack);
av_extend(sortstack, 32);
}
- mustcatch = TRUE;
+ CATCH_SET(TRUE);
SWITCHSTACK(curstack, sortstack);
if (sortstash != stash) {
firstgv = gv_fetchpv("a", TRUE, SVt_PV);
POPBLOCK(cx,curpm);
SWITCHSTACK(sortstack, oldstack);
- mustcatch = oldmustcatch;
+ CATCH_SET(oldcatch);
}
LEAVE;
}
if (curstack == signalstack) {
restartop = retop;
- Siglongjmp(top_env, 3);
+ JMPENV_JUMP(3);
}
RETURNOP(retop);
int ret;
int oldrunlevel = runlevel;
OP *oldop = op;
- Sigjmp_buf oldtop;
+ dJMPENV;
op = o;
- Copy(top_env, oldtop, 1, Sigjmp_buf);
#ifdef DEBUGGING
- assert(mustcatch == TRUE);
+ assert(CATCH_GET == TRUE);
+ DEBUG_l(deb("(Setting up local jumplevel, runlevel = %d)\n", runlevel+1));
#endif
- mustcatch = FALSE;
- switch ((ret = Sigsetjmp(top_env,1))) {
+ switch ((ret = JMPENV_PUSH)) {
default: /* topmost level handles it */
- Copy(oldtop, top_env, 1, Sigjmp_buf);
+ JMPENV_POP;
runlevel = oldrunlevel;
- mustcatch = TRUE;
op = oldop;
- Siglongjmp(top_env, ret);
+ JMPENV_JUMP(ret);
/* NOTREACHED */
case 3:
if (!restartop) {
PerlIO_printf(PerlIO_stderr(), "panic: restartop\n");
break;
}
- mustcatch = FALSE;
op = restartop;
restartop = 0;
/* FALL THROUGH */
runops();
break;
}
- Copy(oldtop, top_env, 1, Sigjmp_buf);
+ JMPENV_POP;
runlevel = oldrunlevel;
- mustcatch = TRUE;
op = oldop;
return Nullop;
}
SV **mark = stack_base + ++*markstack_ptr; /* reuse in entersub */
I32 markoff = mark - stack_base - 1;
char *methname;
- bool oldmustcatch = mustcatch;
+ bool oldcatch = CATCH_GET;
varsv = mark[0];
if (SvTYPE(varsv) == SVt_PVHV)
myop.op_last = (OP *) &myop;
myop.op_next = Nullop;
myop.op_flags = OPf_KNOW|OPf_STACKED;
- mustcatch = TRUE;
+ CATCH_SET(TRUE);
ENTER;
SAVESPTR(op);
runops();
SPAGAIN;
- mustcatch = oldmustcatch;
+ CATCH_SET(oldcatch);
sv = TOPs;
if (sv_isobject(sv)) {
if (SvTYPE(varsv) == SVt_PVHV || SvTYPE(varsv) == SVt_PVAV) {
GV *gv;
BINOP myop;
SV *sv;
- bool oldmustcatch = mustcatch;
+ bool oldcatch = CATCH_GET;
hv = (HV*)POPs;
myop.op_last = (OP *) &myop;
myop.op_next = Nullop;
myop.op_flags = OPf_KNOW|OPf_STACKED;
- mustcatch = TRUE;
+ CATCH_SET(TRUE);
ENTER;
SAVESPTR(op);
SPAGAIN;
}
- mustcatch = oldmustcatch;
+ CATCH_SET(oldcatch);
if (sv_isobject(TOPs))
sv_magic((SV*)hv, TOPs, 'P', Nullch, 0);
LEAVE;
SSPUSHINT(SAVEt_STACK_POS); \
} STMT_END
+
+/* A jmpenv packages the state required to perform a proper non-local jump.
+ * Note that there is a start_env initialized when perl starts, and top_env
+ * points to this initially, so top_env should always be non-null.
+ *
+ * Existence of a non-null top_env->je_prev implies it is valid to call
+ * longjmp() at that runlevel (we make sure start_env.je_prev is always
+ * null to ensure this).
+ *
+ * je_mustcatch, when set at any runlevel to TRUE, means eval ops must
+ * establish a local jmpenv to handle exception traps. Care must be taken
+ * to restore the previous value of je_mustcatch before exiting the
+ * stack frame iff JMPENV_PUSH was not called in that stack frame.
+ * GSAR 97-03-27
+ */
+
+struct jmpenv {
+ struct jmpenv * je_prev;
+ Sigjmp_buf je_buf;
+ int je_ret; /* return value of last setjmp() */
+ bool je_mustcatch; /* longjmp()s must be caught locally */
+};
+
+typedef struct jmpenv JMPENV;
+
+#define dJMPENV JMPENV cur_env
+#define JMPENV_PUSH (cur_env.je_prev = top_env, \
+ cur_env.je_ret = Sigsetjmp(cur_env.je_buf,1), \
+ top_env = &cur_env, \
+ cur_env.je_mustcatch = FALSE, \
+ cur_env.je_ret)
+#define JMPENV_POP (top_env = cur_env.je_prev)
+#define JMPENV_JUMP(v) (top_env->je_prev ? Siglongjmp(top_env->je_buf, (v)) \
+ : ((v) == 2) ? exit(STATUS_NATIVE_EXPORT) \
+ : (PerlIO_printf(PerlIO_stderr(), "panic: top_env\n"), \
+ exit(1)))
+
+#define CATCH_GET (top_env->je_mustcatch)
+#define CATCH_SET(v) (top_env->je_mustcatch = (v))
+
restartop = die_where(message);
if ((!restartop && was_in_eval) || oldrunlevel > 1)
- Siglongjmp(top_env, 3);
+ JMPENV_JUMP(3);
return restartop;
}
}
if (in_eval) {
restartop = die_where(message);
- Siglongjmp(top_env, 3);
+ JMPENV_JUMP(3);
}
PerlIO_puts(PerlIO_stderr(),message);
(void)PerlIO_flush(PerlIO_stderr());