Skip to content

Validate nanoseconds range when unpacking timestamps in the C extension#716

Open
chuenchen309 wants to merge 1 commit into
msgpack:mainfrom
chuenchen309:fix/unpack-timestamp-nanoseconds-range
Open

Validate nanoseconds range when unpacking timestamps in the C extension#716
chuenchen309 wants to merge 1 commit into
msgpack:mainfrom
chuenchen309:fix/unpack-timestamp-nanoseconds-range

Conversation

@chuenchen309

Copy link
Copy Markdown

The C extension's unpack_timestamp() decodes the nanoseconds field of a timestamp64/timestamp96 but never range-checks it, so with timestamp=1|2|3 it silently accepts a malformed value ≥ 1,000,000,000:

>>> import msgpack, struct
>>> b = b"\xd7\xff" + struct.pack("!Q", (10**9 << 34))  # timestamp64, ns = 1e9
>>> msgpack.unpackb(b, timestamp=1)   # C ext, float mode
1.0                                    # accepted, time-shifted ~1s
>>> msgpack.unpackb(b, timestamp=0)   # C ext, default Timestamp mode
ValueError: ...                        # correctly rejected

Three signals agree this is a bug, not lenient parsing:

  • The wire spec: "In timestamp 64 and timestamp 96 formats, nanoseconds must not be larger than 999999999."
  • The pure-Python fallback (Timestamp.__init__) already rejects it in every mode.
  • The C extension itself rejects the identical bytes at timestamp=0 (which routes through Timestamp); only the 1/2/3 fast paths skip validation.

The fix adds the range check inside unpack_timestamp() so all modes reject consistently, matching the fallback and the spec. Valid nanoseconds (≤ 999999999) are unaffected. Added a regression test covering every timestamp= mode; full suite passes.


Disclosure: this PR was authored by an AI coding agent (Claude Code) running on this account — it found the C-vs-fallback divergence, reproduced it, wrote the fix and the test, and wrote this description. The account holder reviews every change and is accountable for it, and the verification above is re-runnable from the diff. Happy to close it if it isn't the kind of contribution you want.

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>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a correctness gap in the C extension’s timestamp unpacking by validating that the decoded nanoseconds field is within the MessagePack timestamp spec range, ensuring consistent behavior across all timestamp= modes and matching the pure-Python fallback.

Changes:

  • Add a nanoseconds upper-bound check (<= 999,999,999) in the C extension’s unpack_timestamp() fast path.
  • Add a regression test asserting out-of-range nanoseconds are rejected for timestamp=0/1/2/3.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
msgpack/unpack.h Adds nanoseconds range validation in unpack_timestamp() so malformed timestamp64/96 payloads error consistently across modes.
test/test_timestamp.py Adds a regression test ensuring out-of-range nanoseconds are rejected for all timestamp= modes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread msgpack/unpack.h
return -1;
}
if (ts->tv_nsec > 999999999) {
PyErr_Format(PyExc_ValueError, "nanoseconds must be a non-negative integer less than 999999999.");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants