From dcb6131f78704196e749125c8877e63061180542 Mon Sep 17 00:00:00 2001 From: why Date: Sat, 23 Sep 2006 21:29:47 +0000 Subject: * eval.c (rb_thread_save_context, rb_thread_restore_context): sandbox hook to save and restore sandbox state. * eval.c (thread_no_ensure): added THREAD_NO_ENSURE thread flag. * eval.c (rb_thread_kill_bang): Thread#kill! uses the above flag to circumvent ensure, in order to prevent endless loops. contributed by MenTaLguY. [ruby-core:08768] * eval.c (rb_thread_kill): fix Thread#kill docs, which returns the thread object in all cases. * node.h: expose the rb_jmpbuf_t and rb_thread_t structs, along with the thread flags. used by the sandbox extension. * ruby.h: extern rb_eThreadError, so sandbox can swap it. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11001 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- eval.c | 184 +++++++++++++++++++++++++++++------------------------------------ 1 file changed, 82 insertions(+), 102 deletions(-) (limited to 'eval.c') diff --git a/eval.c b/eval.c index 4cca629a86..1ef5143f87 100644 --- a/eval.c +++ b/eval.c @@ -29,11 +29,6 @@ #endif #include -#if defined(HAVE_GETCONTEXT) && defined(HAVE_SETCONTEXT) -#include -#define USE_CONTEXT -#endif -#include #include "st.h" #include "dln.h" @@ -80,10 +75,6 @@ void *alloca (); #endif #ifdef USE_CONTEXT -typedef struct { - ucontext_t context; - volatile int status; -} rb_jmpbuf_t[1]; NORETURN(static void rb_jump_context(rb_jmpbuf_t, int)); static inline void @@ -190,7 +181,6 @@ static int volatile freebsd_clear_carry_flag = 0; POST_GETCONTEXT \ (j)->status) #else -typedef jmp_buf rb_jmpbuf_t; # if !defined(setjmp) && defined(HAVE__SETJMP) # define ruby_setjmp(just_before_setjmp, env) \ ((just_before_setjmp), _setjmp(env)) @@ -258,6 +248,8 @@ static int vis_mode; #define VIS_TEST(f) (vis_mode&(f)) #define VIS_MODE() (vis_mode) +VALUE (*ruby_sandbox_save)(struct thread *) = NULL; +VALUE (*ruby_sandbox_restore)(struct thread *) = NULL; NODE* ruby_current_node; int ruby_safe_level = 0; /* safe-level: @@ -1008,9 +1000,8 @@ NODE *ruby_top_cref; ruby_scope = _scope; \ vis_mode = VIS_PUBLIC -typedef struct thread * rb_thread_t; -static rb_thread_t curr_thread = 0; -static rb_thread_t main_thread; +rb_thread_t curr_thread = 0; +rb_thread_t main_thread; static void scope_dup(struct SCOPE *); #define POP_SCOPE() \ @@ -1424,6 +1415,8 @@ static void rb_thread_wait_other_threads(void); static int thread_set_raised(void); static int thread_reset_raised(void); +static int thread_no_ensure _((void)); + static VALUE exception_error; static VALUE sysstack_error; @@ -3061,7 +3054,7 @@ rb_eval(VALUE self, NODE *n) result = rb_eval(self, node->nd_head); } POP_TAG(); - if (node->nd_ensr) { + if (node->nd_ensr && !thread_no_ensure()) { VALUE retval = prot_tag->retval; /* save retval */ VALUE errinfo = ruby_errinfo; @@ -4571,7 +4564,7 @@ rb_f_block_given_p(void) return Qfalse; } -static VALUE rb_eThreadError; +VALUE rb_eThreadError; NORETURN(static void proc_jump_error(int, VALUE)); static void @@ -5340,7 +5333,9 @@ rb_ensure(VALUE (*b_proc)(ANYARGS), VALUE data1, VALUE (*e_proc)(ANYARGS), VALUE } POP_TAG(); retval = prot_tag ? prot_tag->retval : Qnil; /* save retval */ + if (!thread_no_ensure()) { (*e_proc)(data2); + } if (prot_tag) return_value(retval); if (state) JUMP_TAG(state); return result; @@ -9746,13 +9741,6 @@ VALUE rb_cThread; extern VALUE rb_last_status; -enum thread_status { - THREAD_TO_KILL, - THREAD_RUNNABLE, - THREAD_STOPPED, - THREAD_KILLED, -}; - #define WAIT_FD (1<<0) #define WAIT_SELECT (1<<1) #define WAIT_TIME (1<<2) @@ -9865,72 +9853,10 @@ rb_fd_select(int n, rb_fdset_t *readfds, rb_fdset_t *writefds, rb_fdset_t *excep #endif -/* typedef struct thread * rb_thread_t; */ - -struct thread { - struct thread *next, *prev; - rb_jmpbuf_t context; -#ifdef SAVE_WIN32_EXCEPTION_LIST - DWORD win32_exception_list; -#endif - - VALUE result; - - long stk_len; - long stk_max; - VALUE *stk_ptr; - VALUE *stk_pos; -#ifdef __ia64 - long bstr_len; - long bstr_max; - VALUE *bstr_ptr; - VALUE *bstr_pos; -#endif - - struct FRAME *frame; - struct SCOPE *scope; - struct RVarmap *dyna_vars; - struct BLOCK *block; - struct iter *iter; - struct tag *tag; - VALUE wrapper; - NODE *cref; - struct ruby_env *anchor; - - int flags; /* misc. states (rb_trap_immediate/raised) */ - - NODE *node; - - int tracing; - VALUE errinfo; - VALUE last_status; - VALUE last_line; - VALUE last_match; - - int safe; - - enum thread_status status; - int wait_for; - int fd; - rb_fdset_t readfds; - rb_fdset_t writefds; - rb_fdset_t exceptfds; - int select_value; - double delay; - rb_thread_t join; - - int abort; - int priority; - VALUE thgroup; - - st_table *locals; - - VALUE thread; -}; - #define THREAD_RAISED 0x200 /* temporary flag */ #define THREAD_TERMINATING 0x400 /* persistent flag */ -#define THREAD_FLAGS_MASK 0x400 /* mask for persistent flags */ +#define THREAD_NO_ENSURE 0x800 /* persistent flag */ +#define THREAD_FLAGS_MASK 0xc00 /* mask for persistent flags */ #define FOREACH_THREAD_FROM(f,x) x = f; do { x = x->next; #define END_FOREACH_FROM(f,x) } while (x != f) @@ -10001,6 +9927,12 @@ thread_reset_raised(void) return 1; } +static int +thread_no_ensure() +{ + return ((curr_thread->flags & THREAD_NO_ENSURE) == THREAD_NO_ENSURE); +} + static void rb_thread_ready(rb_thread_t); static VALUE @@ -10121,6 +10053,7 @@ thread_mark(rb_thread_t th) rb_gc_mark(th->last_match); rb_mark_tbl(th->locals); rb_gc_mark(th->thgroup); + rb_gc_mark_maybe(th->sandbox); /* mark data in copied stack */ if (th == curr_thread) return; @@ -10329,6 +10262,10 @@ rb_thread_save_context(rb_thread_t th) th->safe = ruby_safe_level; th->node = ruby_current_node; + if (ruby_sandbox_save != NULL) + { + ruby_sandbox_save(th); + } } static int @@ -10387,6 +10324,10 @@ rb_thread_restore_context_0(rb_thread_t th, int exit, void *vp) static VALUE tval; rb_trap_immediate = 0; /* inhibit interrupts from here */ + if (ruby_sandbox_restore != NULL) + { + ruby_sandbox_restore(th); + } ruby_frame = th->frame; ruby_scope = th->scope; ruby_wrapper = th->wrapper; @@ -11307,16 +11248,34 @@ rb_thread_run(VALUE thread) } +static void +kill_thread(th, flags) + rb_thread_t th; + int flags; +{ + if (th != curr_thread && th->safe < 4) { + rb_secure(4); + } + if (th->status == THREAD_TO_KILL || th->status == THREAD_KILLED) + return; + if (th == th->next || th == main_thread) rb_exit(EXIT_SUCCESS); + + rb_thread_ready(th); + th->flags |= flags; + th->status = THREAD_TO_KILL; + if (!rb_thread_critical) rb_thread_schedule(); +} + + /* * call-seq: - * thr.exit => thr or nil - * thr.kill => thr or nil - * thr.terminate => thr or nil + * thr.exit => thr + * thr.kill => thr + * thr.terminate => thr * - * Terminates thr and schedules another thread to be run. If this thread - * is already marked to be killed, exit returns the - * Thread. If this is the main thread, or the last thread, exits - * the process. + * Terminates thr and schedules another thread to be run, returning + * the terminated Thread. If this is the main thread, or the + * last thread, exits the process. */ VALUE @@ -11324,20 +11283,33 @@ rb_thread_kill(VALUE thread) { rb_thread_t th = rb_thread_check(thread); - if (th != curr_thread && th->safe < 4) { - rb_secure(4); - } - if (th->status == THREAD_TO_KILL || th->status == THREAD_KILLED) + kill_thread(th, 0); return thread; - if (th == th->next || th == main_thread) rb_exit(EXIT_SUCCESS); +} - rb_thread_ready(th); - th->status = THREAD_TO_KILL; - if (!rb_thread_critical) rb_thread_schedule(); + +/* + * call-seq: + * thr.exit! => thr + * thr.kill! => thr + * thr.terminate! => thr + * + * Terminates thr without calling ensure clauses and schedules + * another thread to be run, returning the terminated Thread. + * If this is the main thread, or the last thread, exits the process. + * + * See Thread#exit for the safer version. + */ + +static VALUE +rb_thread_kill_bang(thread) + VALUE thread; +{ + rb_thread_t th = rb_thread_check(thread); + kill_thread(th, THREAD_NO_ENSURE); return thread; } - /* * call-seq: * Thread.kill(thread) => thread @@ -11721,6 +11693,11 @@ rb_thread_group(VALUE thread) th->thgroup = thgroup_default;\ th->locals = 0;\ th->thread = 0;\ + if (curr_thread == 0) {\ + th->sandbox = Qnil;\ + } else {\ + th->sandbox = curr_thread->sandbox;\ + }\ th->anchor = 0;\ } while (0) @@ -13005,6 +12982,9 @@ Init_Thread(void) rb_define_method(rb_cThread, "kill", rb_thread_kill, 0); rb_define_method(rb_cThread, "terminate", rb_thread_kill, 0); rb_define_method(rb_cThread, "exit", rb_thread_kill, 0); + rb_define_method(rb_cThread, "kill!", rb_thread_kill_bang, 0); + rb_define_method(rb_cThread, "terminate!", rb_thread_kill_bang, 0); + rb_define_method(rb_cThread, "exit!", rb_thread_kill_bang, 0); rb_define_method(rb_cThread, "value", rb_thread_value, 0); rb_define_method(rb_cThread, "status", rb_thread_status, 0); rb_define_method(rb_cThread, "join", rb_thread_join_m, -1); -- cgit v1.2.3