1
0
Fork 0
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:
ko1 2017-12-28 20:09:24 +00:00
parent 67850e8a9e
commit c39bdb798d
33 changed files with 159 additions and 119 deletions

24
NEWS
View file

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

View file

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

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

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

View file

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

View file

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

View file

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

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -53,6 +53,8 @@ class TestReadline < Test::Unit::TestCase
end end
end end
}.join }.join
ensure
$SAFE = 0
end end
end end

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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