Skip to content

Commit c353b62

Browse files
committed
[Bug #21787] IO::Buffer: Check addition overflows
https://hackerone.com/reports/3437743
1 parent 9519d16 commit c353b62

File tree

2 files changed

+27
-8
lines changed

2 files changed

+27
-8
lines changed

io_buffer.c

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1526,13 +1526,19 @@ VALUE rb_io_buffer_free_locked(VALUE self)
15261526
return self;
15271527
}
15281528

1529+
static bool
1530+
size_sum_is_bigger_than(size_t a, size_t b, size_t x)
1531+
{
1532+
struct rbimpl_size_mul_overflow_tag size = rbimpl_size_add_overflow(a, b);
1533+
return size.left || size.right > x;
1534+
}
1535+
15291536
// Validate that access to the buffer is within bounds, assuming you want to
15301537
// access length bytes from the specified offset.
15311538
static inline void
15321539
io_buffer_validate_range(struct rb_io_buffer *buffer, size_t offset, size_t length)
15331540
{
1534-
// We assume here that offset + length won't overflow:
1535-
if (offset + length > buffer->size) {
1541+
if (size_sum_is_bigger_than(offset, length, buffer->size)) {
15361542
rb_raise(rb_eArgError, "Specified offset+length is bigger than the buffer size!");
15371543
}
15381544
}
@@ -1842,9 +1848,9 @@ rb_io_buffer_compare(VALUE self, VALUE other)
18421848
}
18431849

18441850
static void
1845-
io_buffer_validate_type(size_t size, size_t offset)
1851+
io_buffer_validate_type(size_t size, size_t offset, size_t extend)
18461852
{
1847-
if (offset > size) {
1853+
if (size_sum_is_bigger_than(offset, extend, size)) {
18481854
rb_raise(rb_eArgError, "Type extends beyond end of buffer! (offset=%"PRIdSIZE" > size=%"PRIdSIZE")", offset, size);
18491855
}
18501856
}
@@ -1944,7 +1950,7 @@ static ID RB_IO_BUFFER_DATA_TYPE_##name; \
19441950
static VALUE \
19451951
io_buffer_read_##name(const void* base, size_t size, size_t *offset) \
19461952
{ \
1947-
io_buffer_validate_type(size, *offset + sizeof(type)); \
1953+
io_buffer_validate_type(size, *offset, sizeof(type)); \
19481954
type value; \
19491955
memcpy(&value, (char*)base + *offset, sizeof(type)); \
19501956
if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \
@@ -1955,7 +1961,7 @@ io_buffer_read_##name(const void* base, size_t size, size_t *offset) \
19551961
static void \
19561962
io_buffer_write_##name(const void* base, size_t size, size_t *offset, VALUE _value) \
19571963
{ \
1958-
io_buffer_validate_type(size, *offset + sizeof(type)); \
1964+
io_buffer_validate_type(size, *offset, sizeof(type)); \
19591965
type value = unwrap(_value); \
19601966
if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \
19611967
memcpy((char*)base + *offset, &value, sizeof(type)); \
@@ -2483,7 +2489,7 @@ io_buffer_memmove(struct rb_io_buffer *buffer, size_t offset, const void *source
24832489

24842490
io_buffer_validate_range(buffer, offset, length);
24852491

2486-
if (source_offset + length > source_size) {
2492+
if (size_sum_is_bigger_than(source_offset, length, source_size)) {
24872493
rb_raise(rb_eArgError, "The computed source range exceeds the size of the source buffer!");
24882494
}
24892495

test/ruby/test_io_buffer.rb

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# frozen_string_literal: false
22

33
require 'tempfile'
4+
require 'rbconfig/sizeof'
45

56
class TestIOBuffer < Test::Unit::TestCase
67
experimental = Warning[:experimental]
@@ -414,6 +415,8 @@ def test_zero_length_get_string
414415
:F64 => [-1.0, 0.0, 0.5, 1.0, 128.0],
415416
}
416417

418+
SIZE_MAX = RbConfig::LIMITS["SIZE_MAX"]
419+
417420
def test_get_set_value
418421
buffer = IO::Buffer.new(128)
419422

@@ -422,6 +425,16 @@ def test_get_set_value
422425
buffer.set_value(data_type, 0, value)
423426
assert_equal value, buffer.get_value(data_type, 0), "Converting #{value} as #{data_type}."
424427
end
428+
assert_raise(ArgumentError) {buffer.get_value(data_type, 128)}
429+
assert_raise(ArgumentError) {buffer.set_value(data_type, 128, 0)}
430+
case data_type
431+
when :U8, :S8
432+
else
433+
assert_raise(ArgumentError) {buffer.get_value(data_type, 127)}
434+
assert_raise(ArgumentError) {buffer.set_value(data_type, 127, 0)}
435+
assert_raise(ArgumentError) {buffer.get_value(data_type, SIZE_MAX)}
436+
assert_raise(ArgumentError) {buffer.set_value(data_type, SIZE_MAX, 0)}
437+
end
425438
end
426439
end
427440

@@ -503,7 +516,7 @@ def test_clear
503516
assert_raise(ArgumentError) {buffer.clear(0, 20)}
504517
assert_raise(ArgumentError) {buffer.clear(0, 0, 20)}
505518
assert_raise(ArgumentError) {buffer.clear(0, 10, 10)}
506-
assert_raise(ArgumentError) {buffer.clear(0, (1<<64)-8, 10)}
519+
assert_raise(ArgumentError) {buffer.clear(0, SIZE_MAX-7, 10)}
507520
end
508521

509522
def test_invalidation

0 commit comments

Comments
 (0)