Skip to content

tool_operate: clear the retry flag when the output file fails - #23001

Closed
MegaManSec wants to merge 1 commit into
curl:masterfrom
MegaManSec:retry-write-error-stale-stream
Closed

MegaManSec wants to merge 1 commit into
curl:masterfrom
MegaManSec:retry-write-error-stale-stream

Conversation

@MegaManSec

@MegaManSec MegaManSec commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

retrycheck() sets *retryp = TRUE before flushing/truncating/seeking the partial output file, and returns CURLE_WRITE_ERROR without clearing it if any of those steps fails. post_per_transfer() only skips the close path when !result && *retryp, so it closes the output file, while the caller still sees retry == TRUE and reuses the same transfer. outs->stream is never cleared, so tool_write_cb() writes the next response through the closed FILE* and post_close_output() closes it again.

Clear the flag on those four error paths and reset outs->stream/fopened after closing.

Test 1716 covers it. The failure needs a local I/O error, so it uses a DEBUGBUILD-only hook on the truncate, in the style of the existing CURL_DBG_NO_USE_SSL_ON_FIRST. Without the fix the tool retries and the server sees a second request; with it the transfer ends with (23) after one request. Verified separately with a shim forcing ftruncate() to fail with ENOSPC: fclose() is called twice on the same FILE*, the second time with fileno() returning -1/EBADF. Same in -Z.

@testclutch

Copy link
Copy Markdown

Analysis of PR #23001 at e5bf1eb3:

Test 2270 failed, which has NOT been flaky recently, so there could be a real issue in this PR. Note that this test has failed in 8 different CI jobs (the link just goes to one of them). Note that this CI job has had a number of other flaky tests recently (3, to be specific) so it may be that this failure is rather a systemic issue with this job and not with this specific PR.

Generated by Testclutch

@bagder
bagder requested a balanced review from Copilot September 19, 2026 08:27

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

The reproduced failure path lacks an automated regression test.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
What changed in this PR

Fixes retry error handling to prevent reuse and double-closing of a failed output stream.

Changes:

  • Clears the retry flag when output-file preparation fails.
  • Resets output stream state after closing.
File Description
src/​tool_operate.c Corrects retry and output-stream cleanup state.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/tool_operate.c
retrycheck() left *retryp set when the flush/truncate/seek of the
partial output file failed, so post_per_transfer() closed the output
file and the caller retried the same transfer anyway. The next response
was written through the closed FILE*, which was then closed a second
time.

Clear the flag on those errors and reset the stream after closing it.

Add test 1716, with a debug-build hook to make the truncate fail.
@MegaManSec
MegaManSec force-pushed the retry-write-error-stale-stream branch from e5bf1eb to ac242f6 Compare September 19, 2026 09:27
@github-actions github-actions Bot added the tests label Sep 19, 2026
@bagder bagder closed this in b148863 Sep 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

4 participants