uutils / uutils/coreutils

numfmt aborts/panics on a large `--padding` (unbounded allocation)

Open
#12,560 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

U - numfmt
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 exceeds isize::MAXString::with_capacity
    panics with capacity 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 (commit bcdd1343, 2026-06-01)
  • rustc: 1.89.0
  • Reference: GNU coreutils 8.30

Found by our static analysis tooling.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.