diff options
author | nagachika <nagachika@ruby-lang.org> | 2021-10-09 15:36:53 +0900 |
---|---|---|
committer | nagachika <nagachika@ruby-lang.org> | 2021-10-09 15:36:53 +0900 |
commit | cfad0583eb18bb4505f28ee5cfab703a6b9987be (patch) | |
tree | 5ebee9ff3e8ef7142dff8b1877315ca61aa10b21 /test/ruby/test_regexp.rb | |
parent | 2c947e74a0a11fe6c54253c15224dc80054c62a2 (diff) |
merge revision(s) abc0304cb28cb9dcc3476993bc487884c139fd11: [Backport #17507]
Avoid race condition in Regexp#match
In certain conditions, Regexp#match could return a MatchData with
missing captures. This seems to require at the least, multiple
threads calling a method that calls the same block/proc/lambda
which calls Regexp#match.
The race condition happens because the MatchData is passed from
indirectly via the backref, and other threads can modify the
backref.
Fix the issue by:
1. Not reusing the existing MatchData from the backref, and always
allocating a new MatchData.
2. Passing the MatchData directly to the caller using a VALUE*,
instead of indirectly through the backref.
It's likely that variants of this issue exist for other Regexp
methods. Anywhere that MatchData is passed implicitly through
the backref is probably vulnerable to this issue.
Fixes [Bug #17507]
---
re.c | 46 +++++++++++++++++++---------------------------
test/ruby/test_regexp.rb | 21 +++++++++++++++++++++
2 files changed, 40 insertions(+), 27 deletions(-)
Diffstat (limited to 'test/ruby/test_regexp.rb')
-rw-r--r-- | test/ruby/test_regexp.rb | 21 |
1 files changed, 21 insertions, 0 deletions
diff --git a/test/ruby/test_regexp.rb b/test/ruby/test_regexp.rb index bc98170dcc..35d20eeda6 100644 --- a/test/ruby/test_regexp.rb +++ b/test/ruby/test_regexp.rb @@ -265,6 +265,27 @@ class TestRegexp < Test::Unit::TestCase assert_equal(re, re.match("foo").regexp) end + def test_match_lambda_multithread + bug17507 = "[ruby-core:101901]" + str = "a-x-foo-bar-baz-z-b" + + worker = lambda do + m = /foo-([A-Za-z0-9_\.]+)-baz/.match(str) + assert_equal("bar", m[1], bug17507) + + # These two lines are needed to trigger the bug + File.exist? "/tmp" + str.gsub(/foo-bar-baz/, "foo-abc-baz") + end + + def self. threaded_test(worker) + 6.times.map {Thread.new {10_000.times {worker.call}}}.each(&:join) + end + + # The bug only occurs in a method calling a block/proc/lambda + threaded_test(worker) + end + def test_source bug5484 = '[ruby-core:40364]' assert_equal('', //.source) |