From c39bdb798d838d58126b548465908243c41bb1fb Mon Sep 17 00:00:00 2001 From: ko1 Date: Thu, 28 Dec 2017 20:09:24 +0000 Subject: [PATCH] `$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 --- NEWS | 24 +++++++++++ bootstraptest/test_proc.rb | 4 +- cont.c | 1 - eval.c | 2 +- eval_jump.c | 5 --- lib/drb/drb.rb | 14 +++++-- lib/erb.rb | 7 +++- proc.c | 25 ++---------- safe.c | 46 ++++++++-------------- spec/ruby/optional/capi/string_spec.rb | 2 + test/bigdecimal/test_bigdecimal.rb | 4 ++ test/fiddle/test_func.rb | 2 + test/fiddle/test_handle.rb | 4 ++ test/lib/leakchecker.rb | 5 +++ test/net/imap/test_imap_response_parser.rb | 2 + test/pathname/test_pathname.rb | 2 + test/readline/test_readline.rb | 2 + test/ruby/test_file.rb | 2 + test/ruby/test_optimization.rb | 2 +- test/ruby/test_proc.rb | 26 +++++++----- test/ruby/test_require.rb | 2 + test/ruby/test_thread.rb | 6 +-- test/rubygems/test_gem.rb | 2 +- test/rubygems/test_gem_specification.rb | 3 ++ test/test_tempfile.rb | 2 + test/test_tmpdir.rb | 2 + test/win32ole/test_win32ole.rb | 6 +++ test/win32ole/test_win32ole_event.rb | 2 + thread.c | 10 ++--- transcode.c | 3 +- vm.c | 43 +++++++++----------- vm_core.h | 12 +++--- vm_eval.c | 4 +- 33 files changed, 159 insertions(+), 119 deletions(-) diff --git a/NEWS b/NEWS index 4614c3041d..5f62c90dd7 100644 --- a/NEWS +++ b/NEWS @@ -11,3 +11,27 @@ with all sufficient information, see the ChangeLog file or Redmine (e.g. https://bugs.ruby-lang.org/issues/$FEATURE_OR_BUG_NUMBER) == 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 diff --git a/bootstraptest/test_proc.rb b/bootstraptest/test_proc.rb index c23394e8d2..1e384411dc 100644 --- a/bootstraptest/test_proc.rb +++ b/bootstraptest/test_proc.rb @@ -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), ] } -assert_equal %q{0}, %q{ +assert_equal %q{1}, %q{ pr = proc{ $SAFE } $SAFE = 1 pr.call } -assert_equal %q{[1, 0]}, %q{ +assert_equal %q{[1, 1]}, %q{ pr = proc{ $SAFE += 1 } diff --git a/cont.c b/cont.c index 257bbee33a..9556a32799 100644 --- a/cont.c +++ b/cont.c @@ -696,7 +696,6 @@ cont_restore_thread(rb_context_t *cont) /* other members of ec */ th->ec->cfp = sec->cfp; - th->ec->safe_level = sec->safe_level; th->ec->raised_flag = sec->raised_flag; th->ec->tag = sec->tag; th->ec->protect_tag = sec->protect_tag; diff --git a/eval.c b/eval.c index 3fa538e112..a09dddf8c6 100644 --- a/eval.c +++ b/eval.c @@ -175,7 +175,7 @@ ruby_cleanup(volatile int ex) step_0: step++; 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)]); SAVE_ROOT_JMPBUF(th, ruby_finalize_0()); diff --git a/eval_jump.c b/eval_jump.c index 9ea69736cf..a74aed959e 100644 --- a/eval_jump.c +++ b/eval_jump.c @@ -50,7 +50,6 @@ rb_f_at_exit(void) struct end_proc_data { void (*func) (); VALUE data; - int safe; struct end_proc_data *next; }; @@ -72,7 +71,6 @@ rb_set_end_proc(void (*func)(VALUE), VALUE data) link->next = *list; link->func = func; link->data = data; - link->safe = rb_safe_level(); *list = link; } @@ -104,7 +102,6 @@ exec_end_procs_chain(struct end_proc_data *volatile *procs, VALUE *errp) *procs = link->next; endproc = *link; xfree(link); - rb_set_safe_level_force(endproc.safe); (*endproc.func) (endproc.data); *errp = errinfo; } @@ -114,7 +111,6 @@ void rb_exec_end_proc(void) { enum ruby_tag_type state; - volatile int safe = rb_safe_level(); rb_execution_context_t * volatile ec = GET_EC(); volatile VALUE errinfo = ec->errinfo; @@ -133,7 +129,6 @@ rb_exec_end_proc(void) } EC_POP_TAG(); - rb_set_safe_level_force(safe); ec->errinfo = errinfo; } diff --git a/lib/drb/drb.rb b/lib/drb/drb.rb index e47b303aa4..6c516f0359 100644 --- a/lib/drb/drb.rb +++ b/lib/drb/drb.rb @@ -1571,17 +1571,23 @@ module DRb if $SAFE < @safe_level info = Thread.current['DRb'] if @block - @result = Thread.new { + @result = Thread.new do Thread.current['DRb'] = info + prev_safe_level = $SAFE $SAFE = @safe_level perform_with_block - }.value + ensure + $SAFE = prev_safe_level + end.value else - @result = Thread.new { + @result = Thread.new do Thread.current['DRb'] = info + prev_safe_level = $SAFE $SAFE = @safe_level perform_without_block - }.value + ensure + $SAFE = prev_safe_level + end.value end else if @block diff --git a/lib/erb.rb b/lib/erb.rb index 72da7cde98..215a3f516f 100644 --- a/lib/erb.rb +++ b/lib/erb.rb @@ -864,10 +864,13 @@ class ERB # def result(b=new_toplevel) if @safe_level - proc { + proc do + prev_safe_level = $SAFE $SAFE = @safe_level eval(@src, b, (@filename || '(erb)'), @lineno) - }.call + ensure + $SAFE = prev_safe_level + end.call else eval(@src, b, (@filename || '(erb)'), @lineno) end diff --git a/proc.c b/proc.c index 0df71e2a4c..f486c9a291 100644 --- a/proc.c +++ b/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: */ static VALUE proc_clone(VALUE self) { - VALUE procval = proc_dup(self); + VALUE procval = rb_proc_dup(self); CLONESETUP(procval, self); return procval; } @@ -752,7 +735,7 @@ proc_new(VALUE klass, int8_t is_lambda) return procval; } else { - VALUE newprocval = proc_dup(procval); + VALUE newprocval = rb_proc_dup(procval); RBASIC_SET_CLASS(newprocval, klass); return newprocval; } @@ -1982,7 +1965,7 @@ rb_mod_define_method(int argc, VALUE *argv, VALUE mod) RB_GC_GUARD(body); } else { - VALUE procval = proc_dup(body); + VALUE procval = rb_proc_dup(body); if (vm_proc_iseq(procval) != NULL) { rb_proc_t *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, "arity", proc_arity, 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, "to_s", proc_to_s, 0); rb_define_alias(rb_cProc, "inspect", "to_s"); diff --git a/safe.c b/safe.c index 3575ecca15..68ec59689f 100644 --- a/safe.c +++ b/safe.c @@ -34,28 +34,34 @@ ruby_safe_level_2_warning(void) int rb_safe_level(void) { - return GET_EC()->safe_level; + return GET_VM()->safe_level_; } void rb_set_safe_level_force(int safe) { - GET_EC()->safe_level = safe; + GET_VM()->safe_level_ = safe; } void 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) { - rb_raise(rb_eArgError, "$SAFE=2 to 4 are obsolete"); - } - /* block parameters */ - rb_vm_stack_to_heap(ec); + if (level > SAFE_LEVEL_MAX) { + rb_raise(rb_eArgError, "$SAFE=2 to 4 are obsolete"); + } + else if (level < 0) { + rb_raise(rb_eArgError, "$SAFE should be >= 0"); + } + 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 safe_setter(VALUE val) { - rb_execution_context_t *ec = GET_EC(); - int current_level = ec->safe_level; int level = NUM2INT(val); - - 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; + rb_set_safe_level(level); } void diff --git a/spec/ruby/optional/capi/string_spec.rb b/spec/ruby/optional/capi/string_spec.rb index 73e6deb498..5f2309d1cd 100644 --- a/spec/ruby/optional/capi/string_spec.rb +++ b/spec/ruby/optional/capi/string_spec.rb @@ -485,6 +485,8 @@ describe "C-API String function" do @s.SafeStringValue("str".taint) }.should raise_error(SecurityError) }.join + ensure + $SAFE = 0 end it_behaves_like :string_value_macro, :SafeStringValue diff --git a/test/bigdecimal/test_bigdecimal.rb b/test/bigdecimal/test_bigdecimal.rb index 595783c63d..bb9ec92481 100644 --- a/test/bigdecimal/test_bigdecimal.rb +++ b/test/bigdecimal/test_bigdecimal.rb @@ -125,6 +125,8 @@ class TestBigDecimal < Test::Unit::TestCase $SAFE = 1 BigDecimal('1'.taint) }.join + ensure + $SAFE = 0 end def test_s_ver @@ -195,6 +197,8 @@ class TestBigDecimal < Test::Unit::TestCase $SAFE = 1 BigDecimal('1'.taint) }.join + ensure + $SAFE = 0 end def _test_mode(type) diff --git a/test/fiddle/test_func.rb b/test/fiddle/test_func.rb index 8c35833a32..d170c59a75 100644 --- a/test/fiddle/test_func.rb +++ b/test/fiddle/test_func.rb @@ -19,6 +19,8 @@ module Fiddle f.call("uname -rs".dup.taint) end }.join + ensure + $SAFE = 0 end def test_sinf diff --git a/test/fiddle/test_handle.rb b/test/fiddle/test_handle.rb index 77559eb4d9..c0fac39908 100644 --- a/test/fiddle/test_handle.rb +++ b/test/fiddle/test_handle.rb @@ -15,6 +15,8 @@ module Fiddle Fiddle::Handle.new(LIBC_SO.dup.taint) } end.join + ensure + $SAFE = 0 end def test_safe_function_lookup @@ -25,6 +27,8 @@ module Fiddle h["qsort".dup.taint] } end.join + ensure + $SAFE = 0 end def test_to_i diff --git a/test/lib/leakchecker.rb b/test/lib/leakchecker.rb index d236b7d7f0..dbe6f5cdbb 100644 --- a/test/lib/leakchecker.rb +++ b/test/lib/leakchecker.rb @@ -15,10 +15,15 @@ class LeakChecker check_tempfile_leak(test_name), check_env(test_name), check_encodings(test_name), + check_safe(test_name), ] GC.start if leaks.any? end + def check_safe test_name + puts "#{test_name}: $SAFE == #{$SAFE}" unless $SAFE == 0 + end + def find_fds if IO.respond_to?(:console) and (m = IO.method(:console)).arity.nonzero? m[:close] diff --git a/test/net/imap/test_imap_response_parser.rb b/test/net/imap/test_imap_response_parser.rb index 12c8a27017..ed31a03f5a 100644 --- a/test/net/imap/test_imap_response_parser.rb +++ b/test/net/imap/test_imap_response_parser.rb @@ -29,6 +29,8 @@ class IMAPResponseParserTest < Test::Unit::TestCase EOF }.call assert_equal [:Haschildren], response.data.attr + ensure + $SAFE = 0 end def test_flag_list_too_many_flags diff --git a/test/pathname/test_pathname.rb b/test/pathname/test_pathname.rb index ad104d06df..5000904216 100644 --- a/test/pathname/test_pathname.rb +++ b/test/pathname/test_pathname.rb @@ -1411,6 +1411,8 @@ class TestPathname < Test::Unit::TestCase $SAFE = 1 assert_equal("foo/bar", File.join(Pathname.new("foo"), Pathname.new("bar").taint)) }.call + ensure + $SAFE = 0 end def test_relative_path_from_casefold diff --git a/test/readline/test_readline.rb b/test/readline/test_readline.rb index 68c1ff7312..ce85935e9d 100644 --- a/test/readline/test_readline.rb +++ b/test/readline/test_readline.rb @@ -53,6 +53,8 @@ class TestReadline < Test::Unit::TestCase end end }.join + ensure + $SAFE = 0 end end diff --git a/test/ruby/test_file.rb b/test/ruby/test_file.rb index 2aa145a303..9f09ac6fd1 100644 --- a/test/ruby/test_file.rb +++ b/test/ruby/test_file.rb @@ -465,6 +465,8 @@ class TestFile < Test::Unit::TestCase (0..1).each do |level| assert_nothing_raised(SecurityError, bug5374) {in_safe[level]} end + ensure + $SAFE = 0 end if /(bcc|ms|cyg)win|mingw|emx/ =~ RUBY_PLATFORM diff --git a/test/ruby/test_optimization.rb b/test/ruby/test_optimization.rb index 11cf1ffbfb..dc542b644e 100644 --- a/test/ruby/test_optimization.rb +++ b/test/ruby/test_optimization.rb @@ -677,7 +677,7 @@ class TestRubyOptimization < Test::Unit::TestCase $SAFE = 1 b.call end - assert_equal 0, foo{$SAFE} + assert_equal 1, foo{$SAFE} END end diff --git a/test/ruby/test_proc.rb b/test/ruby/test_proc.rb index 1a1ca24228..1607baf067 100644 --- a/test/ruby/test_proc.rb +++ b/test/ruby/test_proc.rb @@ -160,26 +160,34 @@ class TestProc < Test::Unit::TestCase $SAFE += 1 proc {$SAFE} }.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)} 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} assert_equal(safe + 1, p.call) - assert_equal(safe, $SAFE) + assert_equal(safe + 1, $SAFE) + $SAFE = 0 c.class_eval {define_method(:inc, p)} 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, $SAFE) + assert_equal(safe + 1, $SAFE) + + $SAFE = 0 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 def m2 diff --git a/test/ruby/test_require.rb b/test/ruby/test_require.rb index 28cf686a26..af8e6e30fa 100644 --- a/test/ruby/test_require.rb +++ b/test/ruby/test_require.rb @@ -112,6 +112,8 @@ class TestRequire < Test::Unit::TestCase proc do |require_path| $SAFE = 1 require(require_path) + ensure + $SAFE = 0 end end diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb index 591292962d..cf3eefa25f 100644 --- a/test/ruby/test_thread.rb +++ b/test/ruby/test_thread.rb @@ -505,10 +505,10 @@ class TestThread < Test::Unit::TestCase sleep end Thread.pass until ok - assert_equal(0, Thread.current.safe_level) - assert_equal(1, t.safe_level) - + assert_equal($SAFE, Thread.current.safe_level) + assert_equal($SAFE, t.safe_level) ensure + $SAFE = 0 t.kill if t end diff --git a/test/rubygems/test_gem.rb b/test/rubygems/test_gem.rb index 8a11cc2ecf..315aea020f 100644 --- a/test/rubygems/test_gem.rb +++ b/test/rubygems/test_gem.rb @@ -7,7 +7,7 @@ require 'pathname' require 'tmpdir' # TODO: push this up to test_case.rb once battle tested -$SAFE=1 + $LOAD_PATH.map! do |path| path.dup.untaint end diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb index bb6acbc7de..ab3a7399af 100644 --- a/test/rubygems/test_gem_specification.rb +++ b/test/rubygems/test_gem_specification.rb @@ -948,6 +948,9 @@ dependencies: [] @a2.files.clear assert_equal @a2, spec + + ensure + $SAFE = 0 end def test_self_load_escape_curly diff --git a/test/test_tempfile.rb b/test/test_tempfile.rb index 465ce42e36..63f5468638 100644 --- a/test/test_tempfile.rb +++ b/test/test_tempfile.rb @@ -38,6 +38,8 @@ class TestTempfile < Test::Unit::TestCase assert_nothing_raised(SecurityError, bug3733) { proc {$SAFE = 1; File.expand_path(Dir.tmpdir)}.call } + ensure + $SAFE = 0 end def test_saves_in_given_directory diff --git a/test/test_tmpdir.rb b/test/test_tmpdir.rb index 691d52f3cb..07c7ddc736 100644 --- a/test/test_tmpdir.rb +++ b/test/test_tmpdir.rb @@ -20,6 +20,8 @@ class TestTmpdir < Test::Unit::TestCase tmpdir << "foo" assert_equal(tmpdir_org, Dir.tmpdir) }.join + ensure + $SAFE = 0 end def test_world_writable diff --git a/test/win32ole/test_win32ole.rb b/test/win32ole/test_win32ole.rb index 92e97a7c6d..adc7c9c83a 100644 --- a/test/win32ole/test_win32ole.rb +++ b/test/win32ole/test_win32ole.rb @@ -188,6 +188,8 @@ if defined?(WIN32OLE) th.join } assert_match(/insecure object creation - `Scripting.Dictionary'/, exc.message) + ensure + $SAFE = 0 end def test_s_new_exc_host_tainted @@ -203,6 +205,8 @@ if defined?(WIN32OLE) th.join } assert_match(/insecure object creation - `localhost'/, exc.message) + ensure + $SAFE = 0 end def test_s_new_DCOM @@ -242,6 +246,8 @@ if defined?(WIN32OLE) th.join } assert_match(/insecure connection - `winmgmts:'/, exc.message) + ensure + $SAFE = 0 end def test_invoke_accept_symbol_hash_key diff --git a/test/win32ole/test_win32ole_event.rb b/test/win32ole/test_win32ole_event.rb index 02bbeee321..eef4ec1820 100644 --- a/test/win32ole/test_win32ole_event.rb +++ b/test/win32ole/test_win32ole_event.rb @@ -395,6 +395,8 @@ if defined?(WIN32OLE_EVENT) th.join } assert_match(/insecure event creation - `ConnectionEvents'/, exc.message) + ensure + $SAFE = 0 end end end diff --git a/thread.c b/thread.c index 9f2267a1f4..767f4bb9cb 100644 --- a/thread.c +++ b/thread.c @@ -2952,18 +2952,16 @@ rb_thread_stop_p(VALUE thread) * call-seq: * thr.safe_level -> integer * - * Returns the safe level in effect for thr. Setting thread-local safe - * levels can help when implementing sandboxes which run insecure code. + * Returns the safe level. * - * thr = Thread.new { $SAFE = 1; sleep } - * Thread.current.safe_level #=> 0 - * thr.safe_level #=> 1 + * This method is obsolete because $SAFE is a process global state. + * Simply check $SAFE. */ static VALUE rb_thread_safe_level(VALUE thread) { - return INT2NUM(rb_thread_ptr(thread)->ec->safe_level); + return UINT2NUM(rb_safe_level()); } /* diff --git a/transcode.c b/transcode.c index 2c0b30ca0e..384fdd63fc 100644 --- a/transcode.c +++ b/transcode.c @@ -368,14 +368,13 @@ load_transcoder_entry(transcoder_entry_t *entry) const size_t total_len = sizeof(transcoder_lib_prefix) - 1 + len; const VALUE fn = rb_str_new(0, total_len); char *const path = RSTRING_PTR(fn); - const int safe = rb_safe_level(); memcpy(path, transcoder_lib_prefix, sizeof(transcoder_lib_prefix) - 1); memcpy(path + sizeof(transcoder_lib_prefix) - 1, lib, len); rb_str_set_len(fn, total_len); FL_UNSET(fn, FL_TAINT); OBJ_FREEZE(fn); - rb_require_safe(fn, safe > 3 ? 3 : safe); + rb_require_safe(fn, rb_safe_level()); } if (entry->transcoder) diff --git a/vm.c b/vm.c index 85888be919..82d7e9b72d 100644 --- a/vm.c +++ b/vm.c @@ -809,7 +809,7 @@ static VALUE vm_proc_create_from_captured(VALUE klass, const struct rb_captured_block *captured, 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); 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); vm_block_type_set(&proc->block, block_type); - proc->safe_level = safe_level; proc->is_from_method = is_from_method; 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 -rb_proc_create(VALUE klass, const struct rb_block *block, - int8_t safe_level, int8_t is_from_method, int8_t is_lambda) +static VALUE +proc_create(VALUE klass, const struct rb_block *block, int8_t is_from_method, int8_t is_lambda) { VALUE procval = rb_proc_alloc(klass); 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))); rb_vm_block_copy(procval, &proc->block, block); vm_block_type_set(&proc->block, block->type); - proc->safe_level = safe_level; proc->is_from_method = is_from_method; proc->is_lambda = is_lambda; 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 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)); procval = vm_proc_create_from_captured(klass, captured, - imemo_type(captured->code.val) == imemo_iseq ? block_type_iseq : block_type_ifunc, - (int8_t)ec->safe_level, FALSE, is_lambda); + imemo_type(captured->code.val) == imemo_iseq ? block_type_iseq : block_type_ifunc, FALSE, is_lambda); return procval; } @@ -1139,23 +1148,7 @@ static VALUE vm_invoke_proc(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self, int argc, const VALUE *argv, VALUE passed_block_handler) { - VALUE val = Qundef; - 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; + return invoke_block_from_c_proc(ec, proc, self, argc, argv, passed_block_handler, proc->is_lambda); } static VALUE diff --git a/vm_core.h b/vm_core.h index d1c6cd6574..d99379daa1 100644 --- a/vm_core.h +++ b/vm_core.h @@ -515,6 +515,9 @@ typedef struct rb_vm_struct { unsigned int running: 1; unsigned int thread_abort_on_exception: 1; unsigned int thread_report_on_exception: 1; + + unsigned int safe_level_: 1; + int trace_running; volatile int sleeper; @@ -736,7 +739,6 @@ typedef struct rb_execution_context_struct { struct rb_vm_tag *tag; struct rb_vm_protect_tag *protect_tag; - int safe_level; int raised_flag; /* interrupt flags */ @@ -899,9 +901,8 @@ RUBY_SYMBOL_EXPORT_END typedef struct { const struct rb_block block; - int8_t safe_level; /* 0..1 */ - int8_t is_from_method; /* bool */ - int8_t is_lambda; /* bool */ + unsigned int is_from_method: 1; /* bool */ + unsigned int is_lambda: 1; /* bool */ } rb_proc_t; typedef struct { @@ -1464,8 +1465,9 @@ VM_BH_FROM_PROC(VALUE procval) /* VM related object allocate functions */ VALUE rb_thread_alloc(VALUE klass); -VALUE rb_proc_alloc(VALUE klass); VALUE rb_binding_alloc(VALUE klass); +VALUE rb_proc_alloc(VALUE klass); +VALUE rb_proc_dup(VALUE self); /* for debug */ extern void rb_vmdebug_stack_dump_raw(const rb_execution_context_t *ec, const rb_control_frame_t *cfp); diff --git a/vm_eval.c b/vm_eval.c index 6769b9b8d6..b4a8d390be 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -1490,7 +1490,7 @@ rb_eval_cmd(VALUE cmd, VALUE arg, int level) { enum ruby_tag_type state; 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(); if (OBJ_TAINTED(cmd)) { @@ -1510,7 +1510,7 @@ rb_eval_cmd(VALUE cmd, VALUE arg, int level) } EC_POP_TAG(); - rb_set_safe_level_force(safe); + rb_set_safe_level_force(current_safe_level); if (state) EC_JUMP_TAG(ec, state); return val; }