Skip to content

head panics (aborts) on a write error while printing a long -v filename header #13264

Description

@leeewee

Summary

With -v (or when heading multiple files), head prints a ==> <filename> <== header for each file. The filename is written with print_verbatim(file).unwrap(). When the underlying stdout write fails for a reason other than a broken pipe (e.g. the output device is full, or the redirect target is no longer writable), that .unwrap() turns the io::Error into a panic.

Unlike the plain -v > /dev/full write-error class seen in other utilities, this one only fires when the filename argument is longer than the stdout buffer (~1024 bytes). The two neighbouring header writes use ? and fail gracefully; only the filename write uses unwrap, and a short filename stays buffered (its error surfaces later at the ?-checked flush). A filename longer than the buffer forces write_all to flush mid-write, so the failure is raised inside the un-propagated unwrap — hence the long path is required to trigger it.

Steps to reproduce

Redirect stdout to /dev/full (every write fails with ENOSPC) and pass a file argument whose path is longer than ~1024 bytes. A path made of repeated ./ segments resolves to a normal file while printing verbatim as a long string:

$ LONG="/dev/$(python3 -c "print('./'*512)")null"   # ~1033-byte path to /dev/null
$ head -v "$LONG" > /dev/full
thread 'main' panicked at src/uu/head/src/head.rs:463:42:
called `Result::unwrap()` on an `Err` value: Os { code: 28, kind: StorageFull, message: "No space left on device" }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Aborted (core dumped)
$ echo $?
134

The threshold is sharp — a filename at/below the buffer size fails gracefully, above it aborts:

$ head -v /dev/null > /dev/full ; echo $?    # short name → graceful
head: No space left on device
1

Expected behavior

Match GNU: report the write error and exit non-zero, without panicking — regardless of filename length.

$ /usr/bin/head -v "$LONG" > /dev/full ; echo $?
/usr/bin/head: write error: No space left on device
1

Root cause

In the verbose header block of uu_head (src/uu/head/src/head.rs), the two literal writes propagate their error but the filename write unwraps it:

write!(stdout, "==> ")?;
print_verbatim(file).unwrap();   // <-- panics on write error
writeln!(stdout, " <==")?;

print_verbatim (src/uucore/src/lib/mods/display.rs) writes directly to the
process-global stdout:

pub fn print_verbatim<S: AsRef<OsStr>>(text: S) -> io::Result<()> {
    io::stdout().write_all_os(text.as_ref())
}

io::stdout() is a LineWriter with a ~1024-byte buffer. For a short filename the bytes stay buffered and the error only surfaces at the next ?-checked flush (graceful); for a filename larger than the buffer, write_all_os flushes mid-write and the ENOSPC/write error is returned to the unwrap, which panics.

Found by our static analysis tooling.

Metadata

Metadata

Assignees

No one assigned

    Labels

    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