GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — diff: prevent --tabsize from crashing on huge values by sap1110 · Pull Request #292 · uutils/diffutils

Conversation

@sap1110

@sap1110 sap1110 commented Sep 28, 2026

Copy link
Copy Markdown

Fixed an issue where passing an extremely large value to --tabsize could cause diff to crash.

Added an upper limit matching GNU diff.
Values above the limit now return a normal invalid tabsize error.
Added tests for the boundary values and a large --tabsize case.

This keeps the behavior consistent with GNU diff and prevents the crash.

Huge values below the limit (like 2^62) are still accepted, same as GNU, and can still run out of memory. Handling that would need the diff output to be fallible. Let me know if you want that too.

Closes #286

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
sylvestre
sylvestre reviewed Sep 28, 2026
View reviewed changes
Comment thread src/params.rs Outdated
use regex::Regex;

/// Largest value accepted for `--tabsize`, matching GNU diff.
const MAX_TABSIZE: usize = isize::MAX as usize - 3;

@sylvestre sylvestre Sep 28, 2026

Copy link
Copy Markdown
Collaborator

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how did you get - 3?
it's not in the manual, so where does this value come from?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@sap1110 sap1110 Sep 29, 2026

Copy link
Copy Markdown
Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found it by testing GNU diff 3.12, GNU accepts the one ending in …804 and rejects the one ending in …805. happy to change the exact limit if you want

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
sylvestre
sylvestre reviewed Sep 28, 2026
View reviewed changes
Comment thread src/params.rs
num
}
Err(_) => return Err(format!("invalid tabsize «{tabsize_str}»")),
Ok(num) if num != 0 && num <= MAX_TABSIZE => num,

@sylvestre sylvestre Sep 28, 2026

Copy link
Copy Markdown
Collaborator

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesn't fix the crash, it just moves it
-t --tabsize=9223372036854775804 with a tab in the input still aborts in do_expand_tabs (with_capacity + ntabs * (tabsize - 1) overflow)
could you please fix do_expand_tabs instead (no huge preallocation, write the spaces as a stream)?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@sap1110 sap1110 Sep 29, 2026

Copy link
Copy Markdown
Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, it did just move the crash. I've changed do_expand_tabs the way you suggested: it no longer reserves a big block of memory up front, and it writes the spaces in small chunks. One thing it doesn't fix yet is that diff still holds its whole output in memory, so a big enough value will still run out of memory at some point. Do you want me to stream the whole output in this PR, or leave that for a separate follow-up?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
sylvestre
sylvestre reviewed Sep 28, 2026
View reviewed changes
Comment thread tests/integration.rs Outdated
if !option.is_empty() {
cmd.arg(option);
}
cmd.arg("-t")

@sylvestre sylvestre Sep 28, 2026

Copy link
Copy Markdown
Collaborator

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is -t needed here?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@sap1110 sap1110 Sep 29, 2026

Copy link
Copy Markdown
Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it isn't, so I've removed it. The value gets rejected before any input is read, and GNU rejects it without -t as well.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
sylvestre
sylvestre reviewed Sep 28, 2026
View reviewed changes
Comment thread tests/integration.rs Outdated
file1.write_all("\tx\n".as_bytes())?;
let mut file2 = NamedTempFile::new()?;
file2.write_all("y\n".as_bytes())?;
for option in ["", "-u", "-c"] {

@sylvestre sylvestre Sep 28, 2026

Copy link
Copy Markdown
Collaborator

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the value is rejected while parsing, so looping over the formats doesn't test anything more, no?
please add a test with a value below the limit and a tab in the input, that's the one that crashes

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@sap1110 sap1110 Sep 29, 2026

Copy link
Copy Markdown
Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, the loop didn't add anything, so I've removed it. In its place I added a test that calls do_expand_tabs directly with a tab and a huge value just below the limit. It finishes instantly without crashing. A full end-to-end test would need the output to stream first, so that depends on what you decide about my question in the other thread.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
sylvestre
sylvestre reviewed Oct 23, 2026
View reviewed changes
Comment thread src/params.rs Outdated

use regex::Regex;

/// Largest value accepted for `--tabsize`, matching GNU diff.

@sylvestre sylvestre Oct 23, 2026

Copy link
Copy Markdown
Collaborator

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i guess the - 3 is so that t + GUTTER_WIDTH_MIN in side_diff Config::new doesn't overflow isize?
if so, please say that here instead of "matching GNU diff"

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@sap1110 sap1110 Oct 30, 2026

Copy link
Copy Markdown
Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's exactly it. GUTTER_WIDTH_MIN is 3, and side-by-side mode computes tabsize + 3 as an isize. So isize::MAX - 3 is the largest tabsize that doesn't overflow; one higher panics with "attempt to add with overflow".

I updated the comment to say that, and that GNU accepts the same maximum. I also added a unit test that sets up side-by-side mode with tabsize at isize::MAX - 3, so if the limit changes the test will catch it.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
sylvestre
sylvestre reviewed Oct 23, 2026
View reviewed changes
Comment thread src/utils.rs Outdated
}

/// A writer that fails once it has been given `limit` bytes.
struct LimitedWriter {

@sylvestre sylvestre Oct 23, 2026

Copy link
Copy Markdown
Collaborator

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this writer?
&mut [u8] (e.g. &mut [0u8; 1000][..]) already fails with WriteZero once full

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@sap1110 sap1110 Oct 30, 2026

Copy link
Copy Markdown
Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, we don't. I removed it. The test now writes into a plain 1000-byte buffer, which already fails with WriteZero when it fills up, and the check is the same as before.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
sylvestre
sylvestre reviewed Oct 23, 2026
View reviewed changes
Comment thread src/utils.rs Outdated
fn huge_tabsize() {
// The spaces are streamed to the writer, so a huge tab size
// neither overflows nor needs a huge buffer up front.
for tabsize in [usize::MAX / 2, usize::MAX] {

@sylvestre sylvestre Oct 23, 2026

Copy link
Copy Markdown
Collaborator

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

neither value is below the limit, so this doesn't test what the parser accepts
could you please use MAX_TABSIZE?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@sap1110 sap1110 Oct 30, 2026

Copy link
Copy Markdown
Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. The test now uses MAX_TABSIZE, the largest value the parser accepts, instead of a hard-coded number.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@sylvestre

sylvestre commented Nov 10, 2026

Copy link
Copy Markdown
Collaborator

sorry but it needs to be rebased

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

sap1110 added 4 commits November 12, 2026 12:46
@sap1110
diff: reject a --tabsize too large for GNU diff
3c654e4
A huge --tabsize was accepted and then used to size the tab expansion
buffer, so the first tab in the input made diff overflow or abort on
allocation failure. Reject values above isize::MAX - 3 with
"invalid tabsize" and exit status 2, as GNU diff does.
@sap1110
fixup! diff: reject a --tabsize too large for GNU diff
683601f
@sap1110
fixup! diff: reject a --tabsize too large for GNU diff
bbce911
@sap1110
fixup! diff: reject a --tabsize too large for GNU diff
fbb6fcf
@sap1110
sap1110 force-pushed the diff-huge-tabsize branch from dbc6ebe to fbb6fcf Compare November 12, 2026 07:37
@sap1110

sap1110 commented Nov 12, 2026

Copy link
Copy Markdown
Author

It's rebased.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

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

Reviewers

@sylvestre sylvestre sylvestre left review comments

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

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

Uh oh!

There was an error while loading. Please reload this page.

2 participants

@sap1110 @sylvestre
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.