Skip to content

tool_easysrc: reset the handle between --next operations - #22890

Closed
xhon-pelushi wants to merge 1 commit into
curl:masterfrom
xhon-pelushi:fix/22701-libcurl-reset-between-next
Closed

xhon-pelushi wants to merge 1 commit into
curl:masterfrom
xhon-pelushi:fix/22701-libcurl-reset-between-next

Conversation

@xhon-pelushi

Copy link
Copy Markdown
Contributor

Follow-up to #22745 / 19d0f4a, which fixed the --libcurl cross---next leak for SSL verification by re-emitting CURLOPT_SSL_VERIFYPEER and CURLOPT_SSL_VERIFYHOST on later operations. The handle is still never reset, so every other option still carries over.

The tool creates a fresh easy handle per operation; the --libcurl generator reuses one for all of them. So for

curl -H "X-First: yes" --compressed http://host/one --next http://host/two

the generated program sets CURLOPT_HTTPHEADER and CURLOPT_ACCEPT_ENCODING for the first operation and never clears them, and the second transfer sends both — which the command line does not.

I compiled the generated program and compared what it and the real command line actually put on the wire, against a local HTTP server:

operation 1 operation 2
curl X-First, Accept-Encoding neither
generated program, master X-First, Accept-Encoding X-First, Accept-Encoding
generated program, with this patch X-First, Accept-Encoding neither

With the patch the generated program's requests are identical to curl's own for both operations.

The change

Emit curl_easy_reset() between operations rather than force-emitting more individual options. Everything the next operation needs is re-emitted after the reset already, because the generator writes its options out per operation — visible in the new test's expected output, where operation 2 re-sets CURLOPT_URL, CURLOPT_USERAGENT, CURLOPT_BUFFERSIZE, CURLOPT_MAXREDIRS, CURLOPT_TCP_KEEPALIVE and CURLOPT_VERBOSE after the reset.

The call is deferred rather than written straight after curl_easy_perform(): easysrc_perform() sets a pending flag and easysrc_add() flushes it the next time something is appended. That way a single-operation command generates no reset at all, and none appears before the trailing curl_easy_cleanup().

CURLOPT_USERAGENT no longer leaks on master, incidentally — curl sets a user agent for every operation, so operation 2 re-emits the default. Headers, --compressed and the rest do.

Testing

New test1687 for the multi-operation case: the existing --libcurl tests (1400-1407, 1420, 1465, 1481) are all single-operation, and 1679 covers only the SSL options.

  • test1687 passes with the patch and fails without it.
  • All --libcurl tests: 11/11 pass (1481 and 1679 skipped, "curl lacks SSL support" — my build has no TLS, unrelated to the change).
  • tests 1 to 200: 171/171 OK, 29 skipped for features my build lacks (NTLM, crypto, IDN, SSL, TrackMemory).
  • scripts/checksrc.pl src/tool_easysrc.c clean.

Ref: #22701

The curl tool creates a fresh easy handle for every operation, but the
--libcurl output reuses a single handle for all of them and never resets
it, so options set for one operation silently apply to the following
ones and the generated program does not do what the command line did.

19d0f4a fixed this for SSL verification by re-emitting those two
options after --next. Everything else still leaks. For

  curl -H "X-First: yes" --compressed http://host/one \
       --next http://host/two

the generated program sends X-First and Accept-Encoding on the second
transfer as well, while the command line sends them only on the first.
Confirmed by compiling the generated program and comparing what both
actually put on the wire against a local server:

                     operation 1              operation 2
  curl               X-First, Accept-Encoding  neither
  generated program  X-First, Accept-Encoding  X-First, Accept-Encoding

Emit curl_easy_reset() between operations instead, which covers the
general case: every option the next operation needs is already re-emitted
after the reset, because the tool writes out its options per operation.

The call is deferred rather than written straight after
curl_easy_perform() so that it only appears when another operation
actually follows: a single-operation command generates no reset at all,
and none is emitted before the trailing curl_easy_cleanup().

Existing --libcurl coverage (tests 1400-1407, 1420, 1465, 1481) is all
single-operation, and 1679 checks only the SSL options, so add test 1687
for the general case. It fails without this change.

Test 1679's expected output gains the same curl_easy_reset() line, since
it is a --next test too.

Ref: curl#22701
@xhon-pelushi
xhon-pelushi force-pushed the fix/22701-libcurl-reset-between-next branch from ab94f13 to 369e1b2 Compare September 10, 2026 01:08
@xhon-pelushi

Copy link
Copy Markdown
Contributor Author

Pushed a fix for the CI failures: test1679 is a --next test too, so its expected output needed the same curl_easy_reset() line. My first run here was against a build without TLS, which skips 1679 — my mistake, and the reason I missed it.

I also widened test1687's <stripfile> to the set 1400/1465 already use (CURLOPT_SSL_VERIFYPEER, SSL_VERIFYHOST, SSLVERSION, SSH_KNOWNHOSTS, HTTP_VERSION, HTTP09_ALLOWED, INTERLEAVEDATA, TIMEOUT_MS), since a TLS build emits the verify options again after the reset and the test is not about SSL.

Re-ran with TLS this time, in three configurations:

build tests 1400-1407, 1420, 1465, 1481, 1679, 1687
cmake + OpenSSL 13/13
cmake + OpenSSL + ENABLE_DEBUG 13/13
the same, --test-duphandle 6/6 (1400, 1420, 1465, 1481, 1679, 1687)

tests 1 to 200 were 171/171 before this change and checksrc.pl is still clean.

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.

🟢 Approval recommended

The deferred reset correctly prevents cross-operation option leakage and is covered by focused regression tests.

Pull request overview

This PR makes multi-operation --libcurl output match curl’s per-operation handle behavior by resetting the reused easy handle between --next operations.

Changes:

  • Defers curl_easy_reset() until code for a subsequent operation is emitted.
  • Adds a regression test covering option isolation across --next.
  • Updates the existing SSL verification fixture to include the reset.
File summaries
File Description
src/tool_easysrc.c Tracks and emits a pending easy-handle reset between operations.
tests/data/test1687 Adds multi-operation --libcurl regression coverage.
tests/data/data1687.c Defines the expected generated source for the new test.
tests/data/data1679.c Updates existing multi-operation expected output.
tests/data/Makefile.am Registers and distributes the new test files.
Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 0
  • Review effort level: Balanced

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

@bagder bagder left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nice!

@bagder bagder closed this in 1109367 Sep 10, 2026
@bagder

bagder commented Sep 10, 2026

Copy link
Copy Markdown
Member

Thanks!

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.

3 participants