Skip to content

Commit 5e57548

Browse files
authored
Merge pull request #391 from msgpack/map-size-wrap
Handle integer overflow when parsing maps.
2 parents 09c914d + 43b5931 commit 5e57548

5 files changed

Lines changed: 65 additions & 20 deletions

File tree

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ jobs:
5353
fail-fast: false
5454
matrix:
5555
os: [ubuntu]
56-
ruby: ['jruby-9.3', 'jruby-9.4', 'truffleruby']
56+
ruby: ['jruby-9.4', 'truffleruby']
5757
runs-on: ${{ matrix.os }}-latest
5858
steps:
5959
- uses: actions/checkout@v4

ChangeLog

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
* Fix an integer overflow when parsing maps.
2+
13
2026-06-09 1.8.2
24

35
* Fix `Buffer#clear` to properly reset memory chunks before adding them back to the pool.

ext/java/org/msgpack/jruby/Decoder.java

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@ public Decoder(Ruby runtime, Unpacker unpacker, byte[] bytes, int offset, int le
8585
feed(bytes, offset, length);
8686
}
8787

88+
private int getUnsignedInt() {
89+
int size = buffer.getInt();
90+
if (size < 0) {
91+
throw runtime.newRaiseException(underflowErrorClass, "Size too large (limited to 2**31 for the Java version)");
92+
}
93+
return size;
94+
}
95+
8896
public void feed(byte[] bytes) {
8997
feed(bytes, 0, bytes.length);
9098
}
@@ -200,7 +208,7 @@ public IRubyObject read_array_header() {
200208
} else if (b == ARY16) {
201209
return runtime.newFixnum(buffer.getShort() & 0xffff);
202210
} else if (b == ARY32) {
203-
return runtime.newFixnum(buffer.getInt());
211+
return runtime.newFixnum(getUnsignedInt());
204212
}
205213
throw runtime.newRaiseException(unexpectedTypeErrorClass, "unexpected type");
206214
} catch (RaiseException re) {
@@ -221,7 +229,7 @@ public IRubyObject read_map_header() {
221229
} else if (b == MAP16) {
222230
return runtime.newFixnum(buffer.getShort() & 0xffff);
223231
} else if (b == MAP32) {
224-
return runtime.newFixnum(buffer.getInt());
232+
return runtime.newFixnum(getUnsignedInt());
225233
}
226234
throw runtime.newRaiseException(unexpectedTypeErrorClass, "unexpected type");
227235
} catch (RaiseException re) {
@@ -258,10 +266,10 @@ private IRubyObject consumeNext() {
258266
case TRUE: return runtime.getTrue();
259267
case BIN8: return consumeString(buffer.get() & 0xff, binaryEncoding);
260268
case BIN16: return consumeString(buffer.getShort() & 0xffff, binaryEncoding);
261-
case BIN32: return consumeString(buffer.getInt(), binaryEncoding);
269+
case BIN32: return consumeString(getUnsignedInt(), binaryEncoding);
262270
case VAREXT8: return consumeExtension(buffer.get() & 0xff);
263271
case VAREXT16: return consumeExtension(buffer.getShort() & 0xffff);
264-
case VAREXT32: return consumeExtension(buffer.getInt());
272+
case VAREXT32: return consumeExtension(getUnsignedInt());
265273
case FLOAT32: return runtime.newFloat(buffer.getFloat());
266274
case FLOAT64: return runtime.newFloat(buffer.getDouble());
267275
case UINT8: return runtime.newFixnum(buffer.get() & 0xffL);
@@ -283,11 +291,11 @@ private IRubyObject consumeNext() {
283291
case FIXEXT16: return consumeExtension(16);
284292
case STR8: return consumeString(buffer.get() & 0xff, utf8Encoding);
285293
case STR16: return consumeString(buffer.getShort() & 0xffff, utf8Encoding);
286-
case STR32: return consumeString(buffer.getInt(), utf8Encoding);
294+
case STR32: return consumeString(getUnsignedInt(), utf8Encoding);
287295
case ARY16: return consumeArray(buffer.getShort() & 0xffff);
288-
case ARY32: return consumeArray(buffer.getInt());
296+
case ARY32: return consumeArray(getUnsignedInt());
289297
case MAP16: return consumeHash(buffer.getShort() & 0xffff);
290-
case MAP32: return consumeHash(buffer.getInt());
298+
case MAP32: return consumeHash(getUnsignedInt());
291299
default: break outer;
292300
}
293301
case 0xe:

ext/msgpack/unpacker.c

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -456,13 +456,13 @@ static int read_primitive(msgpack_unpacker_t* uk)
456456
return object_complete(uk, INT2NUM((int8_t)b));
457457

458458
SWITCH_RANGE(b, 0xa0, 0xbf) // FixRaw / fixstr
459-
int count = b & 0x1f;
459+
size_t count = b & 0x1f;
460460
/* read_raw_body_begin sets uk->reading_raw */
461461
uk->reading_raw_remaining = count;
462462
return read_raw_body_begin(uk, RAW_TYPE_STRING);
463463

464464
SWITCH_RANGE(b, 0x90, 0x9f) // FixArray
465-
int count = b & 0x0f;
465+
size_t count = b & 0x0f;
466466
if(count == 0) {
467467
return object_complete(uk, rb_ary_new());
468468
}
@@ -638,7 +638,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
638638
case 0xd9: // raw 8 / str 8
639639
{
640640
READ_CAST_BLOCK_OR_RETURN_EOF(cb, uk, 1);
641-
uint8_t count = cb.u8;
641+
size_t count = cb.u8;
642642
/* read_raw_body_begin sets uk->reading_raw */
643643
uk->reading_raw_remaining = count;
644644
return read_raw_body_begin(uk, RAW_TYPE_STRING);
@@ -647,7 +647,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
647647
case 0xda: // raw 16 / str 16
648648
{
649649
READ_CAST_BLOCK_OR_RETURN_EOF(cb, uk, 2);
650-
uint16_t count = _msgpack_be16(cb.u16);
650+
size_t count = _msgpack_be16(cb.u16);
651651
/* read_raw_body_begin sets uk->reading_raw */
652652
uk->reading_raw_remaining = count;
653653
return read_raw_body_begin(uk, RAW_TYPE_STRING);
@@ -656,7 +656,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
656656
case 0xdb: // raw 32 / str 32
657657
{
658658
READ_CAST_BLOCK_OR_RETURN_EOF(cb, uk, 4);
659-
uint32_t count = _msgpack_be32(cb.u32);
659+
size_t count = _msgpack_be32(cb.u32);
660660
/* read_raw_body_begin sets uk->reading_raw */
661661
uk->reading_raw_remaining = count;
662662
return read_raw_body_begin(uk, RAW_TYPE_STRING);
@@ -665,7 +665,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
665665
case 0xc4: // bin 8
666666
{
667667
READ_CAST_BLOCK_OR_RETURN_EOF(cb, uk, 1);
668-
uint8_t count = cb.u8;
668+
size_t count = cb.u8;
669669
/* read_raw_body_begin sets uk->reading_raw */
670670
uk->reading_raw_remaining = count;
671671
return read_raw_body_begin(uk, RAW_TYPE_BINARY);
@@ -674,7 +674,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
674674
case 0xc5: // bin 16
675675
{
676676
READ_CAST_BLOCK_OR_RETURN_EOF(cb, uk, 2);
677-
uint16_t count = _msgpack_be16(cb.u16);
677+
size_t count = _msgpack_be16(cb.u16);
678678
/* read_raw_body_begin sets uk->reading_raw */
679679
uk->reading_raw_remaining = count;
680680
return read_raw_body_begin(uk, RAW_TYPE_BINARY);
@@ -683,7 +683,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
683683
case 0xc6: // bin 32
684684
{
685685
READ_CAST_BLOCK_OR_RETURN_EOF(cb, uk, 4);
686-
uint32_t count = _msgpack_be32(cb.u32);
686+
size_t count = _msgpack_be32(cb.u32);
687687
/* read_raw_body_begin sets uk->reading_raw */
688688
uk->reading_raw_remaining = count;
689689
return read_raw_body_begin(uk, RAW_TYPE_BINARY);
@@ -692,7 +692,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
692692
case 0xdc: // array 16
693693
{
694694
READ_CAST_BLOCK_OR_RETURN_EOF(cb, uk, 2);
695-
uint16_t count = _msgpack_be16(cb.u16);
695+
size_t count = _msgpack_be16(cb.u16);
696696
if(count == 0) {
697697
return object_complete(uk, rb_ary_new());
698698
}
@@ -702,7 +702,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
702702
case 0xdd: // array 32
703703
{
704704
READ_CAST_BLOCK_OR_RETURN_EOF(cb, uk, 4);
705-
uint32_t count = _msgpack_be32(cb.u32);
705+
size_t count = _msgpack_be32(cb.u32);
706706
if(count == 0) {
707707
return object_complete(uk, rb_ary_new());
708708
}
@@ -712,7 +712,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
712712
case 0xde: // map 16
713713
{
714714
READ_CAST_BLOCK_OR_RETURN_EOF(cb, uk, 2);
715-
uint16_t count = _msgpack_be16(cb.u16);
715+
size_t count = _msgpack_be16(cb.u16);
716716
if(count == 0) {
717717
return object_complete(uk, rb_hash_new());
718718
}
@@ -722,7 +722,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
722722
case 0xdf: // map 32
723723
{
724724
READ_CAST_BLOCK_OR_RETURN_EOF(cb, uk, 4);
725-
uint32_t count = _msgpack_be32(cb.u32);
725+
size_t count = _msgpack_be32(cb.u32);
726726
if(count == 0) {
727727
return object_complete(uk, rb_hash_new());
728728
}

spec/unpack_spec.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
end
88

99
describe MessagePack do
10+
let(:eof_error) do
11+
IS_JRUBY ? MessagePack::UnderflowError : EOFError
12+
end
13+
1014
it 'MessagePack.unpack symbolize_keys' do
1115
symbolized_hash = {:a => 'b', :c => 'd'}
1216
MessagePack.load(MessagePack.pack(symbolized_hash), :symbolize_keys => true).should == symbolized_hash
@@ -54,4 +58,35 @@
5458
MessagePack.unpack([0xc6, 0x00, 0x00, 0x00, 0x01].pack('C*') + 'a').should == "a"
5559
MessagePack.unpack([0xc6, 0x00, 0x00, 0x00, 0x02].pack('C*') + 'aa').should == "aa"
5660
end
61+
62+
it "msgpack fixmap type" do
63+
MessagePack.unpack([0x81, 0xa1, 0x61, 0x01].pack('C*')).should == {"a" => 1}
64+
expect {
65+
MessagePack.unpack([0x82, 0xa1, 0x61, 0x01].pack('C*'))
66+
}.to raise_error(eof_error)
67+
end
68+
69+
it "msgpack map 16 type" do
70+
MessagePack.unpack([0xde, 0x00, 0x01, 0xa1, 0x61, 0x1].pack('C*')).should == {"a" => 1}
71+
72+
expect {
73+
MessagePack.unpack([0xde, 0x00, 0x02, 0xa1, 0x61, 0x1].pack('C*'))
74+
}.to raise_error(eof_error)
75+
76+
expect {
77+
MessagePack.unpack([0xde, 0x80, 0x01, 0xa1, 0x61, 0x1].pack('C*'))
78+
}.to raise_error(eof_error)
79+
end
80+
81+
it "msgpack map 32 type" do
82+
MessagePack.unpack([0xdf, 0x00, 0x00, 0x00, 0x01, 0xa1, 0x61, 0x1].pack('C*')).should == {"a" => 1}
83+
84+
expect {
85+
MessagePack.unpack([0xdf, 0x00, 0x00, 0x00, 0x02, 0xa1, 0x61, 0x1].pack('C*'))
86+
}.to raise_error(eof_error)
87+
88+
expect {
89+
p MessagePack.unpack([0xdf, 0x80, 0x00, 0x00, 0x01, 0xa1, 0x61, 0x1].pack('C*'))
90+
}.to raise_error(eof_error)
91+
end
5792
end

0 commit comments

Comments
 (0)