Skip to content

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

Description

@leeewee

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions