Skip to content

Commit 36fd127

Browse files
chuenchen309claude
andcommitted
Validate nanoseconds range when unpacking timestamps in the C extension
unpack_timestamp() decoded the nanoseconds field of a timestamp64/96 but never range-checked it, so with timestamp=1|2|3 the C extension silently accepted a malformed value >= 1e9, while the pure-Python fallback and the C extension's own timestamp=0 path both reject it. The MessagePack spec states nanoseconds must not be larger than 999999999. Add the range check in unpack_timestamp() so all modes reject consistently. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent c6b4a48 commit 36fd127

2 files changed

Lines changed: 16 additions & 2 deletions

File tree

msgpack/unpack.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,16 +271,21 @@ static int unpack_timestamp(const char* buf, unsigned int buflen, msgpack_timest
271271
uint64_t value =_msgpack_load64(uint64_t, buf);
272272
ts->tv_nsec = (uint32_t)(value >> 34);
273273
ts->tv_sec = value & 0x00000003ffffffffLL;
274-
return 0;
274+
break;
275275
}
276276
case 12:
277277
ts->tv_nsec = _msgpack_load32(uint32_t, buf);
278278
ts->tv_sec = _msgpack_load64(int64_t, buf + 4);
279-
return 0;
279+
break;
280280
default:
281281
PyErr_Format(PyExc_ValueError, "invalid timestamp data (length %u)", buflen);
282282
return -1;
283283
}
284+
if (ts->tv_nsec > 999999999) {
285+
PyErr_Format(PyExc_ValueError, "nanoseconds must be a non-negative integer less than 999999999.");
286+
return -1;
287+
}
288+
return 0;
284289
}
285290

286291
#include "datetime.h"

test/test_timestamp.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,15 @@ def test_unpack_timestamp():
7171
msgpack.unpackb(b"\xc7\x05\xff\0\0\0\0\0") # ext8 (len=5)
7272

7373

74+
def test_unpack_timestamp_out_of_range_nanoseconds():
75+
# An out-of-range nanoseconds field must be rejected in every timestamp=
76+
# mode (spec: nanoseconds must not exceed 999999999), not only the default.
77+
for data in (b"\xd7\xff" + b"\xff" * 8, b"\xc7\x0c\xff" + b"\xff" * 12):
78+
for mode in (0, 1, 2, 3):
79+
with pytest.raises(ValueError):
80+
msgpack.unpackb(data, timestamp=mode)
81+
82+
7483
def test_timestamp_from():
7584
t = Timestamp(42, 14000)
7685
assert Timestamp.from_unix(42.000014) == t

0 commit comments

Comments
 (0)