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 --- ChangeLog | 19 +++++++ eval.c | 184 ++++++++++++++++++++++++++++---------------------------------- node.h | 93 +++++++++++++++++++++++++++++++ ruby.h | 1 + 4 files changed, 195 insertions(+), 102 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9d212645a6..a6a7aecc8c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +Sun Sep 24 06:25:53 2006 why the lucky stiff + + * 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. + Sat Sep 23 21:34:15 2006 Yukihiro Matsumoto * lib/cgi.rb (CGI::QueryExtension::read_multipart): CGI content 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); diff --git a/node.h b/node.h index 46332ca7bf..c08ad33d1b 100644 --- a/node.h +++ b/node.h @@ -393,6 +393,99 @@ typedef void (*rb_event_hook_func_t)(rb_event_t,NODE*,VALUE,ID,VALUE); void rb_add_event_hook(rb_event_hook_func_t,rb_event_t); int rb_remove_event_hook(rb_event_hook_func_t); +#if defined(HAVE_GETCONTEXT) && defined(HAVE_SETCONTEXT) +#include +#define USE_CONTEXT +#endif +#include +#include "st.h" + +#ifdef USE_CONTEXT +typedef struct { + ucontext_t context; + volatile int status; +} rb_jmpbuf_t[1]; +#else +typedef jmp_buf rb_jmpbuf_t; +#endif + +enum thread_status { + THREAD_TO_KILL, + THREAD_RUNNABLE, + THREAD_STOPPED, + THREAD_KILLED, +}; + +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; + + VALUE sandbox; +}; + +extern VALUE (*ruby_sandbox_save)(struct thread *); +extern VALUE (*ruby_sandbox_restore)(struct thread *); +extern rb_thread_t curr_thread; +extern rb_thread_t main_thread; + #if defined(__cplusplus) } /* extern "C" { */ #endif diff --git a/ruby.h b/ruby.h index df0ae75e9f..fd0f5ebca4 100644 --- a/ruby.h +++ b/ruby.h @@ -729,6 +729,7 @@ RUBY_EXTERN VALUE rb_eIOError; RUBY_EXTERN VALUE rb_eRuntimeError; RUBY_EXTERN VALUE rb_eSecurityError; RUBY_EXTERN VALUE rb_eSystemCallError; +RUBY_EXTERN VALUE rb_eThreadError; RUBY_EXTERN VALUE rb_eTypeError; RUBY_EXTERN VALUE rb_eZeroDivError; RUBY_EXTERN VALUE rb_eNotImpError; -- cgit v1.2.3