[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]