diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2021-06-23 20:53:11 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2021-06-23 20:53:11 -0700 |
commit | 2e0159fe467ac3ea9b89394ff2e8be77263a01d2 (patch) | |
tree | d4fe384b9f988823bc425b9f5072381aa9d681f1 /gc.c | |
parent | 6c901d9d33b5fed938114224434cad2f2c069592 (diff) | |
download | txr-2e0159fe467ac3ea9b89394ff2e8be77263a01d2.tar.gz txr-2e0159fe467ac3ea9b89394ff2e8be77263a01d2.tar.bz2 txr-2e0159fe467ac3ea9b89394ff2e8be77263a01d2.zip |
New: stack overflow protection.
* configure: detect getrlimit, producing HAVE_RLIMIT in
config.h.
* eval.c (do_eval, do_expand): Call gc_stack_check inline
function to check stack pointer against limit.
* gc.c (gc_stack_bottom): Static becomes extern, so inline
function in gc.h can refer to it.
(gc_stack_limit): New global variable.
(gc_init): If we have rlimit, then probe RLIMIT_STACK.
If the stack is sufficiently large, then enable the stack
overflow protection, which kicks in when the stack pointer
appears to be within a certain percentage of the limit.
(set_stack_limit, get_stack_limit): New static functions.
(gc_late_init): Register set-stack-limit and get-stack-limit
intrinsics.
(gc_stack_overflow): New function.
* gc.h (gc_stack_bottom, gc_stack_limit, gc_stack_overflow):
Declared.
(gc_stack_check): New inline function.
* lib.c (stack_overflow_s): New symbol variable.
(obj_print_impl): Call gc_stack_check to protect recursive
printing againts overflow.
* lib.h (stack_overflow_s): Declared.
* unwind.c (uw_init): Register stack-overflow symbol as a an
exception symbol subtyped from error.
(uw_unwind_to_exit_point): When dealing with an unhandled
exception, turn off the stack limit, so we can print the
messages without triggering it in a loop.
* vm.c (vm_execute_closure, vm_funcall_common): Insert
gc_stack_check to the top of the execution of every VM
function.
* txr.1: Documented.
* share/txr/stdlib/doc-syms.tl: Updated.
Diffstat (limited to 'gc.c')
-rw-r--r-- | gc.c | 44 |
1 files changed, 43 insertions, 1 deletions
@@ -36,6 +36,9 @@ #if HAVE_VALGRIND #include <valgrind/memcheck.h> #endif +#if HAVE_RLIMIT +#include <sys/resource.h> +#endif #include "lib.h" #include "stream.h" #include "hash.h" @@ -84,7 +87,9 @@ int opt_gc_debug; #if HAVE_VALGRIND int opt_vg_debug; #endif -static val *gc_stack_bottom; + +val *gc_stack_bottom; +val *gc_stack_limit; static val *prot_stack[PROT_STACK_SIZE]; static val **prot_stack_limit = prot_stack + PROT_STACK_SIZE; @@ -888,6 +893,15 @@ int gc_inprogress(void) void gc_init(val *stack_bottom) { gc_stack_bottom = stack_bottom; +#if HAVE_RLIMIT + struct rlimit rl; + if (getrlimit(RLIMIT_STACK, &rl) == 0) { + if (rl.rlim_cur > 512 * 1024) { + rlim_t lim = (rl.rlim_cur - rl.rlim_cur / 16) / sizeof (val); + gc_stack_limit = gc_stack_bottom - lim; + } + } +#endif } void gc_mark(val obj) @@ -970,6 +984,27 @@ static val gc_set_delta(val delta) return nil; } +static val set_stack_limit(val limit) +{ + val self = lit("set-stack-limit"); + val *gsl = gc_stack_limit; + + if (limit == nil || limit == zero) { + gc_stack_limit = 0; + } else { + ucnum lim = c_unum(limit, self); + gc_stack_limit = gc_stack_bottom - lim / sizeof (val); + } + + return if2(gsl, num((gc_stack_bottom - gsl) * sizeof (val))); +} + +static val get_stack_limit(void) +{ + val *gsl = gc_stack_limit; + return if2(gsl, num((gc_stack_bottom - gsl) * sizeof (val))); +} + static val gc_wrap(val full) { if (gc_enabled) { @@ -1051,6 +1086,8 @@ void gc_late_init(void) reg_fun(intern(lit("finalize"), user_package), func_n3o(gc_finalize, 2)); reg_fun(intern(lit("call-finalizers"), user_package), func_n1(gc_call_finalizers)); + reg_fun(intern(lit("set-stack-limit"), user_package), func_n1(set_stack_limit)); + reg_fun(intern(lit("get-stack-limit"), user_package), func_n0(get_stack_limit)); } /* @@ -1161,3 +1198,8 @@ void gc_free_all(void) } } } + +void gc_stack_overflow(void) +{ + uw_throwf(stack_overflow_s, lit("computation exceeded stack limit"), nao); +} |