Skip to content

cmp: --verbose on a zero-length-metadata file underflows the offset padding (overflow-checks only) #265

Description

@leeewee

format_verbose_difference (src/cmp.rs) pads the byte-offset column with a plain subtraction, while its sibling branch a few lines up uses the checked form:

// print_bytes branch (cmp.rs:582)      — safe
let at_byte_padding = offset_width.saturating_sub(at_byte_str.len());
// verbose branch (cmp.rs:610)          — underflows
let at_byte_padding = offset_width - at_byte_str.len();

offset_width is derived from the smaller file's length:

let smaller = cmp::min(a_size, b_size) as BytesLimitU64;   // a_size = fs::metadata(path).len()
offset_width = cmp::min(smaller, offset_width);
let offset_width = 1 + offset_width.checked_ilog10().unwrap_or(1) as usize;

For a file whose metadata length is 0 but which yields bytes when read — e.g. /dev/zerosmaller is 0, so offset_width collapses to
1 + ilog10(0→1) = 2. Once the running byte offset reaches three digits (≥ 100), offset_width (2) - at_byte_str.len() (3) underflows: an abort under overflow-checks, or a wrap to usize::MAX-ish in release (feeding a for _ in 0..at_byte_padding space-padding loop).

$ printf '\377%.0s' {1..150} > nz            # 150 non-zero bytes
$ cmp --verbose /dev/zero nz                 # overflow-checks build
thread 'main' panicked at src/cmp.rs:610:31:
attempt to subtract with overflow
$ echo $?
134

GNU cmp lists the differing bytes and exits 1:

$ /usr/bin/cmp --verbose /dev/zero nz ; echo $?
  1   0 377
  2   0 377
  ...
1

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions