summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKasumi Hanazuki <kasumi@rollingapple.net>2023-03-25 08:12:23 +0900
committerGitHub <noreply@github.com>2023-03-25 12:12:23 +1300
commit09295ea796900fb7b05d29e93364090e21598566 (patch)
tree0d590abbe67939eaaa1b9d7c79a7b31d39aa5322
parent59c3fac6c4d803019095eebb92b0d2862450ded6 (diff)
downloadruby-09295ea796900fb7b05d29e93364090e21598566.tar.gz
IO::Buffer#resize: Free internal buffer if new size is zero (#7569)
`#resize(0)` on an IO::Buffer with internal buffer allocated will result in calling `realloc(data->base, 0)`. The behavior of `realloc` with size = 0 is implementation-defined (glibc frees the object and returns NULL, while BSDs return an inaccessible object). And thus such usage is deprecated in standard C (upcoming C23 will make it UB). To avoid this problem, just `free`s the memory when the new size is zero.
-rw-r--r--io_buffer.c5
-rw-r--r--test/ruby/test_io_buffer.rb18
2 files changed, 23 insertions, 0 deletions
diff --git a/io_buffer.c b/io_buffer.c
index 91083cd7e4..1ff3935094 100644
--- a/io_buffer.c
+++ b/io_buffer.c
@@ -1422,6 +1422,11 @@ rb_io_buffer_resize(VALUE self, size_t size)
#endif
if (data->flags & RB_IO_BUFFER_INTERNAL) {
+ if (size == 0) {
+ io_buffer_free(data);
+ return;
+ }
+
void *base = realloc(data->base, size);
if (!base) {
diff --git a/test/ruby/test_io_buffer.rb b/test/ruby/test_io_buffer.rb
index 55e347471d..e29400d76e 100644
--- a/test/ruby/test_io_buffer.rb
+++ b/test/ruby/test_io_buffer.rb
@@ -156,6 +156,24 @@ class TestIOBuffer < Test::Unit::TestCase
assert_equal message, buffer.get_string(0, message.bytesize)
end
+ def test_resize_zero_internal
+ buffer = IO::Buffer.new(1)
+
+ buffer.resize(0)
+ assert_equal 0, buffer.size
+
+ buffer.resize(1)
+ assert_equal 1, buffer.size
+ end
+
+ def test_resize_zero_external
+ buffer = IO::Buffer.for('1')
+
+ assert_raise IO::Buffer::AccessError do
+ buffer.resize(0)
+ end
+ end
+
def test_compare_same_size
buffer1 = IO::Buffer.new(1)
assert_equal buffer1, buffer1