merge revision(s) c479492a67: [Backport #20427]

Resize ary when `Array#sort!` block modifies embedded ary

	In cases where `rb_ary_sort_bang` is called with a block and
	tmp is an embedded array, we need to account for the block
	potentially impacting the capacity of ary.

	ex:
	```
	var_0 = (1..70).to_a
	var_0.sort! do |var_0_block_129, var_1_block_129|
	  var_0.pop
	  var_1_block_129 <=> var_0_block_129
	end.shift(3)
	```

	The above example can put the array into a corrupted state
	resulting in a heap buffer overflow and possible segfault:
	```
	ERROR: AddressSanitizer: heap-buffer-overflow on address [...]
	WRITE of size 560 at 0x60b0000034f0 thread T0 [...]
	```

	This commit adds a conditional to determine when the capacity
	of ary has been modified by the provided block. If this is
	the case, ensure that the capacity of ary is adjusted to
	handle at minimum the len of tmp.
This commit is contained in:
Takashi Kokubun 2024-05-29 15:44:55 -07:00
parent 5688bcb54a
commit b44c02ad5a
3 changed files with 13 additions and 1 deletions

View file

@ -3387,6 +3387,9 @@ rb_ary_sort_bang(VALUE ary)
rb_ary_unshare(ary); rb_ary_unshare(ary);
FL_SET_EMBED(ary); FL_SET_EMBED(ary);
} }
if (ARY_EMBED_LEN(tmp) > ARY_CAPA(ary)) {
ary_resize_capa(ary, ARY_EMBED_LEN(tmp));
}
ary_memcpy(ary, 0, ARY_EMBED_LEN(tmp), ARY_EMBED_PTR(tmp)); ary_memcpy(ary, 0, ARY_EMBED_LEN(tmp), ARY_EMBED_PTR(tmp));
ARY_SET_LEN(ary, ARY_EMBED_LEN(tmp)); ARY_SET_LEN(ary, ARY_EMBED_LEN(tmp));
} }

View file

@ -3556,6 +3556,15 @@ class TestArray < Test::Unit::TestCase
assert_equal(10000, eval(lit).size) assert_equal(10000, eval(lit).size)
end end
def test_array_safely_modified_by_sort_block
var_0 = (1..70).to_a
var_0.sort! do |var_0_block_129, var_1_block_129|
var_0.pop
var_1_block_129 <=> var_0_block_129
end.shift(3)
assert_equal((1..67).to_a.reverse, var_0)
end
private private
def need_continuation def need_continuation
unless respond_to?(:callcc, true) unless respond_to?(:callcc, true)

View file

@ -11,7 +11,7 @@
# define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR # define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR
#define RUBY_VERSION_TEENY 1 #define RUBY_VERSION_TEENY 1
#define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR #define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR
#define RUBY_PATCHLEVEL 69 #define RUBY_PATCHLEVEL 70
#include "ruby/version.h" #include "ruby/version.h"
#include "ruby/internal/abi.h" #include "ruby/internal/abi.h"