diff options
-rw-r--r-- | ChangeLog | 19 | ||||
-rw-r--r-- | eval.c | 182 | ||||
-rw-r--r-- | node.h | 91 | ||||
-rw-r--r-- | ruby.h | 1 |
4 files changed, 193 insertions, 100 deletions
@@ -1,3 +1,22 @@ +Sat Sep 23 23:24:57 2006 why the lucky stiff <why@ruby-lang.org> + + * 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. + [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 <matz@ruby-lang.org> * lib/cgi.rb (CGI::QueryExtension::read_multipart): CGI content @@ -29,11 +29,6 @@ #endif #include <stdio.h> -#if defined(HAVE_GETCONTEXT) && defined(HAVE_SETCONTEXT) -#include <ucontext.h> -#define USE_CONTEXT -#endif -#include <setjmp.h> #include "st.h" #include "dln.h" @@ -90,10 +85,6 @@ char *strrchr _((const char*,const char)); #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 @@ -202,7 +193,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)) @@ -253,6 +243,8 @@ static int scope_vmode; #define SCOPE_SET(f) (scope_vmode=(f)) #define SCOPE_TEST(f) (scope_vmode&(f)) +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: @@ -1081,9 +1073,8 @@ NODE *ruby_top_cref; ruby_scope = _scope; \ scope_vmode = SCOPE_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() \ @@ -1440,6 +1431,8 @@ static void rb_thread_wait_other_threads _((void)); static int thread_set_raised(); static int thread_reset_raised(); +static int thread_no_ensure _((void)); + static VALUE exception_error; static VALUE sysstack_error; @@ -3310,7 +3303,7 @@ rb_eval(self, 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; @@ -4743,7 +4736,7 @@ rb_f_block_given_p() return Qfalse; } -static VALUE rb_eThreadError; +VALUE rb_eThreadError; NORETURN(static void proc_jump_error(int, VALUE)); static void @@ -5456,7 +5449,9 @@ rb_ensure(b_proc, data1, e_proc, data2) } 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; @@ -9747,13 +9742,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) @@ -9773,70 +9761,10 @@ enum thread_status { /* 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 klass; - VALUE wrapper; - NODE *cref; - - int flags; /* misc. states (vmode/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; - fd_set readfds; - fd_set writefds; - fd_set 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) @@ -9904,6 +9832,12 @@ thread_reset_raised() 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 run_trap_eval _((VALUE)); @@ -10034,6 +9968,7 @@ thread_mark(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; @@ -10238,6 +10173,10 @@ rb_thread_save_context(th) th->safe = ruby_safe_level; th->node = ruby_current_node; + if (ruby_sandbox_save != NULL) + { + ruby_sandbox_save(th); + } } static int @@ -10295,6 +10234,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_class = th->klass; @@ -11232,16 +11175,34 @@ rb_thread_run(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 <i>thr</i> and schedules another thread to be run. If this thread - * is already marked to be killed, <code>exit</code> returns the - * <code>Thread</code>. If this is the main thread, or the last thread, exits - * the process. + * Terminates <i>thr</i> and schedules another thread to be run, returning + * the terminated <code>Thread</code>. If this is the main thread, or the + * last thread, exits the process. */ VALUE @@ -11250,20 +11211,33 @@ rb_thread_kill(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 <i>thr</i> without calling ensure clauses and schedules + * another thread to be run, returning the terminated <code>Thread</code>. + * If this is the main thread, or the last thread, exits the process. + * + * See <code>Thread#exit</code> 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 @@ -11658,6 +11632,11 @@ rb_thread_group(thread) th->thgroup = thgroup_default;\ th->locals = 0;\ th->thread = 0;\ + if (curr_thread == 0) {\ + th->sandbox = Qnil;\ + } else {\ + th->sandbox = curr_thread->sandbox;\ + }\ } while (0) static rb_thread_t @@ -12915,6 +12894,9 @@ Init_Thread() 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); @@ -374,6 +374,97 @@ 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 <ucontext.h> +#define USE_CONTEXT +#endif +#include <setjmp.h> +#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__ + VALUE *bstr_ptr; + long bstr_len; +#endif + + struct FRAME *frame; + struct SCOPE *scope; + struct RVarmap *dyna_vars; + struct BLOCK *block; + struct iter *iter; + struct tag *tag; + VALUE klass; + VALUE wrapper; + NODE *cref; + + int flags; /* misc. states (vmode/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; + fd_set readfds; + fd_set writefds; + fd_set 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 @@ -645,6 +645,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; |