~ chicken-core (chicken-5) 10ef6f66761eb4a62f16581239b7c7fcee715adb
commit 10ef6f66761eb4a62f16581239b7c7fcee715adb
Author: Peter Bex <peter@more-magic.net>
AuthorDate: Sun Apr 24 14:45:17 2016 +0200
Commit: Evan Hanson <evhan@foldling.org>
CommitDate: Wed Apr 27 23:01:21 2016 +1200
Avoid triggering stack overflows in signal handler
The signal handler would touch C_stack_limit in order to force a GC, but
this only really works for C_demand(). The C_stack_limit is also
checked by C_stack_check1, but this check is used to detect runaway C
recursion and *actual* stack overflow errors.
This patch introduces a distinction between the C_stack_limit of
old (which is unchanged in meaning) and a new C_stack_hard_limit, which
is the true stack limit. The C_demand checks are still against
C_stack_limit, but C_stack_check1 checks are against C_stack_hard_limit.
Fixes #1283
Signed-off-by: Evan Hanson <evhan@foldling.org>
diff --git a/NEWS b/NEWS
index 0485b4ed..e4388e38 100644
--- a/NEWS
+++ b/NEWS
@@ -24,6 +24,8 @@
- Compiled programs with large literals won't crash on startup (#1221).
- Comparisons of closures now behave in a stable way, whether or not
the code was compiled with the -no-lambda-info option (#1041).
+ - The signal handling code can no longer trigger "stack overflow" or
+ "recursion too deep or circular data encountered" errors (#1283).
- Compiler:
- Specializations on implicit "or" types like "number" or "boolean" now
diff --git a/chicken.h b/chicken.h
index 9e0854dc..150a56fc 100644
--- a/chicken.h
+++ b/chicken.h
@@ -1085,8 +1085,8 @@ typedef void (C_ccall *C_proc)(C_word, C_word *) C_noret;
# define C_stack_check1(err) if(!C_disable_overflow_check) { \
do { C_byte *_sp = (C_byte*)(C_stack_pointer); \
- if(_sp < (C_byte *)C_stack_limit && \
- ((C_byte *)C_stack_limit - _sp) > C_STACK_RESERVE) \
+ if(_sp < (C_byte *)C_stack_hard_limit && \
+ ((C_byte *)C_stack_hard_limit - _sp) > C_STACK_RESERVE) \
err; } \
while(0);}
@@ -1097,8 +1097,8 @@ typedef void (C_ccall *C_proc)(C_word, C_word *) C_noret;
# define C_stack_check1(err) if(!C_disable_overflow_check) { \
do { C_byte *_sp = (C_byte*)(C_stack_pointer); \
- if(_sp > (C_byte *)C_stack_limit && \
- (_sp - (C_byte *)C_stack_limit) > C_STACK_RESERVE) \
+ if(_sp > (C_byte *)C_stack_hard_limit && \
+ (_sp - (C_byte *)C_stack_hard_limit) > C_STACK_RESERVE) \
err; } \
while(0);}
@@ -1610,7 +1610,8 @@ C_varextern C_TLS C_word
*C_temporary_stack,
*C_temporary_stack_bottom,
*C_temporary_stack_limit,
- *C_stack_limit;
+ *C_stack_limit,
+ *C_stack_hard_limit;
C_varextern C_TLS C_long
C_timer_interrupt_counter,
C_initial_timer_interrupt_period;
diff --git a/runtime.c b/runtime.c
index 7f163174..113e26dd 100644
--- a/runtime.c
+++ b/runtime.c
@@ -329,7 +329,8 @@ C_TLS C_word
*C_temporary_stack,
*C_temporary_stack_bottom,
*C_temporary_stack_limit,
- *C_stack_limit;
+ *C_stack_limit, /* "Soft" limit, may be reset to force GC */
+ *C_stack_hard_limit; /* Actual stack limit */
C_TLS C_long
C_timer_interrupt_counter,
C_initial_timer_interrupt_period;
@@ -410,7 +411,6 @@ static C_TLS C_word
**collectibles,
**collectibles_top,
**collectibles_limit,
- *saved_stack_limit,
**mutation_stack_bottom,
**mutation_stack_limit,
**mutation_stack_top,
@@ -1233,10 +1233,11 @@ void C_do_resize_stack(C_word stack)
stack_size = stack;
#if C_STACK_GROWS_DOWNWARD
- C_stack_limit = (C_word *)((C_byte *)C_stack_limit - diff);
+ C_stack_hard_limit = (C_word *)((C_byte *)C_stack_hard_limit - diff);
#else
- C_stack_limit = (C_word *)((C_byte *)C_stack_limit + diff);
+ C_stack_hard_limit = (C_word *)((C_byte *)C_stack_hard_limit + diff);
#endif
+ C_stack_limit = C_stack_hard_limit;
}
}
@@ -1472,10 +1473,11 @@ C_word CHICKEN_run(void *toplevel)
if(profiling) set_profile_timer(profile_frequency);
#if C_STACK_GROWS_DOWNWARD
- C_stack_limit = (C_word *)((C_byte *)C_stack_pointer - stack_size);
+ C_stack_hard_limit = (C_word *)((C_byte *)C_stack_pointer - stack_size);
#else
- C_stack_limit = (C_word *)((C_byte *)C_stack_pointer + stack_size);
+ C_stack_hard_limit = (C_word *)((C_byte *)C_stack_pointer + stack_size);
#endif
+ C_stack_limit = C_stack_hard_limit;
stack_bottom = C_stack_pointer;
@@ -2026,12 +2028,13 @@ void C_fcall C_callback_adjust_stack(C_word *a, int size)
a, stack_bottom, C_stack_limit);
#if C_STACK_GROWS_DOWNWARD
- C_stack_limit = (C_word *)((C_byte *)a - stack_size);
+ C_stack_hard_limit = (C_word *)((C_byte *)a - stack_size);
stack_bottom = a + size;
#else
- C_stack_limit = (C_word *)((C_byte *)a + stack_size);
+ C_stack_hard_limit = (C_word *)((C_byte *)a + stack_size);
stack_bottom = a;
#endif
+ C_stack_limit = C_stack_hard_limit;
if(debug_mode)
C_dbg(C_text("debug"), C_text("new: \t%p (bottom) - %p (limit)\n"),
@@ -3782,7 +3785,7 @@ void handle_interrupt(void *trampoline)
/* Restore state to the one at the time of the interrupt: */
C_temporary_stack = C_temporary_stack_bottom;
- C_stack_limit = saved_stack_limit;
+ C_stack_limit = C_stack_hard_limit;
/* Invoke high-level interrupt handler: */
reason = C_fix(pending_interrupts[ --pending_interrupts_count ]);
@@ -4598,22 +4601,14 @@ C_regparm void C_fcall C_paranoid_check_for_interrupt(void)
C_regparm void C_fcall C_raise_interrupt(int reason)
{
- /*
- * Save the value of C_stack_limit from before the interrupt is queued
- * to ensure that multiple signals only ever cause saved_stack_limit
- * to be assigned a value from when pending_interrupts_count was zero.
- */
- C_word *stack_limit = C_stack_limit;
-
if(C_interrupts_enabled) {
if(pending_interrupts_count == 0 && !handling_interrupts) {
pending_interrupts[ pending_interrupts_count++ ] = reason;
/*
- * Force the next stack check to fail by faking a "full" stack.
- * This causes save_and_reclaim() to be called, which invokes
- * handle_interrupt(), which restores the stack limit.
+ * Force the next "soft" stack check to fail by faking a "full"
+ * stack. This causes save_and_reclaim() to be called, which
+ * invokes handle_interrupt(), which restores the stack limit.
*/
- saved_stack_limit = stack_limit;
C_stack_limit = stack_bottom;
interrupt_time = C_cpu_milliseconds();
} else if(pending_interrupts_count < MAX_PENDING_INTERRUPTS) {
Trap