Skip to content

fix(printf): compute asterisk width magnitude in unsigned arithmetic - #13800

Open
bolverk wants to merge 1 commit into
uutils:mainfrom
bolverk:fix/printf-asterisk-width-i64-min-overflow
Open

fix(printf): compute asterisk width magnitude in unsigned arithmetic#13800
bolverk wants to merge 1 commit into
uutils:mainfrom
bolverk:fix/printf-asterisk-width-i64-min-overflow

Conversation

@bolverk

@bolverk bolverk commented Aug 8, 2026

Copy link
Copy Markdown

Fixes #13766

Summary

A negative * field-width argument of i64::MIN (printf '%*d' -9223372036854775808 1) made resolve_asterisk_width panic with attempt to negate with overflow: the old code computed the magnitude as -(nb as isize), and |i64::MIN| = 2^63 is not representable in i64/isize.

The fix computes the magnitude with nb.unsigned_abs(), which is designed for exactly this case and returns 2^63 in unsigned (u64) arithmetic.

Behavior

  • Before: panic / exit 134 (attempt to negate with overflow)
  • After: the width 2^63 exceeds MAX_FORMAT_WIDTH, so printf fails gracefully with printf: write error: formatting width too large (exit 1), matching the handling of other oversized widths.

Verification

Formal verification of the width-resolution arithmetic was done in Dafny (4.11): it specifies the intended behavior (negative width ⇒ left-align with magnitude), proves |i64::MIN| overflows i64 but fits u64/usize, and proves the unsigned_abs()-based implementation is overflow-free and matches the spec. Proof: 15 verified, 0 errors.

Tests

  • New unit test resolve_asterisk_width::asterisk_i64_min_width (both SignedInt and Unparsed argument forms)
  • New integration test test_printf::test_asterisk_width_i64_min_no_panic
  • Full printf suite passes: 131 passed, 0 failed, 1 ignored; cargo fmt and clippy clean.

@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

GNU testsuite comparison:

Skip an intermittent issue tests/date/date-locale-hour (fails in this run but passes in the 'main' branch)
Skipping an intermittent issue tests/tail/symlink (passes in this run but fails in the 'main' branch)
Note: The gnu test tests/misc/write-errors was skipped on 'main' but is now failing.

@bolverk
bolverk force-pushed the fix/printf-asterisk-width-i64-min-overflow branch from 8728f09 to 2d76eba Compare August 8, 2026 07:14
@bolverk

bolverk commented Aug 8, 2026

Copy link
Copy Markdown
Author

Updated the integration test to be portable: on 32-bit targets (usize = 32 bits) |i64::MIN| = 2^63 does not fit, so the width is clamped to 0 (no padding) and printf succeeds with |1|; on 64-bit targets it exceeds MAX_FORMAT_WIDTH and fails with a write error. In both cases the original panic ("attempt to negate with overflow") is gone. Verified locally on both x86_64-musl and i686-musl (131/131 printf tests pass on each).

A negative '*' field width argument of i64::MIN used to panic with
attempt to negate with overflow because |i64::MIN| = 2^63 is not
representable in i64/isize. Use nb.unsigned_abs() to compute the
magnitude in unsigned arithmetic, which can represent 2^63.

Add a unit test for the width resolution and an end-to-end regression
test verifying printf fails gracefully with a write error instead of
panicking.

Fixes uutils#13766
@bolverk
bolverk force-pushed the fix/printf-asterisk-width-i64-min-overflow branch from 2d76eba to 2864ff4 Compare August 8, 2026 08:27

#[test]
fn test_asterisk_width_i64_min_no_panic() {
// Regression test for https://github.com/uutils/coreutils/issues/13766

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.

Please make the comment shorter

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.

printf: arithmetic overflow (overflow-checks) on a %*d dynamic field width of i64::MIN

3 participants