Skip to content

Fix division by zero when packing coarse or sub-nanosecond time_points - #1193

Merged
redboltz merged 1 commit into
msgpack:cpp_masterfrom
wleeaf:chrono-pack-coarse-duration
Oct 26, 2026
Merged

redboltz merged 1 commit into
msgpack:cpp_masterfrom
wleeaf:chrono-pack-coarse-duration

Conversation

@wleeaf

@wleeaf wleeaf commented Oct 25, 2026

Copy link
Copy Markdown

Packing a std::chrono::time_point whose period is longer than one second crashes with SIGFPE:

std::chrono::time_point<std::chrono::system_clock, std::chrono::minutes> tp(std::chrono::minutes(2));
msgpack::sbuffer sbuf;
msgpack::pack(sbuf, tp); // Floating point exception

The same applies to std::chrono::hours and C++20 std::chrono::sys_days, and to msgpack::object(tp, zone).

The pack and object_with_zone adaptors in v1/adaptor/cpp11/chrono.hpp compute the nanosecond part as count % (1000000000 / nano_num) * nano_num, where nano_num = num * (1000000000 / den). For a period longer than one second, 1000000000 / nano_num is 0. For a period shorter than a nanosecond (std::pico), nano_num itself is 0. Either way the code divides by zero.

This change splits the time since the epoch into whole seconds and the nanosecond remainder with duration_cast, keeping the existing adjustment for negative remainders. The encoded bytes for the periods that already worked are unchanged (checked for seconds, milliseconds, and system_clock::time_point min/max).

Tests: new system_clock_minutes, system_clock_minutes_negative and system_clock_picoseconds cases in msgpack_cpp11.cpp, plus system_clock_minutes and system_clock_minutes_negative in object_with_zone.cpp. Each new case hits the division by zero (UBSan "division by zero" at chrono.hpp:254/305) without the fix and passes with it.

The pack and object_with_zone adaptors for std::chrono::time_point
computed the nanosecond part as
count % (1000000000 / nano_num) * nano_num. When the period is longer
than one second (std::chrono::minutes, hours, C++20 sys_days),
1000000000 / nano_num is 0; when it is shorter than a nanosecond
(std::pico), nano_num itself is 0. Either way packing divided by zero.

Split the time since the epoch into whole seconds and the nanosecond
remainder with duration_cast instead. Results for the periods that
already worked are unchanged.
@wleeaf
wleeaf force-pushed the chrono-pack-coarse-duration branch from 6c520e9 to 2b78583 Compare October 25, 2026 13:14
@redboltz

Copy link
Copy Markdown
Contributor

@wleeaf , thank you for sending the PR. It looks good to me.

@redboltz
redboltz merged commit b2cd08c into msgpack:cpp_master Oct 26, 2026
24 of 25 checks passed
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