numfmt aborts/panics on a large `--padding` (unbounded allocation)
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 24.1k
- Forks
- 2k
- Avg merge
- 1d 5h
- Merged PRs (30d)
- 365
Description
Summary
numfmt --padding=N sizes an output String directly from the user-supplied
width and allocates it unconditionally with String::with_capacity(width). The
width is unbounded, so a large --padding makes numfmt attempt an enormous
allocation and crash:
- a large width (≤
isize::MAX) → allocation failure → abort (SIGABRT, exit 134); --padding=-9223372036854775808(isize::MIN) →unsigned_abs()is
isize::MAX + 1, which exceedsisize::MAX→String::with_capacity
panics withcapacity overflow(deterministic, regardless of available RAM).
GNU numfmt handles the same inputs cleanly: it prints numfmt: memory exhausted
and exits 1.
This is effectively a regression introduced by the fix for the format!-width
bug (gh-numfmt-11411): that fix replaced format!'s u16-capped width specifier
with a pad_string helper, removing the u16 cap but leaving the width unbounded
at the allocation level.
Steps to reproduce
# large width -> allocation failure -> SIGABRT
$ numfmt --padding=9000000000000000000 1
memory allocation of 9000000000000000000 bytes failed
Aborted (core dumped)
$ echo $?
134
# isize::MIN: unsigned_abs() = isize::MAX + 1 > isize::MAX -> capacity overflow panic
$ numfmt --padding=-9223372036854775808 1
thread 'main' panicked at .../alloc: capacity overflow
$ echo $?
134
Expected behavior
Match GNU: report the failure and exit non-zero, without aborting/panicking.
$ /usr/bin/numfmt --padding=9000000000000000000 1
/usr/bin/numfmt: memory exhausted
$ echo $?
1
$ /usr/bin/numfmt --padding=-9223372036854775808 1
/usr/bin/numfmt: memory exhausted
$ echo $?
1
Actual behavior
The process aborts (exit 134) — either an allocator abort
(memory allocation of N bytes failed) or a capacity overflow panic — instead
of a clean diagnostic.
Root cause
pad_string in src/uu/numfmt/src/format.rs — the helper added by the
gh-numfmt-11411 fix — allocates the full padded width up front:
fn pad_string(s: &str, width: usize, fill: char, right_align: bool) -> String {
...
let mut result = String::with_capacity(width); // width is the --padding value
...
}
--padding is parsed as an isize and passed through as p as usize /
p.unsigned_abs(), with no upper bound. A fix should bound the padding to a
sane limit (or use try_reserve and report an error) rather than eagerly
allocating the full width.
(The neighbouring "0".repeat(precision) is the same class but is guarded by
is_too_large_to_format on the normal paths.)
Environment
- uutils coreutils:
numfmt (uutils coreutils) 0.8.0(commitbcdd1343, 2026-06-01) - rustc: 1.89.0
- Reference: GNU coreutils 8.30
Found by our static analysis tooling.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/uu/numfmt/src/format.rs, focusing on pad_string and its String::with_capacity(width) allocation. Reproduce the large positive and isize::MIN --padding cases, then inspect how --padding is parsed and passed into the helper. Done means both inputs produce a clean memory-exhaustion diagnostic and non-zero exit without aborting or panicking.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100