ruby-changes:72895
From: Jeremy <ko1@a...>
Date: Thu, 11 Aug 2022 05:02:31 +0900 (JST)
Subject: [ruby-changes:72895] bfa6a8ddc8 (master): Only allow procs created by Symbol#to_proc to call public methods
https://git.ruby-lang.org/ruby.git/commit/?id=bfa6a8ddc8 From bfa6a8ddc84fffe0aef5a0f91b417167e124dbbf Mon Sep 17 00:00:00 2001 From: Jeremy Evans <code@j...> Date: Wed, 10 Aug 2022 13:02:19 -0700 Subject: Only allow procs created by Symbol#to_proc to call public methods Fixes [Bug #18826] Co-authored-by: Nobuyoshi Nakada <nobu@r...> --- spec/ruby/core/kernel/fixtures/warn_core_method.rb | 2 +- spec/ruby/core/symbol/to_proc_spec.rb | 27 ++++++++++++++++ vm_insnhelper.c | 36 +++++++++++++++++----- 3 files changed, 57 insertions(+), 8 deletions(-) diff --git a/spec/ruby/core/kernel/fixtures/warn_core_method.rb b/spec/ruby/core/kernel/fixtures/warn_core_method.rb index f5dee6b668..fd82562404 100644 --- a/spec/ruby/core/kernel/fixtures/warn_core_method.rb +++ b/spec/ruby/core/kernel/fixtures/warn_core_method.rb @@ -1,6 +1,6 @@ https://github.com/ruby/ruby/blob/trunk/spec/ruby/core/kernel/fixtures/warn_core_method.rb#L1 raise 'should be run without RubyGems' if defined?(Gem) -def deprecated(n=1) +public def deprecated(n=1) # puts nil, caller(0), nil warn "use X instead", uplevel: n end diff --git a/spec/ruby/core/symbol/to_proc_spec.rb b/spec/ruby/core/symbol/to_proc_spec.rb index 47f2a939ab..81939e0046 100644 --- a/spec/ruby/core/symbol/to_proc_spec.rb +++ b/spec/ruby/core/symbol/to_proc_spec.rb @@ -46,6 +46,33 @@ describe "Symbol#to_proc" do https://github.com/ruby/ruby/blob/trunk/spec/ruby/core/symbol/to_proc_spec.rb#L46 end end + ruby_version_is "3.2" do + it "only calls public methods" do + body = proc do + public def pub; @a << :pub end + protected def pro; @a << :pro end + private def pri; @a << :pri end + attr_reader :a + end + + @a = [] + singleton_class.class_eval(&body) + tap(&:pub) + proc{tap(&:pro)}.should raise_error(NoMethodError) + proc{tap(&:pri)}.should raise_error(NoMethodError) + @a.should == [:pub] + + @a = [] + c = Class.new(&body) + o = c.new + o.instance_variable_set(:@a, []) + o.tap(&:pub) + proc{tap(&:pro)}.should raise_error(NoMethodError) + proc{o.tap(&:pri)}.should raise_error(NoMethodError) + o.a.should == [:pub] + end + end + it "raises an ArgumentError when calling #call on the Proc without receiver" do -> { :object_id.to_proc.call diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 2c0a369a43..1812f7ce71 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -3182,9 +3182,11 @@ ci_missing_reason(const struct rb_callinfo *ci) https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L3182 return stat; } +static VALUE vm_call_method_missing(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling); + static VALUE vm_call_symbol(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, - struct rb_calling_info *calling, const struct rb_callinfo *ci, VALUE symbol) + struct rb_calling_info *calling, const struct rb_callinfo *ci, VALUE symbol, int flags) { ASSUME(calling->argc >= 0); /* Also assumes CALLER_SETUP_ARG is already done. */ @@ -3194,9 +3196,7 @@ vm_call_symbol(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L3196 VALUE recv = calling->recv; VALUE klass = CLASS_OF(recv); ID mid = rb_check_id(&symbol); - int flags = VM_CALL_FCALL | - VM_CALL_OPT_SEND | - (calling->kw_splat ? VM_CALL_KW_SPLAT : 0); + flags |= VM_CALL_OPT_SEND | (calling->kw_splat ? VM_CALL_KW_SPLAT : 0); if (UNLIKELY(! mid)) { mid = idMethodMissing; @@ -3243,7 +3243,29 @@ vm_call_symbol(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L3243 { .method_missing_reason = missing_reason }, rb_callable_method_entry_with_refinements(klass, mid, NULL)); - return vm_call_method(ec, reg_cfp, calling); + if (flags & VM_CALL_FCALL) { + return vm_call_method(ec, reg_cfp, calling); + } + + const struct rb_callcache *cc = calling->cc; + VM_ASSERT(callable_method_entry_p(vm_cc_cme(cc))); + + if (vm_cc_cme(cc) != NULL) { + switch (METHOD_ENTRY_VISI(vm_cc_cme(cc))) { + case METHOD_VISI_PUBLIC: /* likely */ + return vm_call_method_each_type(ec, reg_cfp, calling); + case METHOD_VISI_PRIVATE: + vm_cc_method_missing_reason_set(cc, MISSING_PRIVATE); + case METHOD_VISI_PROTECTED: + vm_cc_method_missing_reason_set(cc, MISSING_PROTECTED); + break; + default: + VM_UNREACHABLE(vm_call_method); + } + return vm_call_method_missing(ec, reg_cfp, calling); + } + + return vm_call_method_nome(ec, reg_cfp, calling); } static VALUE @@ -3283,7 +3305,7 @@ vm_call_opt_send(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L3305 calling->argc -= 1; DEC_SP(1); - return vm_call_symbol(ec, reg_cfp, calling, calling->ci, sym); + return vm_call_symbol(ec, reg_cfp, calling, calling->ci, sym, VM_CALL_FCALL); } } @@ -4097,7 +4119,7 @@ vm_invoke_symbol_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L4119 VALUE symbol = VM_BH_TO_SYMBOL(block_handler); CALLER_SETUP_ARG(reg_cfp, calling, ci); calling->recv = TOPN(--calling->argc); - return vm_call_symbol(ec, reg_cfp, calling, ci, symbol); + return vm_call_symbol(ec, reg_cfp, calling, ci, symbol, 0); } } -- cgit v1.2.1 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/