Skip to content

Commit cf8d95e

Browse files
committed
fix(msgpack): enhance validation and fix bugs
- Separate max_string_length and max_bin_length limits - Add validation for binary and extension data lengths - Fix BufferStream bounds checking in seekTo method - Fix array packing error type (ArrayLengthTooLong) - Pre-allocate map capacity to prevent allocation issues - Update documentation with clarified error types
1 parent 7201732 commit cf8d95e

File tree

4 files changed

+54
-7
lines changed

4 files changed

+54
-7
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ const StrictPacker = msgpack.PackWithLimits(
190190
.max_array_length = 10_000, // Max 10K array elements
191191
.max_map_size = 10_000, // Max 10K map pairs
192192
.max_string_length = 1024 * 1024, // Max 1MB strings
193+
.max_bin_length = 1024 * 1024, // Max 1MB binary blobs
194+
.max_ext_length = 512 * 1024, // Max 512KB extension data
193195
},
194196
);
195197
```
@@ -207,7 +209,9 @@ Possible security errors:
207209
msgpack.MsgPackError.MaxDepthExceeded // Nesting too deep
208210
msgpack.MsgPackError.ArrayTooLarge // Array claims too many elements
209211
msgpack.MsgPackError.MapTooLarge // Map claims too many pairs
210-
msgpack.MsgPackError.StringTooLong // String/binary data too large
212+
msgpack.MsgPackError.StringTooLong // String data too large
213+
msgpack.MsgPackError.BinDataLengthTooLong // Binary blob too large
214+
msgpack.MsgPackError.ExtDataTooLarge // Extension payload too large
211215
```
212216
213217
## API Overview

README_CN.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ const StrictPacker = msgpack.PackWithLimits(
190190
.max_array_length = 10_000, // 最大 1 万个数组元素
191191
.max_map_size = 10_000, // 最大 1 万个 map 键值对
192192
.max_string_length = 1024 * 1024, // 最大 1MB 字符串
193+
.max_bin_length = 1024 * 1024, // 最大 1MB 二进制数据
194+
.max_ext_length = 512 * 1024, // 最大 512KB 扩展类型数据
193195
},
194196
);
195197
```
@@ -207,7 +209,9 @@ const StrictPacker = msgpack.PackWithLimits(
207209
msgpack.MsgPackError.MaxDepthExceeded // 嵌套过深
208210
msgpack.MsgPackError.ArrayTooLarge // 数组声称过多元素
209211
msgpack.MsgPackError.MapTooLarge // Map 声称过多键值对
210-
msgpack.MsgPackError.StringTooLong // 字符串/二进制数据过大
212+
msgpack.MsgPackError.StringTooLong // 字符串过长
213+
msgpack.MsgPackError.BinDataLengthTooLong // 二进制数据过大
214+
msgpack.MsgPackError.ExtDataTooLarge // 扩展类型数据过大
211215
```
212216

213217
## API 概览

src/compat.zig

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ pub const BufferStream = if (current_zig.minor >= 16) struct {
4545
}
4646

4747
pub fn seekTo(self: *Self, pos: usize) !void {
48+
if (pos > self.buffer.len) {
49+
return error.OutOfBounds;
50+
}
4851
self.pos = pos;
4952
}
5053

@@ -53,7 +56,7 @@ pub const BufferStream = if (current_zig.minor >= 16) struct {
5356
}
5457

5558
pub fn getEndPos(self: Self) usize {
56-
return self.pos;
59+
return self.buffer.len;
5760
}
5861
} else std.io.FixedBufferStream([]u8);
5962

src/msgpack.zig

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,12 @@ pub const ParseLimits = struct {
103103
/// Maximum map size (default 1 million key-value pairs)
104104
max_map_size: usize = 1_000_000,
105105

106-
/// Maximum string/binary data length (default 100MB)
106+
/// Maximum string data length (default 100MB)
107107
max_string_length: usize = 100 * 1024 * 1024,
108108

109+
/// Maximum binary data length (default 100MB)
110+
max_bin_length: usize = 100 * 1024 * 1024,
111+
109112
/// Maximum extension data length (default 100MB)
110113
max_ext_length: usize = 100 * 1024 * 1024,
111114
};
@@ -1153,7 +1156,7 @@ pub fn PackWithLimits(
11531156
try self.writeTypeMarker(.ARRAY32);
11541157
try self.writeU32Value(@as(u32, @intCast(len)));
11551158
} else {
1156-
return MsgPackError.MapLengthTooLong;
1159+
return MsgPackError.ArrayLengthTooLong;
11571160
}
11581161
for (arr) |val| {
11591162
try self.write(val);
@@ -1509,22 +1512,31 @@ pub fn PackWithLimits(
15091512
}
15101513
}
15111514

1515+
inline fn validateBinLength(len: usize) !void {
1516+
if (len > parse_limits.max_bin_length) {
1517+
return MsgPackError.BinDataLengthTooLong;
1518+
}
1519+
}
1520+
15121521
fn readBin8Value(self: Self, allocator: Allocator) ![]u8 {
15131522
const len = try self.readV8Value();
1523+
try validateBinLength(len);
15141524
const bin = try self.readData(allocator, len);
15151525

15161526
return bin;
15171527
}
15181528

15191529
fn readBin16Value(self: Self, allocator: Allocator) ![]u8 {
15201530
const len = try self.readU16Value();
1531+
try validateBinLength(len);
15211532
const bin = try self.readData(allocator, len);
15221533

15231534
return bin;
15241535
}
15251536

15261537
fn readBin32Value(self: Self, allocator: Allocator) ![]u8 {
15271538
const len = try self.readU32Value();
1539+
try validateBinLength(len);
15281540
const bin = try self.readData(allocator, len);
15291541

15301542
return bin;
@@ -1545,7 +1557,14 @@ pub fn PackWithLimits(
15451557
}
15461558
}
15471559

1560+
inline fn validateExtLength(len: usize) !void {
1561+
if (len > parse_limits.max_ext_length) {
1562+
return MsgPackError.ExtDataTooLarge;
1563+
}
1564+
}
1565+
15481566
inline fn readExtData(self: Self, allocator: Allocator, len: usize) !EXT {
1567+
try validateExtLength(len);
15491568
const ext_type = try self.readI8Value();
15501569
const data = try self.readData(allocator, len);
15511570
return EXT{
@@ -1582,6 +1601,7 @@ pub fn PackWithLimits(
15821601

15831602
/// Read non-timestamp EXT data
15841603
inline fn readRegularExt(self: Self, ext_type: i8, len: usize, allocator: Allocator) !Payload {
1604+
try validateExtLength(len);
15851605
const ext_data = try allocator.alloc(u8, len);
15861606
errdefer allocator.free(ext_data);
15871607
_ = try self.readFrom(ext_data);
@@ -1612,6 +1632,13 @@ pub fn PackWithLimits(
16121632

16131633
/// Read timestamp payload based on marker
16141634
inline fn readTimestampPayload(self: Self, marker: Markers) !Payload {
1635+
const required_len: usize = switch (marker) {
1636+
.FIXEXT4 => FIXEXT4_LEN,
1637+
.FIXEXT8 => FIXEXT8_LEN,
1638+
.EXT8 => TIMESTAMP96_DATA_LEN,
1639+
else => unreachable,
1640+
};
1641+
try validateExtLength(required_len);
16151642
const timestamp: Timestamp = switch (marker) {
16161643
.FIXEXT4 => try self.readTimestamp32(),
16171644
.FIXEXT8 => try self.readTimestamp64(),
@@ -1844,7 +1871,7 @@ pub fn PackWithLimits(
18441871
const val = try self.readBinValue(marker, allocator);
18451872

18461873
// Validate binary length
1847-
if (val.len > parse_limits.max_string_length) {
1874+
if (val.len > parse_limits.max_bin_length) {
18481875
allocator.free(val);
18491876
cleanupParseStack(&parse_stack, allocator);
18501877
return MsgPackError.BinDataLengthTooLong;
@@ -1901,7 +1928,15 @@ pub fn PackWithLimits(
19011928
current_payload = Payload{ .map = Map.init(allocator) };
19021929
} else {
19031930
// Initialize map
1904-
const map = Map.init(allocator);
1931+
var map = Map.init(allocator);
1932+
var map_owned = false;
1933+
errdefer if (!map_owned) map.deinit();
1934+
1935+
const capacity = std.math.cast(u32, len) orelse {
1936+
cleanupParseStack(&parse_stack, allocator);
1937+
return MsgPackError.MapTooLarge;
1938+
};
1939+
try map.ensureTotalCapacity(capacity);
19051940

19061941
// Push to stack
19071942
try appendToStack(&parse_stack, allocator, .{
@@ -1912,6 +1947,7 @@ pub fn PackWithLimits(
19121947
.remaining_pairs = len,
19131948
} },
19141949
});
1950+
map_owned = true;
19151951

19161952
needs_parent_fill = false;
19171953
continue; // Continue to read first key

0 commit comments

Comments
 (0)