8000 Avoid reading unused lvars in Primitive.cexpr · github/ruby@256350b · GitHub
[go: up one dir, main page]

Skip to content

Commit 256350b

Browse files
jhawthornHParker
authored andcommitted
Avoid reading unused lvars in Primitive.cexpr
Previously on builds with optimizations disabled, this could result in an out of bounds read. When we had all of: * built with -O0 * Leaf builtin * Primitive.mandatory_only * "no args builtin", called by vm_call_single_noarg_inline_builti * The stack is escaped to the heap via binding or a proc This is because mk_builtin_loader generated reads for all locals regardless of whether they were used and in the case we generated a mandatory_only iseq that would include more variables than were actually available. On optimized builds, the invalid accesses would be optimized away, and this also was often unnoticed as the invalid access would just hit another part of the stack unless it had been escaped to the heap. The fix here is imperfect, as this could have false positives, but since Primitive.cexpr! is only available within the cruby codebase itself that's probably fine as a proper fix would be much more challenging (the only false positives we found were in rjit.rb). Fixes [Bug #20178] Co-authored-by: Adam Hess <HParker@github.com>
1 parent c27bc9c commit 256350b

File tree

2 files changed

+15
-0
lines changed

2 files changed

+15
-0
lines changed

bootstraptest/test_method.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,3 +1190,12 @@ def test2 o, args, block
11901190
test2 o1, [], block
11911191
$result.join
11921192
}
1193+
1194+
assert_equal 'ok', %q{
1195+
def foo
1196+
binding
1197+
["ok"].first
1198+
end
1199+
foo
1200+
foo
1201+
}, '[Bug #20178]'

tool/mk_builtin_loader.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,11 +263,17 @@ def collect_iseq iseq_ary
263263

264264
def generate_cexpr(ofile, lineno, line_file, body_lineno, text, locals, func_name)
265265
f = StringIO.new
266+
267+
# Avoid generating fetches of lvars we don't need. This is imperfect as it
268+
# will match text inside strings or other false positives.
269+
local_candidates = text.scan(/[a-zA-Z_][a-zA-Z0-9_]*/)
270+
266271
f.puts '{'
267272
lineno += 1
268273
# locals is nil outside methods
269274
locals&.reverse_each&.with_index{|param, i|
270275
next unless Symbol === param
276+
next unless local_candidates.include?(param.to_s)
271277
f.puts "MAYBE_UNUSED(const VALUE) #{param} = rb_vm_lvar(ec, #{-3 - i});"
272278
lineno += 1
273279
}

0 commit comments

Comments
 (0)
0