mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
$SAFE
as a process global state. [Feature #14250]
* vm_core.h (rb_vm_t): move `rb_execution_context_t::safe_level` to `rb_vm_t::safe_level_` because `$SAFE` is a process (VM) global state. * vm_core.h (rb_proc_t): remove `rb_proc_t::safe_level` because `Proc` objects don't need to keep `$SAFE` at the creation. Also make `is_from_method` and `is_lambda` as 1 bit fields. * cont.c (cont_restore_thread): no need to keep `$SAFE` for Continuation. * eval.c (ruby_cleanup): use `rb_set_safe_level_force()` instead of access `vm->safe_level_` directly. * eval_jump.c: End procs `END{}` doesn't keep `$SAFE`. * proc.c (proc_dup): removed and introduce `rb_proc_dup` in vm.c. * safe.c (rb_set_safe_level): don't check `$SAFE` 1 -> 0 changes. * safe.c (safe_setter): use `rb_set_safe_level()`. * thread.c (rb_thread_safe_level): `Thread#safe_level` returns `$SAFE`. It should be obsolete. * transcode.c (load_transcoder_entry): `rb_safe_level()` only returns 0 or 1 so that this check is not needed. * vm.c (vm_proc_create_from_captured): don't need to keep `$SAFE` for Proc. * vm.c (rb_proc_create): renamed to `proc_create`. * vm.c (rb_proc_dup): moved from proc.c. * vm.c (vm_invoke_proc): do not need to set and restore `$SAFE` for `Proc#call`. * vm_eval.c (rb_eval_cmd): rename a local variable to represent clearer meaning. * lib/drb/drb.rb: restore `$SAFE`. * lib/erb.rb: restore `$SAFE`, too. * test/lib/leakchecker.rb: check `$SAFE == 0` at the end of tests. * test/rubygems/test_gem.rb: do not set `$SAFE = 1`. * bootstraptest/test_proc.rb: catch up this change. * spec/ruby/optional/capi/string_spec.rb: ditto. * test/bigdecimal/test_bigdecimal.rb: ditto. * test/fiddle/test_func.rb: ditto. * test/fiddle/test_handle.rb: ditto. * test/net/imap/test_imap_response_parser.rb: ditto. * test/pathname/test_pathname.rb: ditto. * test/readline/test_readline.rb: ditto. * test/ruby/test_file.rb: ditto. * test/ruby/test_optimization.rb: ditto. * test/ruby/test_proc.rb: ditto. * test/ruby/test_require.rb: ditto. * test/ruby/test_thread.rb: ditto. * test/rubygems/test_gem_specification.rb: ditto. * test/test_tempfile.rb: ditto. * test/test_tmpdir.rb: ditto. * test/win32ole/test_win32ole.rb: ditto. * test/win32ole/test_win32ole_event.rb: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@61510 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
67850e8a9e
commit
c39bdb798d
33 changed files with 159 additions and 119 deletions
24
NEWS
24
NEWS
|
@ -11,3 +11,27 @@ with all sufficient information, see the ChangeLog file or Redmine
|
||||||
(e.g. <tt>https://bugs.ruby-lang.org/issues/$FEATURE_OR_BUG_NUMBER</tt>)
|
(e.g. <tt>https://bugs.ruby-lang.org/issues/$FEATURE_OR_BUG_NUMBER</tt>)
|
||||||
|
|
||||||
== Changes since the 2.5.0 release
|
== Changes since the 2.5.0 release
|
||||||
|
|
||||||
|
=== Language changes
|
||||||
|
|
||||||
|
* $SAFE is a process global state and we can set 0 again. [Feature #14250]
|
||||||
|
|
||||||
|
=== Core classes updates (outstanding ones only)
|
||||||
|
|
||||||
|
* Proc
|
||||||
|
|
||||||
|
* Proc#call doesn't change $SAFE any more. [Feature #14250]
|
||||||
|
|
||||||
|
=== Stdlib updates (outstanding ones only)
|
||||||
|
|
||||||
|
=== Compatibility issues (excluding feature bug fixes)
|
||||||
|
|
||||||
|
=== Stdlib compatibility issues (excluding feature bug fixes)
|
||||||
|
|
||||||
|
=== C API updates
|
||||||
|
|
||||||
|
=== Supported platform changes
|
||||||
|
|
||||||
|
=== Implementation improvements
|
||||||
|
|
||||||
|
=== Miscellaneous changes
|
||||||
|
|
|
@ -224,14 +224,14 @@ assert_equal %q{[[nil, []], [1, []], [1, [2]], [1, [2, 3]]]}, %q{
|
||||||
Proc.new{|a, *b| [a, b]}.call(1, 2, 3),
|
Proc.new{|a, *b| [a, b]}.call(1, 2, 3),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
assert_equal %q{0}, %q{
|
assert_equal %q{1}, %q{
|
||||||
pr = proc{
|
pr = proc{
|
||||||
$SAFE
|
$SAFE
|
||||||
}
|
}
|
||||||
$SAFE = 1
|
$SAFE = 1
|
||||||
pr.call
|
pr.call
|
||||||
}
|
}
|
||||||
assert_equal %q{[1, 0]}, %q{
|
assert_equal %q{[1, 1]}, %q{
|
||||||
pr = proc{
|
pr = proc{
|
||||||
$SAFE += 1
|
$SAFE += 1
|
||||||
}
|
}
|
||||||
|
|
1
cont.c
1
cont.c
|
@ -696,7 +696,6 @@ cont_restore_thread(rb_context_t *cont)
|
||||||
/* other members of ec */
|
/* other members of ec */
|
||||||
|
|
||||||
th->ec->cfp = sec->cfp;
|
th->ec->cfp = sec->cfp;
|
||||||
th->ec->safe_level = sec->safe_level;
|
|
||||||
th->ec->raised_flag = sec->raised_flag;
|
th->ec->raised_flag = sec->raised_flag;
|
||||||
th->ec->tag = sec->tag;
|
th->ec->tag = sec->tag;
|
||||||
th->ec->protect_tag = sec->protect_tag;
|
th->ec->protect_tag = sec->protect_tag;
|
||||||
|
|
2
eval.c
2
eval.c
|
@ -175,7 +175,7 @@ ruby_cleanup(volatile int ex)
|
||||||
|
|
||||||
step_0: step++;
|
step_0: step++;
|
||||||
errs[1] = th->ec->errinfo;
|
errs[1] = th->ec->errinfo;
|
||||||
th->ec->safe_level = 0;
|
rb_set_safe_level_force(0);
|
||||||
ruby_init_stack(&errs[STACK_UPPER(errs, 0, 1)]);
|
ruby_init_stack(&errs[STACK_UPPER(errs, 0, 1)]);
|
||||||
|
|
||||||
SAVE_ROOT_JMPBUF(th, ruby_finalize_0());
|
SAVE_ROOT_JMPBUF(th, ruby_finalize_0());
|
||||||
|
|
|
@ -50,7 +50,6 @@ rb_f_at_exit(void)
|
||||||
struct end_proc_data {
|
struct end_proc_data {
|
||||||
void (*func) ();
|
void (*func) ();
|
||||||
VALUE data;
|
VALUE data;
|
||||||
int safe;
|
|
||||||
struct end_proc_data *next;
|
struct end_proc_data *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -72,7 +71,6 @@ rb_set_end_proc(void (*func)(VALUE), VALUE data)
|
||||||
link->next = *list;
|
link->next = *list;
|
||||||
link->func = func;
|
link->func = func;
|
||||||
link->data = data;
|
link->data = data;
|
||||||
link->safe = rb_safe_level();
|
|
||||||
*list = link;
|
*list = link;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +102,6 @@ exec_end_procs_chain(struct end_proc_data *volatile *procs, VALUE *errp)
|
||||||
*procs = link->next;
|
*procs = link->next;
|
||||||
endproc = *link;
|
endproc = *link;
|
||||||
xfree(link);
|
xfree(link);
|
||||||
rb_set_safe_level_force(endproc.safe);
|
|
||||||
(*endproc.func) (endproc.data);
|
(*endproc.func) (endproc.data);
|
||||||
*errp = errinfo;
|
*errp = errinfo;
|
||||||
}
|
}
|
||||||
|
@ -114,7 +111,6 @@ void
|
||||||
rb_exec_end_proc(void)
|
rb_exec_end_proc(void)
|
||||||
{
|
{
|
||||||
enum ruby_tag_type state;
|
enum ruby_tag_type state;
|
||||||
volatile int safe = rb_safe_level();
|
|
||||||
rb_execution_context_t * volatile ec = GET_EC();
|
rb_execution_context_t * volatile ec = GET_EC();
|
||||||
volatile VALUE errinfo = ec->errinfo;
|
volatile VALUE errinfo = ec->errinfo;
|
||||||
|
|
||||||
|
@ -133,7 +129,6 @@ rb_exec_end_proc(void)
|
||||||
}
|
}
|
||||||
EC_POP_TAG();
|
EC_POP_TAG();
|
||||||
|
|
||||||
rb_set_safe_level_force(safe);
|
|
||||||
ec->errinfo = errinfo;
|
ec->errinfo = errinfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1571,17 +1571,23 @@ module DRb
|
||||||
if $SAFE < @safe_level
|
if $SAFE < @safe_level
|
||||||
info = Thread.current['DRb']
|
info = Thread.current['DRb']
|
||||||
if @block
|
if @block
|
||||||
@result = Thread.new {
|
@result = Thread.new do
|
||||||
Thread.current['DRb'] = info
|
Thread.current['DRb'] = info
|
||||||
|
prev_safe_level = $SAFE
|
||||||
$SAFE = @safe_level
|
$SAFE = @safe_level
|
||||||
perform_with_block
|
perform_with_block
|
||||||
}.value
|
ensure
|
||||||
|
$SAFE = prev_safe_level
|
||||||
|
end.value
|
||||||
else
|
else
|
||||||
@result = Thread.new {
|
@result = Thread.new do
|
||||||
Thread.current['DRb'] = info
|
Thread.current['DRb'] = info
|
||||||
|
prev_safe_level = $SAFE
|
||||||
$SAFE = @safe_level
|
$SAFE = @safe_level
|
||||||
perform_without_block
|
perform_without_block
|
||||||
}.value
|
ensure
|
||||||
|
$SAFE = prev_safe_level
|
||||||
|
end.value
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if @block
|
if @block
|
||||||
|
|
|
@ -864,10 +864,13 @@ class ERB
|
||||||
#
|
#
|
||||||
def result(b=new_toplevel)
|
def result(b=new_toplevel)
|
||||||
if @safe_level
|
if @safe_level
|
||||||
proc {
|
proc do
|
||||||
|
prev_safe_level = $SAFE
|
||||||
$SAFE = @safe_level
|
$SAFE = @safe_level
|
||||||
eval(@src, b, (@filename || '(erb)'), @lineno)
|
eval(@src, b, (@filename || '(erb)'), @lineno)
|
||||||
}.call
|
ensure
|
||||||
|
$SAFE = prev_safe_level
|
||||||
|
end.call
|
||||||
else
|
else
|
||||||
eval(@src, b, (@filename || '(erb)'), @lineno)
|
eval(@src, b, (@filename || '(erb)'), @lineno)
|
||||||
end
|
end
|
||||||
|
|
25
proc.c
25
proc.c
|
@ -124,28 +124,11 @@ rb_obj_is_proc(VALUE proc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VALUE rb_proc_create(VALUE klass, const struct rb_block *block,
|
|
||||||
int8_t safe_level, int8_t is_from_method, int8_t is_lambda);
|
|
||||||
|
|
||||||
/* :nodoc: */
|
|
||||||
static VALUE
|
|
||||||
proc_dup(VALUE self)
|
|
||||||
{
|
|
||||||
VALUE procval;
|
|
||||||
rb_proc_t *src;
|
|
||||||
|
|
||||||
GetProcPtr(self, src);
|
|
||||||
procval = rb_proc_create(rb_cProc, &src->block,
|
|
||||||
src->safe_level, src->is_from_method, src->is_lambda);
|
|
||||||
RB_GC_GUARD(self); /* for: body = proc_dup(body) */
|
|
||||||
return procval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* :nodoc: */
|
/* :nodoc: */
|
||||||
static VALUE
|
static VALUE
|
||||||
proc_clone(VALUE self)
|
proc_clone(VALUE self)
|
||||||
{
|
{
|
||||||
VALUE procval = proc_dup(self);
|
VALUE procval = rb_proc_dup(self);
|
||||||
CLONESETUP(procval, self);
|
CLONESETUP(procval, self);
|
||||||
return procval;
|
return procval;
|
||||||
}
|
}
|
||||||
|
@ -752,7 +735,7 @@ proc_new(VALUE klass, int8_t is_lambda)
|
||||||
return procval;
|
return procval;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
VALUE newprocval = proc_dup(procval);
|
VALUE newprocval = rb_proc_dup(procval);
|
||||||
RBASIC_SET_CLASS(newprocval, klass);
|
RBASIC_SET_CLASS(newprocval, klass);
|
||||||
return newprocval;
|
return newprocval;
|
||||||
}
|
}
|
||||||
|
@ -1982,7 +1965,7 @@ rb_mod_define_method(int argc, VALUE *argv, VALUE mod)
|
||||||
RB_GC_GUARD(body);
|
RB_GC_GUARD(body);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
VALUE procval = proc_dup(body);
|
VALUE procval = rb_proc_dup(body);
|
||||||
if (vm_proc_iseq(procval) != NULL) {
|
if (vm_proc_iseq(procval) != NULL) {
|
||||||
rb_proc_t *proc;
|
rb_proc_t *proc;
|
||||||
GetProcPtr(procval, proc);
|
GetProcPtr(procval, proc);
|
||||||
|
@ -3115,7 +3098,7 @@ Init_Proc(void)
|
||||||
rb_define_method(rb_cProc, "to_proc", proc_to_proc, 0);
|
rb_define_method(rb_cProc, "to_proc", proc_to_proc, 0);
|
||||||
rb_define_method(rb_cProc, "arity", proc_arity, 0);
|
rb_define_method(rb_cProc, "arity", proc_arity, 0);
|
||||||
rb_define_method(rb_cProc, "clone", proc_clone, 0);
|
rb_define_method(rb_cProc, "clone", proc_clone, 0);
|
||||||
rb_define_method(rb_cProc, "dup", proc_dup, 0);
|
rb_define_method(rb_cProc, "dup", rb_proc_dup, 0);
|
||||||
rb_define_method(rb_cProc, "hash", proc_hash, 0);
|
rb_define_method(rb_cProc, "hash", proc_hash, 0);
|
||||||
rb_define_method(rb_cProc, "to_s", proc_to_s, 0);
|
rb_define_method(rb_cProc, "to_s", proc_to_s, 0);
|
||||||
rb_define_alias(rb_cProc, "inspect", "to_s");
|
rb_define_alias(rb_cProc, "inspect", "to_s");
|
||||||
|
|
46
safe.c
46
safe.c
|
@ -34,28 +34,34 @@ ruby_safe_level_2_warning(void)
|
||||||
int
|
int
|
||||||
rb_safe_level(void)
|
rb_safe_level(void)
|
||||||
{
|
{
|
||||||
return GET_EC()->safe_level;
|
return GET_VM()->safe_level_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
rb_set_safe_level_force(int safe)
|
rb_set_safe_level_force(int safe)
|
||||||
{
|
{
|
||||||
GET_EC()->safe_level = safe;
|
GET_VM()->safe_level_ = safe;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
rb_set_safe_level(int level)
|
rb_set_safe_level(int level)
|
||||||
{
|
{
|
||||||
rb_execution_context_t *ec = GET_EC();
|
rb_vm_t *vm = GET_VM();
|
||||||
|
|
||||||
if (level > ec->safe_level) {
|
if (level > SAFE_LEVEL_MAX) {
|
||||||
if (level > SAFE_LEVEL_MAX) {
|
rb_raise(rb_eArgError, "$SAFE=2 to 4 are obsolete");
|
||||||
rb_raise(rb_eArgError, "$SAFE=2 to 4 are obsolete");
|
}
|
||||||
}
|
else if (level < 0) {
|
||||||
/* block parameters */
|
rb_raise(rb_eArgError, "$SAFE should be >= 0");
|
||||||
rb_vm_stack_to_heap(ec);
|
}
|
||||||
|
else {
|
||||||
|
int line;
|
||||||
|
const char *path = rb_source_location_cstr(&line);
|
||||||
|
|
||||||
ec->safe_level = level;
|
if (0) fprintf(stderr, "%s:%d $SAFE %d -> %d\n",
|
||||||
|
path ? path : "-", line, vm->safe_level_, level);
|
||||||
|
|
||||||
|
vm->safe_level_ = level;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,26 +74,8 @@ safe_getter(void)
|
||||||
static void
|
static void
|
||||||
safe_setter(VALUE val)
|
safe_setter(VALUE val)
|
||||||
{
|
{
|
||||||
rb_execution_context_t *ec = GET_EC();
|
|
||||||
int current_level = ec->safe_level;
|
|
||||||
int level = NUM2INT(val);
|
int level = NUM2INT(val);
|
||||||
|
rb_set_safe_level(level);
|
||||||
if (level == current_level) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (level < current_level) {
|
|
||||||
rb_raise(rb_eSecurityError,
|
|
||||||
"tried to downgrade safe level from %d to %d",
|
|
||||||
current_level, level);
|
|
||||||
}
|
|
||||||
else if (level > SAFE_LEVEL_MAX) {
|
|
||||||
rb_raise(rb_eArgError, "$SAFE=2 to 4 are obsolete");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* block parameters */
|
|
||||||
rb_vm_stack_to_heap(ec);
|
|
||||||
|
|
||||||
ec->safe_level = level;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -485,6 +485,8 @@ describe "C-API String function" do
|
||||||
@s.SafeStringValue("str".taint)
|
@s.SafeStringValue("str".taint)
|
||||||
}.should raise_error(SecurityError)
|
}.should raise_error(SecurityError)
|
||||||
}.join
|
}.join
|
||||||
|
ensure
|
||||||
|
$SAFE = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like :string_value_macro, :SafeStringValue
|
it_behaves_like :string_value_macro, :SafeStringValue
|
||||||
|
|
|
@ -125,6 +125,8 @@ class TestBigDecimal < Test::Unit::TestCase
|
||||||
$SAFE = 1
|
$SAFE = 1
|
||||||
BigDecimal('1'.taint)
|
BigDecimal('1'.taint)
|
||||||
}.join
|
}.join
|
||||||
|
ensure
|
||||||
|
$SAFE = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_s_ver
|
def test_s_ver
|
||||||
|
@ -195,6 +197,8 @@ class TestBigDecimal < Test::Unit::TestCase
|
||||||
$SAFE = 1
|
$SAFE = 1
|
||||||
BigDecimal('1'.taint)
|
BigDecimal('1'.taint)
|
||||||
}.join
|
}.join
|
||||||
|
ensure
|
||||||
|
$SAFE = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def _test_mode(type)
|
def _test_mode(type)
|
||||||
|
|
|
@ -19,6 +19,8 @@ module Fiddle
|
||||||
f.call("uname -rs".dup.taint)
|
f.call("uname -rs".dup.taint)
|
||||||
end
|
end
|
||||||
}.join
|
}.join
|
||||||
|
ensure
|
||||||
|
$SAFE = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_sinf
|
def test_sinf
|
||||||
|
|
|
@ -15,6 +15,8 @@ module Fiddle
|
||||||
Fiddle::Handle.new(LIBC_SO.dup.taint)
|
Fiddle::Handle.new(LIBC_SO.dup.taint)
|
||||||
}
|
}
|
||||||
end.join
|
end.join
|
||||||
|
ensure
|
||||||
|
$SAFE = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_safe_function_lookup
|
def test_safe_function_lookup
|
||||||
|
@ -25,6 +27,8 @@ module Fiddle
|
||||||
h["qsort".dup.taint]
|
h["qsort".dup.taint]
|
||||||
}
|
}
|
||||||
end.join
|
end.join
|
||||||
|
ensure
|
||||||
|
$SAFE = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_to_i
|
def test_to_i
|
||||||
|
|
|
@ -15,10 +15,15 @@ class LeakChecker
|
||||||
check_tempfile_leak(test_name),
|
check_tempfile_leak(test_name),
|
||||||
check_env(test_name),
|
check_env(test_name),
|
||||||
check_encodings(test_name),
|
check_encodings(test_name),
|
||||||
|
check_safe(test_name),
|
||||||
]
|
]
|
||||||
GC.start if leaks.any?
|
GC.start if leaks.any?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def check_safe test_name
|
||||||
|
puts "#{test_name}: $SAFE == #{$SAFE}" unless $SAFE == 0
|
||||||
|
end
|
||||||
|
|
||||||
def find_fds
|
def find_fds
|
||||||
if IO.respond_to?(:console) and (m = IO.method(:console)).arity.nonzero?
|
if IO.respond_to?(:console) and (m = IO.method(:console)).arity.nonzero?
|
||||||
m[:close]
|
m[:close]
|
||||||
|
|
|
@ -29,6 +29,8 @@ class IMAPResponseParserTest < Test::Unit::TestCase
|
||||||
EOF
|
EOF
|
||||||
}.call
|
}.call
|
||||||
assert_equal [:Haschildren], response.data.attr
|
assert_equal [:Haschildren], response.data.attr
|
||||||
|
ensure
|
||||||
|
$SAFE = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_flag_list_too_many_flags
|
def test_flag_list_too_many_flags
|
||||||
|
|
|
@ -1411,6 +1411,8 @@ class TestPathname < Test::Unit::TestCase
|
||||||
$SAFE = 1
|
$SAFE = 1
|
||||||
assert_equal("foo/bar", File.join(Pathname.new("foo"), Pathname.new("bar").taint))
|
assert_equal("foo/bar", File.join(Pathname.new("foo"), Pathname.new("bar").taint))
|
||||||
}.call
|
}.call
|
||||||
|
ensure
|
||||||
|
$SAFE = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_relative_path_from_casefold
|
def test_relative_path_from_casefold
|
||||||
|
|
|
@ -53,6 +53,8 @@ class TestReadline < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
}.join
|
}.join
|
||||||
|
ensure
|
||||||
|
$SAFE = 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -465,6 +465,8 @@ class TestFile < Test::Unit::TestCase
|
||||||
(0..1).each do |level|
|
(0..1).each do |level|
|
||||||
assert_nothing_raised(SecurityError, bug5374) {in_safe[level]}
|
assert_nothing_raised(SecurityError, bug5374) {in_safe[level]}
|
||||||
end
|
end
|
||||||
|
ensure
|
||||||
|
$SAFE = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
if /(bcc|ms|cyg)win|mingw|emx/ =~ RUBY_PLATFORM
|
if /(bcc|ms|cyg)win|mingw|emx/ =~ RUBY_PLATFORM
|
||||||
|
|
|
@ -677,7 +677,7 @@ class TestRubyOptimization < Test::Unit::TestCase
|
||||||
$SAFE = 1
|
$SAFE = 1
|
||||||
b.call
|
b.call
|
||||||
end
|
end
|
||||||
assert_equal 0, foo{$SAFE}
|
assert_equal 1, foo{$SAFE}
|
||||||
END
|
END
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -160,26 +160,34 @@ class TestProc < Test::Unit::TestCase
|
||||||
$SAFE += 1
|
$SAFE += 1
|
||||||
proc {$SAFE}
|
proc {$SAFE}
|
||||||
}.call
|
}.call
|
||||||
assert_equal(safe, $SAFE)
|
|
||||||
assert_equal(safe + 1, p.call)
|
|
||||||
assert_equal(safe, $SAFE)
|
|
||||||
|
|
||||||
|
assert_equal(safe + 1, $SAFE)
|
||||||
|
assert_equal(safe + 1, p.call)
|
||||||
|
assert_equal(safe + 1, $SAFE)
|
||||||
|
|
||||||
|
$SAFE = 0
|
||||||
c.class_eval {define_method(:safe, p)}
|
c.class_eval {define_method(:safe, p)}
|
||||||
assert_equal(safe, x.safe)
|
assert_equal(safe, x.safe)
|
||||||
assert_equal(safe, x.method(:safe).call)
|
|
||||||
assert_equal(safe, x.method(:safe).to_proc.call)
|
|
||||||
|
|
||||||
|
$SAFE = 0
|
||||||
p = proc {$SAFE += 1}
|
p = proc {$SAFE += 1}
|
||||||
assert_equal(safe + 1, p.call)
|
assert_equal(safe + 1, p.call)
|
||||||
assert_equal(safe, $SAFE)
|
assert_equal(safe + 1, $SAFE)
|
||||||
|
|
||||||
|
$SAFE = 0
|
||||||
c.class_eval {define_method(:inc, p)}
|
c.class_eval {define_method(:inc, p)}
|
||||||
assert_equal(safe + 1, proc {x.inc; $SAFE}.call)
|
assert_equal(safe + 1, proc {x.inc; $SAFE}.call)
|
||||||
assert_equal(safe, $SAFE)
|
assert_equal(safe + 1, $SAFE)
|
||||||
|
|
||||||
|
$SAFE = 0
|
||||||
assert_equal(safe + 1, proc {x.method(:inc).call; $SAFE}.call)
|
assert_equal(safe + 1, proc {x.method(:inc).call; $SAFE}.call)
|
||||||
assert_equal(safe, $SAFE)
|
assert_equal(safe + 1, $SAFE)
|
||||||
|
|
||||||
|
$SAFE = 0
|
||||||
assert_equal(safe + 1, proc {x.method(:inc).to_proc.call; $SAFE}.call)
|
assert_equal(safe + 1, proc {x.method(:inc).to_proc.call; $SAFE}.call)
|
||||||
assert_equal(safe, $SAFE)
|
assert_equal(safe + 1, $SAFE)
|
||||||
|
ensure
|
||||||
|
$SAFE = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def m2
|
def m2
|
||||||
|
|
|
@ -112,6 +112,8 @@ class TestRequire < Test::Unit::TestCase
|
||||||
proc do |require_path|
|
proc do |require_path|
|
||||||
$SAFE = 1
|
$SAFE = 1
|
||||||
require(require_path)
|
require(require_path)
|
||||||
|
ensure
|
||||||
|
$SAFE = 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -505,10 +505,10 @@ class TestThread < Test::Unit::TestCase
|
||||||
sleep
|
sleep
|
||||||
end
|
end
|
||||||
Thread.pass until ok
|
Thread.pass until ok
|
||||||
assert_equal(0, Thread.current.safe_level)
|
assert_equal($SAFE, Thread.current.safe_level)
|
||||||
assert_equal(1, t.safe_level)
|
assert_equal($SAFE, t.safe_level)
|
||||||
|
|
||||||
ensure
|
ensure
|
||||||
|
$SAFE = 0
|
||||||
t.kill if t
|
t.kill if t
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ require 'pathname'
|
||||||
require 'tmpdir'
|
require 'tmpdir'
|
||||||
|
|
||||||
# TODO: push this up to test_case.rb once battle tested
|
# TODO: push this up to test_case.rb once battle tested
|
||||||
$SAFE=1
|
|
||||||
$LOAD_PATH.map! do |path|
|
$LOAD_PATH.map! do |path|
|
||||||
path.dup.untaint
|
path.dup.untaint
|
||||||
end
|
end
|
||||||
|
|
|
@ -948,6 +948,9 @@ dependencies: []
|
||||||
@a2.files.clear
|
@a2.files.clear
|
||||||
|
|
||||||
assert_equal @a2, spec
|
assert_equal @a2, spec
|
||||||
|
|
||||||
|
ensure
|
||||||
|
$SAFE = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_self_load_escape_curly
|
def test_self_load_escape_curly
|
||||||
|
|
|
@ -38,6 +38,8 @@ class TestTempfile < Test::Unit::TestCase
|
||||||
assert_nothing_raised(SecurityError, bug3733) {
|
assert_nothing_raised(SecurityError, bug3733) {
|
||||||
proc {$SAFE = 1; File.expand_path(Dir.tmpdir)}.call
|
proc {$SAFE = 1; File.expand_path(Dir.tmpdir)}.call
|
||||||
}
|
}
|
||||||
|
ensure
|
||||||
|
$SAFE = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_saves_in_given_directory
|
def test_saves_in_given_directory
|
||||||
|
|
|
@ -20,6 +20,8 @@ class TestTmpdir < Test::Unit::TestCase
|
||||||
tmpdir << "foo"
|
tmpdir << "foo"
|
||||||
assert_equal(tmpdir_org, Dir.tmpdir)
|
assert_equal(tmpdir_org, Dir.tmpdir)
|
||||||
}.join
|
}.join
|
||||||
|
ensure
|
||||||
|
$SAFE = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_world_writable
|
def test_world_writable
|
||||||
|
|
|
@ -188,6 +188,8 @@ if defined?(WIN32OLE)
|
||||||
th.join
|
th.join
|
||||||
}
|
}
|
||||||
assert_match(/insecure object creation - `Scripting.Dictionary'/, exc.message)
|
assert_match(/insecure object creation - `Scripting.Dictionary'/, exc.message)
|
||||||
|
ensure
|
||||||
|
$SAFE = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_s_new_exc_host_tainted
|
def test_s_new_exc_host_tainted
|
||||||
|
@ -203,6 +205,8 @@ if defined?(WIN32OLE)
|
||||||
th.join
|
th.join
|
||||||
}
|
}
|
||||||
assert_match(/insecure object creation - `localhost'/, exc.message)
|
assert_match(/insecure object creation - `localhost'/, exc.message)
|
||||||
|
ensure
|
||||||
|
$SAFE = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_s_new_DCOM
|
def test_s_new_DCOM
|
||||||
|
@ -242,6 +246,8 @@ if defined?(WIN32OLE)
|
||||||
th.join
|
th.join
|
||||||
}
|
}
|
||||||
assert_match(/insecure connection - `winmgmts:'/, exc.message)
|
assert_match(/insecure connection - `winmgmts:'/, exc.message)
|
||||||
|
ensure
|
||||||
|
$SAFE = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_invoke_accept_symbol_hash_key
|
def test_invoke_accept_symbol_hash_key
|
||||||
|
|
|
@ -395,6 +395,8 @@ if defined?(WIN32OLE_EVENT)
|
||||||
th.join
|
th.join
|
||||||
}
|
}
|
||||||
assert_match(/insecure event creation - `ConnectionEvents'/, exc.message)
|
assert_match(/insecure event creation - `ConnectionEvents'/, exc.message)
|
||||||
|
ensure
|
||||||
|
$SAFE = 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
10
thread.c
10
thread.c
|
@ -2952,18 +2952,16 @@ rb_thread_stop_p(VALUE thread)
|
||||||
* call-seq:
|
* call-seq:
|
||||||
* thr.safe_level -> integer
|
* thr.safe_level -> integer
|
||||||
*
|
*
|
||||||
* Returns the safe level in effect for <i>thr</i>. Setting thread-local safe
|
* Returns the safe level.
|
||||||
* levels can help when implementing sandboxes which run insecure code.
|
|
||||||
*
|
*
|
||||||
* thr = Thread.new { $SAFE = 1; sleep }
|
* This method is obsolete because $SAFE is a process global state.
|
||||||
* Thread.current.safe_level #=> 0
|
* Simply check $SAFE.
|
||||||
* thr.safe_level #=> 1
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
rb_thread_safe_level(VALUE thread)
|
rb_thread_safe_level(VALUE thread)
|
||||||
{
|
{
|
||||||
return INT2NUM(rb_thread_ptr(thread)->ec->safe_level);
|
return UINT2NUM(rb_safe_level());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -368,14 +368,13 @@ load_transcoder_entry(transcoder_entry_t *entry)
|
||||||
const size_t total_len = sizeof(transcoder_lib_prefix) - 1 + len;
|
const size_t total_len = sizeof(transcoder_lib_prefix) - 1 + len;
|
||||||
const VALUE fn = rb_str_new(0, total_len);
|
const VALUE fn = rb_str_new(0, total_len);
|
||||||
char *const path = RSTRING_PTR(fn);
|
char *const path = RSTRING_PTR(fn);
|
||||||
const int safe = rb_safe_level();
|
|
||||||
|
|
||||||
memcpy(path, transcoder_lib_prefix, sizeof(transcoder_lib_prefix) - 1);
|
memcpy(path, transcoder_lib_prefix, sizeof(transcoder_lib_prefix) - 1);
|
||||||
memcpy(path + sizeof(transcoder_lib_prefix) - 1, lib, len);
|
memcpy(path + sizeof(transcoder_lib_prefix) - 1, lib, len);
|
||||||
rb_str_set_len(fn, total_len);
|
rb_str_set_len(fn, total_len);
|
||||||
FL_UNSET(fn, FL_TAINT);
|
FL_UNSET(fn, FL_TAINT);
|
||||||
OBJ_FREEZE(fn);
|
OBJ_FREEZE(fn);
|
||||||
rb_require_safe(fn, safe > 3 ? 3 : safe);
|
rb_require_safe(fn, rb_safe_level());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry->transcoder)
|
if (entry->transcoder)
|
||||||
|
|
43
vm.c
43
vm.c
|
@ -809,7 +809,7 @@ static VALUE
|
||||||
vm_proc_create_from_captured(VALUE klass,
|
vm_proc_create_from_captured(VALUE klass,
|
||||||
const struct rb_captured_block *captured,
|
const struct rb_captured_block *captured,
|
||||||
enum rb_block_type block_type,
|
enum rb_block_type block_type,
|
||||||
int8_t safe_level, int8_t is_from_method, int8_t is_lambda)
|
int8_t is_from_method, int8_t is_lambda)
|
||||||
{
|
{
|
||||||
VALUE procval = rb_proc_alloc(klass);
|
VALUE procval = rb_proc_alloc(klass);
|
||||||
rb_proc_t *proc = RTYPEDDATA_DATA(procval);
|
rb_proc_t *proc = RTYPEDDATA_DATA(procval);
|
||||||
|
@ -822,7 +822,6 @@ vm_proc_create_from_captured(VALUE klass,
|
||||||
rb_vm_block_ep_update(procval, &proc->block, captured->ep);
|
rb_vm_block_ep_update(procval, &proc->block, captured->ep);
|
||||||
|
|
||||||
vm_block_type_set(&proc->block, block_type);
|
vm_block_type_set(&proc->block, block_type);
|
||||||
proc->safe_level = safe_level;
|
|
||||||
proc->is_from_method = is_from_method;
|
proc->is_from_method = is_from_method;
|
||||||
proc->is_lambda = is_lambda;
|
proc->is_lambda = is_lambda;
|
||||||
|
|
||||||
|
@ -849,9 +848,8 @@ rb_vm_block_copy(VALUE obj, const struct rb_block *dst, const struct rb_block *s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VALUE
|
static VALUE
|
||||||
rb_proc_create(VALUE klass, const struct rb_block *block,
|
proc_create(VALUE klass, const struct rb_block *block, int8_t is_from_method, int8_t is_lambda)
|
||||||
int8_t safe_level, int8_t is_from_method, int8_t is_lambda)
|
|
||||||
{
|
{
|
||||||
VALUE procval = rb_proc_alloc(klass);
|
VALUE procval = rb_proc_alloc(klass);
|
||||||
rb_proc_t *proc = RTYPEDDATA_DATA(procval);
|
rb_proc_t *proc = RTYPEDDATA_DATA(procval);
|
||||||
|
@ -859,13 +857,25 @@ rb_proc_create(VALUE klass, const struct rb_block *block,
|
||||||
VM_ASSERT(VM_EP_IN_HEAP_P(GET_EC(), vm_block_ep(block)));
|
VM_ASSERT(VM_EP_IN_HEAP_P(GET_EC(), vm_block_ep(block)));
|
||||||
rb_vm_block_copy(procval, &proc->block, block);
|
rb_vm_block_copy(procval, &proc->block, block);
|
||||||
vm_block_type_set(&proc->block, block->type);
|
vm_block_type_set(&proc->block, block->type);
|
||||||
proc->safe_level = safe_level;
|
|
||||||
proc->is_from_method = is_from_method;
|
proc->is_from_method = is_from_method;
|
||||||
proc->is_lambda = is_lambda;
|
proc->is_lambda = is_lambda;
|
||||||
|
|
||||||
return procval;
|
return procval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VALUE
|
||||||
|
rb_proc_dup(VALUE self)
|
||||||
|
{
|
||||||
|
VALUE procval;
|
||||||
|
rb_proc_t *src;
|
||||||
|
|
||||||
|
GetProcPtr(self, src);
|
||||||
|
procval = proc_create(rb_cProc, &src->block, src->is_from_method, src->is_lambda);
|
||||||
|
RB_GC_GUARD(self); /* for: body = rb_proc_dup(body) */
|
||||||
|
return procval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
VALUE
|
VALUE
|
||||||
rb_vm_make_proc_lambda(const rb_execution_context_t *ec, const struct rb_captured_block *captured, VALUE klass, int8_t is_lambda)
|
rb_vm_make_proc_lambda(const rb_execution_context_t *ec, const struct rb_captured_block *captured, VALUE klass, int8_t is_lambda)
|
||||||
{
|
{
|
||||||
|
@ -880,8 +890,7 @@ rb_vm_make_proc_lambda(const rb_execution_context_t *ec, const struct rb_capture
|
||||||
imemo_type_p(captured->code.val, imemo_ifunc));
|
imemo_type_p(captured->code.val, imemo_ifunc));
|
||||||
|
|
||||||
procval = vm_proc_create_from_captured(klass, captured,
|
procval = vm_proc_create_from_captured(klass, captured,
|
||||||
imemo_type(captured->code.val) == imemo_iseq ? block_type_iseq : block_type_ifunc,
|
imemo_type(captured->code.val) == imemo_iseq ? block_type_iseq : block_type_ifunc, FALSE, is_lambda);
|
||||||
(int8_t)ec->safe_level, FALSE, is_lambda);
|
|
||||||
return procval;
|
return procval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1139,23 +1148,7 @@ static VALUE
|
||||||
vm_invoke_proc(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self,
|
vm_invoke_proc(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self,
|
||||||
int argc, const VALUE *argv, VALUE passed_block_handler)
|
int argc, const VALUE *argv, VALUE passed_block_handler)
|
||||||
{
|
{
|
||||||
VALUE val = Qundef;
|
return invoke_block_from_c_proc(ec, proc, self, argc, argv, passed_block_handler, proc->is_lambda);
|
||||||
enum ruby_tag_type state;
|
|
||||||
volatile int stored_safe = ec->safe_level;
|
|
||||||
|
|
||||||
EC_PUSH_TAG(ec);
|
|
||||||
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
|
|
||||||
ec->safe_level = proc->safe_level;
|
|
||||||
val = invoke_block_from_c_proc(ec, proc, self, argc, argv, passed_block_handler, proc->is_lambda);
|
|
||||||
}
|
|
||||||
EC_POP_TAG();
|
|
||||||
|
|
||||||
ec->safe_level = stored_safe;
|
|
||||||
|
|
||||||
if (state) {
|
|
||||||
EC_JUMP_TAG(ec, state);
|
|
||||||
}
|
|
||||||
return val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
|
|
12
vm_core.h
12
vm_core.h
|
@ -515,6 +515,9 @@ typedef struct rb_vm_struct {
|
||||||
unsigned int running: 1;
|
unsigned int running: 1;
|
||||||
unsigned int thread_abort_on_exception: 1;
|
unsigned int thread_abort_on_exception: 1;
|
||||||
unsigned int thread_report_on_exception: 1;
|
unsigned int thread_report_on_exception: 1;
|
||||||
|
|
||||||
|
unsigned int safe_level_: 1;
|
||||||
|
|
||||||
int trace_running;
|
int trace_running;
|
||||||
volatile int sleeper;
|
volatile int sleeper;
|
||||||
|
|
||||||
|
@ -736,7 +739,6 @@ typedef struct rb_execution_context_struct {
|
||||||
|
|
||||||
struct rb_vm_tag *tag;
|
struct rb_vm_tag *tag;
|
||||||
struct rb_vm_protect_tag *protect_tag;
|
struct rb_vm_protect_tag *protect_tag;
|
||||||
int safe_level;
|
|
||||||
int raised_flag;
|
int raised_flag;
|
||||||
|
|
||||||
/* interrupt flags */
|
/* interrupt flags */
|
||||||
|
@ -899,9 +901,8 @@ RUBY_SYMBOL_EXPORT_END
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const struct rb_block block;
|
const struct rb_block block;
|
||||||
int8_t safe_level; /* 0..1 */
|
unsigned int is_from_method: 1; /* bool */
|
||||||
int8_t is_from_method; /* bool */
|
unsigned int is_lambda: 1; /* bool */
|
||||||
int8_t is_lambda; /* bool */
|
|
||||||
} rb_proc_t;
|
} rb_proc_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -1464,8 +1465,9 @@ VM_BH_FROM_PROC(VALUE procval)
|
||||||
|
|
||||||
/* VM related object allocate functions */
|
/* VM related object allocate functions */
|
||||||
VALUE rb_thread_alloc(VALUE klass);
|
VALUE rb_thread_alloc(VALUE klass);
|
||||||
VALUE rb_proc_alloc(VALUE klass);
|
|
||||||
VALUE rb_binding_alloc(VALUE klass);
|
VALUE rb_binding_alloc(VALUE klass);
|
||||||
|
VALUE rb_proc_alloc(VALUE klass);
|
||||||
|
VALUE rb_proc_dup(VALUE self);
|
||||||
|
|
||||||
/* for debug */
|
/* for debug */
|
||||||
extern void rb_vmdebug_stack_dump_raw(const rb_execution_context_t *ec, const rb_control_frame_t *cfp);
|
extern void rb_vmdebug_stack_dump_raw(const rb_execution_context_t *ec, const rb_control_frame_t *cfp);
|
||||||
|
|
|
@ -1490,7 +1490,7 @@ rb_eval_cmd(VALUE cmd, VALUE arg, int level)
|
||||||
{
|
{
|
||||||
enum ruby_tag_type state;
|
enum ruby_tag_type state;
|
||||||
volatile VALUE val = Qnil; /* OK */
|
volatile VALUE val = Qnil; /* OK */
|
||||||
const int VAR_NOCLOBBERED(safe) = rb_safe_level();
|
const int VAR_NOCLOBBERED(current_safe_level) = rb_safe_level();
|
||||||
rb_execution_context_t * volatile ec = GET_EC();
|
rb_execution_context_t * volatile ec = GET_EC();
|
||||||
|
|
||||||
if (OBJ_TAINTED(cmd)) {
|
if (OBJ_TAINTED(cmd)) {
|
||||||
|
@ -1510,7 +1510,7 @@ rb_eval_cmd(VALUE cmd, VALUE arg, int level)
|
||||||
}
|
}
|
||||||
EC_POP_TAG();
|
EC_POP_TAG();
|
||||||
|
|
||||||
rb_set_safe_level_force(safe);
|
rb_set_safe_level_force(current_safe_level);
|
||||||
if (state) EC_JUMP_TAG(ec, state);
|
if (state) EC_JUMP_TAG(ec, state);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue