From b94a7571a02eb7b7c9556493d027daaf992515d0 Mon Sep 17 00:00:00 2001 From: Dmitry Rybakov Date: Wed, 6 May 2026 17:04:29 +0200 Subject: [PATCH] RUBY-3869 Validate Binary subtype 0x02 outer/inner length on decode Cross-check that the inner length equals outer_length - 4, and reject negative outer/inner lengths up front. Both raise BSON::Error::BSONDecodeError with explicit messages. Without the check, malformed payloads were caught only by incidental side effects (next-field-type misalignment or get_bytes overrun in the C extension). Matches the behavior already implemented in the Python driver (bson/__init__.py::_get_binary). --- lib/bson/binary.rb | 15 +++++++++++- spec/bson/binary_spec.rb | 52 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/lib/bson/binary.rb b/lib/bson/binary.rb index e3fc78c27..74d84ffc8 100644 --- a/lib/bson/binary.rb +++ b/lib/bson/binary.rb @@ -306,7 +306,20 @@ def self.from_bson(buffer, **_options) type = type_byte end - length = buffer.get_int32 if type == :old + if type == :old + inner_length = buffer.get_int32 + unless inner_length == length - 4 + raise Error::BSONDecodeError, + "BSON binary subtype 0x02 length mismatch: outer=#{length}, inner=#{inner_length}" + end + length = inner_length + end + + if length.negative? + raise Error::BSONDecodeError, + "BSON binary length is negative: #{length}" + end + data = buffer.get_bytes(length) new(data, type) end diff --git a/spec/bson/binary_spec.rb b/spec/bson/binary_spec.rb index ded26f87d..15d5da05a 100644 --- a/spec/bson/binary_spec.rb +++ b/spec/bson/binary_spec.rb @@ -204,6 +204,58 @@ /BSON data contains unsupported binary subtype 0x10/) end end + + context 'when subtype 0x02 inner length is too long' do + let(:bson) do + ([6].pack('l<') + 2.chr + [3].pack('l<') + 'xxx').force_encoding('BINARY') + end + + it 'raises BSONDecodeError' do + expect { obj }.to raise_error( + BSON::Error::BSONDecodeError, + /length mismatch.*outer=6.*inner=3/ + ) + end + end + + context 'when subtype 0x02 inner length is too short' do + let(:bson) do + ([6].pack('l<') + 2.chr + [1].pack('l<') + 'xxx').force_encoding('BINARY') + end + + it 'raises BSONDecodeError' do + expect { obj }.to raise_error( + BSON::Error::BSONDecodeError, + /length mismatch.*outer=6.*inner=1/ + ) + end + end + + context 'when subtype 0x02 inner length is negative' do + let(:bson) do + ([6].pack('l<') + 2.chr + [-1].pack('l<') + 'xx').force_encoding('BINARY') + end + + it 'raises BSONDecodeError' do + expect { obj }.to raise_error( + BSON::Error::BSONDecodeError, + /length mismatch.*outer=6.*inner=-1/ + ) + end + end + + context 'when non-old subtype outer length is negative' do + let(:bson) do + ([-1].pack('l<') + 0.chr).force_encoding('BINARY') + end + + it 'raises BSONDecodeError' do + expect { obj }.to raise_error( + BSON::Error::BSONDecodeError, + /length is negative.*-1/ + ) + end + end end describe "#to_bson/#from_bson" do