1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

* 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
This commit is contained in:
why 2006-09-23 21:29:47 +00:00
parent 9911bca1e2
commit dcb6131f78
4 changed files with 196 additions and 103 deletions

View file

@ -1,3 +1,22 @@
Sun Sep 24 06:25:53 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.
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 <matz@ruby-lang.org>
* lib/cgi.rb (CGI::QueryExtension::read_multipart): CGI content

186
eval.c
View file

@ -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"
@ -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 <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
@ -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();
return thread;
}
/*
* 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
@ -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);

93
node.h
View file

@ -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 <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
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

1
ruby.h
View file

@ -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;