From 46a4099f9fedf89d1a8cc45cf566750ab7284863 Mon Sep 17 00:00:00 2001 From: Kaz Kylheku Date: Mon, 11 Apr 2016 21:30:19 -0700 Subject: Harden glob: continuations, exceptions, re-entry. * glob.c (s_exit_point): New static variable. (errfunc_thunk): Set up a continuation guard frame, so the error function cannot capture a continuation across glob's stack frames. Intercept all unwinding with a simple unwind block, save the exit point in the global variable and then stop the unwinding by setting the unwind frame's exit point to null. (glob_wrap): Check for glob being re-entered and throw error if so. If the glob callback bailed due to unwinding (as seen by a non-null value in the exit point global variable), clean up the glob memory with globfree and continue the unwinding to the exit point. unwind.h (uw_curr_exit_point): New macro for accessing saved exit point in pure unwind frame. --- glob.c | 36 +++++++++++++++++++++++++++++++++++- unwind.h | 3 +++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/glob.c b/glob.c index dcab2ed8..76673a59 100644 --- a/glob.c +++ b/glob.c @@ -27,19 +27,40 @@ #include #include #include +#include #include #include "config.h" #include "lib.h" #include "gc.h" #include "utf8.h" #include "eval.h" +#include "signal.h" +#include "unwind.h" #include "glob.h" static val s_errfunc; +static uw_frame_t *s_exit_point; static int errfunc_thunk(const char *errpath, int errcode) { - val result = funcall2(s_errfunc, string_utf8(errpath), num(errcode)); + val result = t; + uw_frame_t cont_guard; + + uw_push_guard(&cont_guard); + + uw_simple_catch_begin; + + result = funcall2(s_errfunc, string_utf8(errpath), num(errcode)); + + uw_unwind { + s_exit_point = uw_curr_exit_point; + uw_curr_exit_point = 0; /* stops unwinding */ + } + + uw_catch_end; + + uw_pop_frame(&cont_guard); + return result ? 1 : 0; } @@ -48,11 +69,24 @@ val glob_wrap(val pattern, val flags, val errfunc) char *pat_u8 = utf8_dup_to(c_str(pattern)); glob_t gl; + if (s_errfunc) + uw_throwf(error_s, lit("glob: glob cannot be re-entered from " + "its error callback function"), nao); + s_errfunc = default_bool_arg(errfunc); (void) glob(pat_u8, c_num(default_arg(flags, zero)), s_errfunc ? errfunc_thunk : 0, &gl); + s_errfunc = nil; + + if (s_exit_point) { + uw_frame_t *ep = s_exit_point; + s_exit_point = 0; + globfree(&gl); + uw_continue(ep); + } + { size_t i; list_collect_decl (out, ptail); diff --git a/unwind.h b/unwind.h index 3a88574b..fb353b15 100644 --- a/unwind.h +++ b/unwind.h @@ -212,6 +212,9 @@ noreturn val type_mismatch(val, ...); /* prevent looping */ \ uw_catch.ca.visible = 0; +#define uw_curr_exit_point \ + (uw_catch.ca.cont) + #define uw_catch_end \ break; \ default: \ -- cgit v1.2.3