Skip to content

diff: -t --tabsize=<huge> aborts in do_expand_tabs #286

Description

@leeewee

--tabsize is parsed into a usize with no upper bound and then used to size an allocation: do_expand_tabs reserves line.len() + ntabs * (tabsize - 1) bytes or every line containing a tab. A large --tabsize makes that request exceed what the allocator can serve and the process aborts with no diff: diagnostic.

$ printf '\tx\n' > a; printf 'y\n' > b
$ diff -t --tabsize=18446744073709551615 a b > /dev/null
Aborted (core dumped)
$ echo $?
134

The threshold is the allocator, not a parse guard — the request scales directly
with the flag:

$ diff -t --tabsize=1000000000          a b   # 1e9  -> exit 1, diffs normally
$ diff -t --tabsize=4611686018427387904 a b   # 2^62
memory allocation of 4611686018427387905 bytes failed   -> exit 134
$ diff -t --tabsize=9223372036854775807 a b   # isize::MAX -> exit 134

A tab in the input is required — ntabs == 0 returns early.

Root cause

src/utils.rs:

pub fn do_expand_tabs(line: &[u8], tabsize: usize) -> Vec<u8> {
    let tab = b'\t';
    let ntabs = line.iter().filter(|c| **c == tab).count();
    if ntabs == 0 {
        return line.to_vec();
    }
    let mut result = Vec::with_capacity(line.len() + ntabs * (tabsize - 1));  // :20
    let mut offset = 0;

    let mut iter = line.split(|c| *c == tab).peekable();
    while let Some(chunk) = iter.next() {
        ...
        if iter.peek().is_some() {
            result.resize(result.len() + tabsize - offset % tabsize, b' ');    // :31

tabsize comes straight from --tabsize and multiplies the tab count. Nothing bounds it at parse time, so the capacity is attacker-chosen and the allocation aborts.

The same function aborts at a second site, src/utils.rs:31. Once the with_capacity at :20 is survived, the per-chunk result.resize(result.len() + tabsize - offset % tabsize, b' ') requests another attacker-sized allocation. Which of the two fires depends on the --tabsize value:

# :20  — capacity overflow (tabsize = 2^63)
$ diffutils diff -t --tabsize=9223372036854775808 a b
thread 'main' panicked at library/alloc/src/raw_vec/mod.rs:...: capacity overflow
$ echo $?
134

# :31  — allocation abort (tabsize = u64::MAX)
$ printf '\tx\n' > a; printf 'y\n' > b
$ diffutils diff -t --tabsize=18446744073709551615 a b
thread 'main' panicked at library/alloc/src/raw_vec/mod.rs:28:5:
$ echo $?
134

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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