Skip to content

Commit

Permalink
Fix crash when timeouts and break loops get mixed up.
Browse files Browse the repository at this point in the history
Ensure that the jump buffer is restored correctly when returning from
a break loop. Imposes maximum depth limit for timeouts of 1024.
  • Loading branch information
stevelinton committed Nov 4, 2015
1 parent 137f534 commit 8d50cf5
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 40 deletions.
57 changes: 51 additions & 6 deletions src/gap.c
Original file line number Diff line number Diff line change
Expand Up @@ -1363,15 +1363,17 @@ Obj FuncSetUserHasQuit( Obj Self, Obj value)
}


syJmp_buf AlarmJumpBuffer;
#define MAX_TIMEOUT_NESTING_DEPTH 1024

syJmp_buf AlarmJumpBuffers[MAX_TIMEOUT_NESTING_DEPTH];
UInt NumAlarmJumpBuffers = 0;

Obj FuncTIMEOUTS_SUPPORTED(Obj self) {
return SyHaveAlarms ? True: False;
}

Obj FuncCALL_WITH_TIMEOUT( Obj self, Obj seconds, Obj microseconds, Obj func, Obj args )
{
syJmp_buf alarmJumpBuffer;
Obj plain_args;
Obj res;
Obj currLVars;
Expand All @@ -1396,12 +1398,14 @@ Obj FuncSetUserHasQuit( Obj Self, Obj value)
else
plain_args = args;
if (SyAlarmRunning)
ErrorMayQuit("CALL_WITH_TIMEOUT cannot currently be nested. There is already a timeout running", 0, 0);
memcpy((void *)&alarmJumpBuffer, (void *)&TLS(AlarmJumpBuffer), sizeof(AlarmJumpBuffer));
ErrorMayQuit("CALL_WITH_TIMEOUT cannot currently be nested except via break loops."
" There is already a timeout running", 0, 0);
if (NumAlarmJumpBuffers++ >= MAX_TIMEOUT_NESTING_DEPTH)
ErrorMayQuit("Nesting depth of timeouts via break loops limited to %i", MAX_TIMEOUT_NESTING_DEPTH, 0L);
currLVars = TLS(CurrLVars);
currStat = TLS(CurrStat);
recursionDepth = TLS(RecursionDepth);
if (sySetjmp(TLS(AlarmJumpBuffer))) {
if (sySetjmp(AlarmJumpBuffers[NumAlarmJumpBuffers])) {
/* Timeout happened */
TLS(CurrLVars) = currLVars;
TLS(PtrLVars) = PTR_BAG(TLS(CurrLVars));
Expand Down Expand Up @@ -1451,10 +1455,45 @@ Obj FuncSetUserHasQuit( Obj Self, Obj value)
SET_LEN_PLIST(res,0);
}
}
memcpy((void *)&TLS(AlarmJumpBuffer), (void *)&alarmJumpBuffer, sizeof(syJmp_buf));
assert(NumAlarmJumpBuffers);
NumAlarmJumpBuffers--;
return res;
}

Obj FuncSTOP_TIMEOUT( Obj self ) {
UInt seconds, nanoseconds;
if (!SyHaveAlarms || !SyAlarmRunning)
return Fail;
SyStopAlarm(&seconds, &nanoseconds);
Obj state = NEW_PLIST(T_PLIST_CYC+IMMUTABLE, 3);
SET_ELM_PLIST(state,1,INTOBJ_INT(seconds));
SET_ELM_PLIST(state,2,INTOBJ_INT(nanoseconds/1000));
SET_ELM_PLIST(state,3,INTOBJ_INT(NumAlarmJumpBuffers));
SET_LEN_PLIST(state,3);
return state;
}

Obj FuncRESUME_TIMEOUT( Obj self, Obj state ) {
if (!SyHaveAlarms || SyAlarmRunning)
return Fail;
if (!IS_PLIST(state) || LEN_PLIST(state) < 2)
return Fail;
if (!IS_INTOBJ(ELM_PLIST(state,1)) ||
!IS_INTOBJ(ELM_PLIST(state,2)))
return Fail;
Int s = INT_INTOBJ(ELM_PLIST(state,1));
Int us = INT_INTOBJ(ELM_PLIST(state,2));
if (s < 0 || us < 0 || us > 999999)
return Fail;
Int depth = INT_INTOBJ(ELM_PLIST(state,3));
if (depth < 0 || depth >= MAX_TIMEOUT_NESTING_DEPTH)
return Fail;
NumAlarmJumpBuffers = depth;
SyInstallAlarm(s, 1000*us);
return True;
}



/****************************************************************************
**
Expand Down Expand Up @@ -3018,6 +3057,12 @@ static StructGVarFunc GVarFuncs [] = {
{ "CALL_WITH_TIMEOUT", 4, "seconds, microseconds, func, args",
FuncCALL_WITH_TIMEOUT, "src/gap.c:CALL_WITH_TIMEOUT" },

{"STOP_TIMEOUT", 0, "",
FuncSTOP_TIMEOUT, "src/gap.c:FuncSTOP_TIMEOUT" },

{"RESUME_TIMEOUT", 1, "state",
FuncRESUME_TIMEOUT, "src/gap.c:FuncRESUME_TIMEOUT" },

{ "JUMP_TO_CATCH", 1, "payload",
FuncJUMP_TO_CATCH, "src/gap.c:JUMP_TO_CATCH" },

Expand Down
10 changes: 10 additions & 0 deletions src/gap.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ extern UInt Last3;
*/
extern UInt Time;

/****************************************************************************
**
*V AlarmJumpBuffer . . . . . .long jump buffer used for timeouts
**
** Needs to be visible to code in read.c that stores away execution state
*/


extern syJmp_buf AlarmJumpBuffers[];
extern UInt NumAlarmJumpBuffers;


/****************************************************************************
Expand Down
2 changes: 1 addition & 1 deletion src/stats.c
Original file line number Diff line number Diff line change
Expand Up @@ -1684,7 +1684,7 @@ UInt ExecIntrStat (
CallWithTimeLimit where we started */

if ( SyAlarmHasGoneOff )
syLongjmp(AlarmJumpBuffer,1);
syLongjmp(AlarmJumpBuffers[NumAlarmJumpBuffers],1);

/* and now for something completely different */
SET_BRK_CURR_STAT( stat );
Expand Down
33 changes: 0 additions & 33 deletions src/sysfiles.c
Original file line number Diff line number Diff line change
Expand Up @@ -1569,34 +1569,6 @@ void SyStopAlarm(UInt *seconds, UInt *nanoseconds) {
#endif
#endif

Obj FuncSTOP_TIMEOUT( Obj self ) {
UInt seconds, nanoseconds;
if (!SyHaveAlarms || !SyAlarmRunning)
return Fail;
SyStopAlarm(&seconds, &nanoseconds);
Obj state = NEW_PLIST(T_PLIST_CYC+IMMUTABLE, 2);
SET_ELM_PLIST(state,1,INTOBJ_INT(seconds));
SET_ELM_PLIST(state,2,INTOBJ_INT(nanoseconds/1000));
SET_LEN_PLIST(state,2);
return state;
}

Obj FuncRESUME_TIMEOUT( Obj self, Obj state ) {
if (!SyHaveAlarms || SyAlarmRunning)
return Fail;
if (!IS_PLIST(state) || LEN_PLIST(state) < 2)
return Fail;
if (!IS_INTOBJ(ELM_PLIST(state,1)) ||
!IS_INTOBJ(ELM_PLIST(state,2)))
return Fail;
Int s = INT_INTOBJ(ELM_PLIST(state,1));
Int us = INT_INTOBJ(ELM_PLIST(state,2));
if (s < 0 || us < 0 || us > 999999)
return Fail;
SyInstallAlarm(s, 1000*us);
return True;
}


/****************************************************************************
**
Expand Down Expand Up @@ -4071,11 +4043,6 @@ static StructGVarFunc GVarFuncs [] = {
FuncREADLINEINITLINE, "src/sysfiles.c:FuncREADLINEINITLINE" },
#endif

{"STOP_TIMEOUT", 0, "",
FuncSTOP_TIMEOUT, "src/sysfiles.c:FuncSTOP_TIMEOUT" },

{"RESUME_TIMEOUT", 1, "state",
FuncRESUME_TIMEOUT, "src/sysfiles.c:FuncRESUME_TIMEOUT" },


{ 0 } };
Expand Down

0 comments on commit 8d50cf5

Please sign in to comment.