Skip to content

Avoid panics for full-width monotonic increments - #140

Open
perfloop[bot] wants to merge 3 commits into
mainfrom
perfloop-pr-open-b8g6nqv7mm
Open

perfloop[bot] wants to merge 3 commits into
mainfrom
perfloop-pr-open-b8g6nqv7mm

Conversation

@perfloop

@perfloop perfloop Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Monotonic accepts a uint64 increment bound. With a math/rand source and inc > MaxInt64, a later public New call at the same timestamp can panic if prior entropy is nonzero: the signed fast path converts the bound before Int63n. When prior entropy is zero, MonotonicRead reads fresh entropy instead. The constructor now keeps Int63n for bounds through MaxInt64 and uses the existing byte sampler for larger bounds. inc=0 still selects MaxUint32; no public limit or API is added.

uint80.Add now uses bits.Add64. On carry, it checks u.Hi against math.MaxUint16; it increments the high word only when the carry fits and returns before writing either word if the full 80-bit value would overflow. An overflowing addition returns ErrMonotonicOverflow and leaves the stored entropy at its last valid value instead of wrapping lower; a later increment can succeed if it fits. The Unreleased changelog records the fix.

TestMonotonicFullWidthIncrement calls public New and uses math/big to verify unsigned bounds at MaxInt64, MaxInt64+1, and MaxUint64, a low-word carry, repeated overflow, and a high-bound reader error. It reproduces the two panic bounds and a lower-entropy retry failure on base. The intermediate that changed only the signed-bound path removes the panics but still fails the retry; the previous PR revision and this revision pass. TestMonotonic exercises the inc=0 and inc=1 controls, and TestMonotonicOverflow guards the default inc=0 overflow error. Both pass on base, the signed-bound-only intermediate, the previous PR revision, and this revision.

The Linux race-enabled suite, go vet, gofmt, and Staticcheck passed. The macOS and Windows race-test jobs were not run because this environment is Linux-only; they remain target-CI work.

This condition holds with this change and does not hold at 75921a7395850ea47ed4dfd832184a5a294c8fde: Public New with a nonzero initialized Monotonic entropy state and the same timestamp returns an ID whose unsigned 80-bit entropy increment is between 1 and the effective bound for math/rand-backed inc values 0, 1, MaxInt64, MaxInt64+1, and MaxUint64; with inc=1 at a non-overflowing low-word carry the increment is 1; a positive increment from MaxUint80 returns ErrMonotonicOverflow, and the next same-timestamp attempt also returns ErrMonotonicOverflow instead of succeeding with lower entropy; a high-bound entropy-read error is returned by New..

The comparison harness used for the correctness assertion is not included in the pull request. Production frequency for high custom bounds is unknown; this repairs a supported-input boundary, not a measured incident.

@peterbourgon, please review.


Generated by Perfloop.

Signed-off-by: Perfloop Agent <agent@perfloop.ai>

@peterbourgon peterbourgon left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think easier?

Comment thread ulid.go Resolved
Signed-off-by: Perfloop Agent <agent@perfloop.ai>
@perfloop

perfloop Bot commented Oct 27, 2026

Copy link
Copy Markdown
Author

PR head: 93dc68b1e79dc55cc1160b574d10a6166875bbc4. Response to this feedback.

I kept the existing Add path rather than applying the shorter update: it updates the high word only on carry and restores the low word when 80-bit overflow is detected. TestMonotonicFullWidthIncrement covers the same-timestamp retry returning ErrMonotonicOverflow.

Signed-off-by: Perfloop Agent <agent@perfloop.ai>
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