From cfb04966505fa0c01a1fa0b6713d1f770da301a1 Mon Sep 17 00:00:00 2001 From: Jamis Buck Date: Thu, 28 Mar 2024 08:17:42 -0600 Subject: [PATCH 1/2] SECBUG-240 Fix out-of-bounds reads (#325) * final tweaks for BSON 5 release * SECBUG-240 strlen might read beyond end of buffer * ruby 2.6 isn't supported * nix 2.6 --- ext/bson/read.c | 21 ++++++++++++++++++--- ext/bson/write.c | 4 ++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/ext/bson/read.c b/ext/bson/read.c index 57d105889..6369a4663 100644 --- a/ext/bson/read.c +++ b/ext/bson/read.c @@ -29,6 +29,7 @@ static VALUE pvt_get_symbol(byte_buffer_t *b, VALUE rb_buffer, int argc, VALUE * static VALUE pvt_get_boolean(byte_buffer_t *b); static VALUE pvt_read_field(byte_buffer_t *b, VALUE rb_buffer, uint8_t type, int argc, VALUE *argv); static void pvt_skip_cstring(byte_buffer_t *b); +static size_t pvt_strnlen(const byte_buffer_t *b); void pvt_raise_decode_error(volatile VALUE msg) { VALUE klass = pvt_const_get_3("BSON", "Error", "BSONDecodeError"); @@ -128,7 +129,7 @@ VALUE rb_bson_byte_buffer_get_bytes(VALUE self, VALUE i) } VALUE pvt_get_boolean(byte_buffer_t *b){ - VALUE result; + VALUE result = Qnil; char byte_value; ENSURE_BSON_READ(b, 1); byte_value = *READ_PTR(b); @@ -221,7 +222,7 @@ VALUE rb_bson_byte_buffer_get_cstring(VALUE self) int length; TypedData_Get_Struct(self, byte_buffer_t, &rb_byte_buffer_data_type, b); - length = (int)strlen(READ_PTR(b)); + length = (int)pvt_strnlen(b); ENSURE_BSON_READ(b, length); string = rb_enc_str_new(READ_PTR(b), length, rb_utf8_encoding()); b->read_position += length + 1; @@ -234,7 +235,7 @@ VALUE rb_bson_byte_buffer_get_cstring(VALUE self) void pvt_skip_cstring(byte_buffer_t *b) { int length; - length = (int)strlen(READ_PTR(b)); + length = (int)pvt_strnlen(b); ENSURE_BSON_READ(b, length); b->read_position += length + 1; } @@ -438,3 +439,17 @@ VALUE rb_bson_byte_buffer_get_array(int argc, VALUE *argv, VALUE self){ return array; } + +/** + * Returns the length of the given string `str`. If no null-terminating byte + * is present when `len` bytes have been scanned, then `len` is + * returned. + */ +size_t pvt_strnlen(const byte_buffer_t *b) { + const char *ptr = memchr(READ_PTR(b), '\0', READ_SIZE(b)); + + if (!ptr) + rb_raise(rb_eRangeError, "string is too long (possibly not null-terminated)"); + + return (size_t)(ptr - READ_PTR(b)); +} diff --git a/ext/bson/write.c b/ext/bson/write.c index 4268fa617..a3a551e49 100644 --- a/ext/bson/write.c +++ b/ext/bson/write.c @@ -642,6 +642,10 @@ void pvt_put_array_index(byte_buffer_t *b, int32_t index) c_str = buffer; snprintf(buffer, sizeof(buffer), "%d", index); } + // strlen is a potential vector for out-of-bounds errors, but + // the only way for that to happen here, specifically, is if `index` + // is greater than 10e16 - 1, which is far beyond the domain of an + // int32. length = strlen(c_str) + 1; ENSURE_BSON_WRITE(b, length); memcpy(WRITE_PTR(b), c_str, length); From 0e474a3038050ae5fdf1da2a230fd10da2f19bc8 Mon Sep 17 00:00:00 2001 From: Jamis Buck Date: Thu, 28 Mar 2024 14:03:58 -0600 Subject: [PATCH 2/2] version bump --- lib/bson/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bson/version.rb b/lib/bson/version.rb index dea2a8fe4..65ed5cd1c 100644 --- a/lib/bson/version.rb +++ b/lib/bson/version.rb @@ -14,5 +14,5 @@ # limitations under the License. module BSON - VERSION = "4.15.0" + VERSION = "4.15.1" end