Uuid::new_v7 can panic or wrap on large Timestamp values because secs * 1000 is not saturating #868
Labels
No labels
breaking
bug
dependencies
good first issue
help wanted
unstable
No milestone
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
gh-uuid-rs/uuid#868
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Uuid::new_v7currently converts aTimestampfrom(seconds, subsec_nanos)to Unix milliseconds like this:The
saturating_addhappens after a plainu64multiplication. For sufficiently large publicTimestampinputs,secs * 1000overflows first.Because
Timestamp::from_unix_timeis a public safe API and accepts arbitraryu64seconds, this is reachable withoutunsafe.Reproduction
Using
uuid = { version = "1.22.0", features = ["v7"] }:Debug build
This panics with an overflow in
Uuid::new_v7.Release build
This does not panic, but the multiplication wraps before encoding, so the UUID gets a wrapped/truncated millisecond timestamp that is unrelated to the input timestamp.
Impact
This is a correctness bug in a public safe API:
Timestamp::from_unix_time,Even though these are extreme timestamp values, the API currently accepts them and the implementation already suggests it intends to avoid overflow via
saturating_add.Suggested fix
A minimal fix is to make the multiplication saturating too:
A more complete fix would also clamp
millisto the maximum value representable by UUIDv7's 48-bit millisecond timestamp field before passing it toBuilder::from_unix_timestamp_millis, e.g.:That would avoid both the immediate overflow and a later mismatch between an oversized
u64millisecond value and the 48-bit v7 timestamp encoding.Thanks @meng-xu-cs, we should be using a saturating multiplication there. I’m not sure if we need to further clamp the value, since we shift it into position in the resulting UUID, so if there’s work we can safely avoid doing then I think we should.