Skip to content

fix(gitlimit): keep only the tail of git's stderr in git errors - #759

Merged
ThomasK33 merged 8 commits into
mainfrom
fix-gitread-error-argv-bound
Nov 7, 2026
Merged

ThomasK33 merged 8 commits into
mainfrom
fix-gitread-error-argv-bound

Conversation

@ThomasK33

@ThomasK33 ThomasK33 commented Nov 7, 2026 •

Copy link
Copy Markdown
Member

Why

gitread.Run, the write path's git helpers (wcScratch.git, wrefGit) and applyPatch's plain error quote git's stderr in their errors, and the API logs those errors. wcScratch.git and wrefGit kept all of git's stderr, and applyPatch's plain error (a cancelled request, a git killed by a signal) quoted all of it, after its whole command line. A git that prints 1 MiB to stderr gave a log record of about 1 MB.

#757 bounded the command line in Run, wcScratch.git and wrefGit with gitlimit.Name. This PR bounds git's stderr in all four, and applyPatch's command line.

What changes

  • Run already kept only the last 64 KiB of git's stderr. That buffer moves to internal/gitlimit as gitlimit.TailBuffer, so git runners outside gitread can use it (githttp cannot import gitread). wcScratch.git and wrefGit write git's stderr into it, and applyPatch's plain error quotes its tail. git prints its fatal message last.
  • applyPatch's plain error names its command with gitlimit.Name.
  • internal/gitlimit/README.md lists TailBuffer and its bound, and internal/gitread/README.md states that Run's errors keep it.

The first rounds of this PR added gitlimit.CommandLine for the command line. #757 landed gitlimit.Name for the same job first, so the merge of main removes CommandLine and its tests.

Still unbounded after this PR

  • Other git runners keep all of git's stderr in their errors, and those reach the log: githttp's runGitCtx, localRefSeq, updateRefStdin and the commit-graph write, jobs' runGitStdin, sync's runGitTo, the three children of lfsscan.ReachableLFSOIDs, and simtest's simGit, and the write path's wcBlobSink (hash-object -w). A follow-up PR (branch bounded-stderr-git-runners) moves them to gitlimit.TailBuffer. It opens after this PR merges, because it uses it.
  • POST /repos/commit-pack answers a failed hash-object with 400 and git's stderr in the body. fix(api): answer 404 or 400 for a git check only when git says no #760 answers 500 and logs the error instead.
  • A grep pattern or a commit message over the exec argument limit (128 KiB on Linux) still gets a 500, because exec fails. The E2BIG PR (branch fix-long-git-args-client-input) fixes that input.

Verification

  • TestWriteGitErrorsKeepStderrTail runs a git that prints 1 MiB to stderr. Before this PR the wcScratch.git and wrefGit errors were 1,048,617 bytes, and the applyPatch plain error (a git apply that dies of SIGTERM) was 1,048,630 bytes.
  • TestTailBufferKeepsTheLast64KiB (internal/gitlimit) writes 40 chunks of about 5 KB and expects exactly the last 64 KiB. TestRunErrorKeepsTheStderrTail runs a git that prints 1 MiB to stderr and expects Run's error to end with exactly its last 64 KiB. Mutants that fail them: a tail of 1, 32, 60 or 128 KiB, and Run with a plain buffer (a 1,048,618-byte error).
  • TestApplyPatchErrorBoundsTheArgv runs applyPatch with a fake git that prints nothing (a real git can echo the argument into stderr) and dies of SIGTERM, with a 64 KiB argument (git runs) and a 200 KiB one (exec fails with E2BIG on Linux). With strings.Join the error was 65,577 bytes.
  • Mutants on this head that fail a test: wcScratch.git, wrefGit or Run with a plain buffer, and applyPatch without its tail or back on strings.Join.
  • Lanes at this head, all with -race, and with -tags=openstorage_viewcheck except on the local backend, as in CI: internal/gitlimit, internal/gitread, internal/api and cmd/openstorage pass on PostgreSQL, local and S3-WAL. internal/gitread and internal/gitlimit test binaries build for linux and darwin on amd64 and arm64. golangci-lint: 0 issues. go vet ./... is clean. Both --fingerprints-only checks pass, and the doc-link check finds 0 broken links.

Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: xhigh

Run's errors start with the whole git command line, and they reach the
server's log. Some argvs are request-sized: a grep pattern or a list of
paths from the API has no length limit of its own, and past 128 KiB one
argument fails exec with E2BIG. Each such call logged a record as large
as the request.

Run now quotes at most 4096 bytes of the command (maxErrArgvBytes), cut
at a rune boundary, and then its full size: "git grep -e aaa... (204812
bytes)". A shorter command reads as before.

TestRunErrorBoundsTheArgv runs a fake git with a 64 KiB argument (git
exits 1) and a 200 KiB one (exec fails with E2BIG). Before this change
the errors were 65,566 and 204,907 bytes long.

Change-Id: I53303afd1147fd53ecc70260e40ccf2b1004dcd5
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Commit and merge requests pass the commit message to git as an argument
(commit-tree -m), and wcScratch.git's errors quoted the whole command
line, so a message past 128 KiB (exec fails with E2BIG) logged a record
as large as the message. The error now quotes the command through
gitread.CommandLine (exported from Run's error path), which keeps 4096
bytes and the size.

TestWCScratchGitErrorBoundsTheArgv runs commit-tree with a 200 KiB
message. With the old error line the error was 204,932 bytes long.

Change-Id: I7c4756a0fe24dd8cc527de23508dbd6ef2e3d2ba
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I85b7d4cf39b81008a060df7c67be0582a3c3ea47
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 7, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-07T13:55:49.944727Z 8195caa Manual request
🔒 Security Review ✅ Completed 2026-11-07T13:57:12.210175Z 8195caa Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Chef's kiss.

Reviewed commit: bcf9fb2b02

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: bcf9fb2b02

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

applyPatch and wrefGit still quoted their whole command line, and
wcScratch.git and wrefGit kept all of git's stderr in a bytes.Buffer.
The API logs these errors, so a request-sized argument or a git that
prints megabytes made a log record as large.

All three helpers now quote the command through gitread.CommandLine,
and wcScratch.git and wrefGit keep git's stderr in gitread.TailBuffer
(exported from Run's tailBuffer), which keeps the last 64 KiB: git
prints its fatal message last. The README lists both and states the
bound.

TestWriteGitErrorsBoundTheArgv runs the three helpers with a 200 KiB
argument: before this change the applyPatch and wrefGit errors were
204,891 and 204,888 bytes. TestWriteGitErrorsKeepStderrTail runs a git
that prints 1 MiB to stderr: both errors were 1,048,617 bytes.

Change-Id: Ic40b87c486f6dc54fd6757d9e38c8634e877d7ba
Signed-off-by: Thomas Kosiewski <tk@coder.com>
applyPatch's plain error (a cancelled request, a git killed by a signal)
quoted all of git's stderr, which can echo the patch. It now keeps the
last 64 KiB through gitread.TailBuffer. The classification below it
still reads all of the stderr.

TestCommandLineCut pins the cut on both sides: a 4096-byte command stays
whole and a 4097-byte one is cut at 4096, so a cap of 1024, 5000 or 7000
fails. A 3-byte and a 4-byte rune across the cut need two and three
steps back, so a loop limited to one or two steps fails.
TestWriteGitErrorsKeepStderrTail gets an applyPatch case, a git apply
that prints 1 MiB and dies of SIGTERM (1,048,631 bytes before).

Change-Id: Idf2401f68e3b21eb46b92b1cdafcf4a7af243f35
Signed-off-by: Thomas Kosiewski <tk@coder.com>
githttp cannot import gitread (gitread imports githttp), and sync and
lfsscan import neither, but their git runners need the same bounds.
gitlimit, which already starts and reaps every git child, now holds
CommandLine and TailBuffer. gitread.Run and the write-path helpers use
them from there, and TestCommandLineCut moves with them. No behavior
changes.

Change-Id: Ie64ca578793f9a03bea4beeea1ca03d47df5cf92
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@ThomasK33 ThomasK33 changed the title fix(gitread): keep a request-sized git argv out of error messages fix(gitlimit): bound the command line and the stderr that git errors quote Nov 7, 2026
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: a30b267fe1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: a30b267fe1

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

TestTailBufferKeepsTheLast64KiB writes 40 chunks of about 5 KB and
expects exactly the last 64 KiB. TestRunErrorKeepsTheStderrTail runs a
git that prints 1 MiB to stderr and expects Run's error to end with
exactly its last 64 KiB. Mutants that fail them: a tail of 1, 32, 60 or
128 KiB, and Run with a plain bytes.Buffer (a 1,048,618-byte error).

TestWriteGitErrorsBoundTheArgv ran a real git with a 200 KiB argument
and relied on exec failing with E2BIG, which only Linux guarantees:
elsewhere git starts and can echo the argument into stderr. It now runs
a fake git that prints nothing, with a 64 KiB argument (git runs and
exits 1) and a 200 KiB one (E2BIG on Linux).

Change-Id: I28575a148086c976a11a67c799d3ec1334926928
Signed-off-by: Thomas Kosiewski <tk@coder.com>
gitlimit.Name (each argument cut at 256 bytes, at most 32 arguments).
This branch's gitlimit.CommandLine did the same job, so it goes, with
TestCommandLineCut and TestRunErrorBoundsTheArgv. applyPatch's plain
error, which #757 left on strings.Join, uses gitlimit.Name too, and
TestApplyPatchErrorBoundsTheArgv keeps only that case.

What stays from this branch: gitlimit.TailBuffer (the last 64 KiB of
git's stderr) in Run, wcScratch.git, wrefGit and applyPatch's plain
error, and its tests.

Change-Id: If56daff93587005b6557293eab407a1bddec895a
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@ThomasK33 ThomasK33 changed the title fix(gitlimit): bound the command line and the stderr that git errors quote fix(gitlimit): keep only the tail of git's stderr in git errors Nov 7, 2026
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

Reviewed commit: 8195caaeb0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: 8195caaeb0

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@ThomasK33
ThomasK33 added this pull request to the merge queue Nov 7, 2026
Merged via the queue into main with commit 6d4b1bb Nov 7, 2026
13 of 14 checks passed
@ThomasK33
ThomasK33 deleted the fix-gitread-error-argv-bound branch November 7, 2026 15:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant