Environment: curl 8.22.0 on Linux (also reproduced on current master). HTTP proxy with multi-round
proxy auth (Digest/NTLM/Negotiate) over CONNECT, connection kept alive.
What happens
When a proxy answers the first CONNECT with 407 + Transfer-Encoding: chunked, curl ignores that
body with its chunk decoder. On the auth retry (still the same connection), the per-tunnel state
ts->chunked_encoding and the chunk decoder ts->ch are not reset (tunnel_reinit() only
resets rcvbuf/cl/keepon). If the retry then gets another body-less 407 (e.g. Content-Length: 0),
curl enters its "ignore response body" loop believing the body is chunked, and waits forever for
chunked bytes that never come -- it never finishes processing the 407, never performs the next auth
round, and the transfer hangs (until the proxy or a timeout kills the connection).
Repro
Fake proxy (Digest, persistent connection):
CONNECT (no auth) -> 407 + Transfer-Encoding: chunked, chunked body HELLO
CONNECT (with auth) -> 407 + Proxy-Authenticate: Digest ... nonce="n2" ... stale=true + Content-Length: 0 (no body)
CONNECT (with auth) -> 200 Connection established
Client: curl --proxy-digest -U user:pass -x http://127.0.0.1:8080/ https://example.invalid/
Observed: after response 2, curl never sends the third CONNECT -- it hangs in "ignore chunked
response body" (the proxy never sees CONNECT 3). Control run where response 1 is Content-Length: 0
instead of chunked: all three CONNECTs proceed normally and the tunnel is established.
Why
lib/cf-h1-proxy.c, tunnel_reinit(): resets rcvbuf, request_data, keepon, cl,
close_connection, maybe_folded, leading_unfold, nsent, headerlines -- but not
ts->chunked_encoding, and it does not re-init ts->ch (the chunk decoder stays at the
CHUNK_DONE state after the previous body was ignored).
single_header() / the KEEPON_IGNORE loop then treat the next response's (empty) body as
chunked: Curl_httpchunk_read() on a completed decoder consumes nothing, so is_done is only
reached when data arrives -- which never happens for a body-less 407.
Expected vs actual
Expected: each CONNECT response is processed with a fresh per-response body state.
Actual: a stale chunked_encoding from a previous 407 stalls the auth retry (or, when bytes do
arrive, swallows the first byte of the next response and corrupts its parse). Connection/protocol
state corruption class, low-to-medium severity.
Fix direction: clear ts->chunked_encoding and reset (re-init) ts->ch in tunnel_reinit() (and/or
before starting to read each new CONNECT response).
Environment: curl 8.22.0 on Linux (also reproduced on current master). HTTP proxy with multi-round
proxy auth (Digest/NTLM/Negotiate) over CONNECT, connection kept alive.
What happens
When a proxy answers the first CONNECT with
407+Transfer-Encoding: chunked, curl ignores thatbody with its chunk decoder. On the auth retry (still the same connection), the per-tunnel state
ts->chunked_encodingand the chunk decoderts->chare not reset (tunnel_reinit()onlyresets rcvbuf/cl/keepon). If the retry then gets another body-less
407(e.g.Content-Length: 0),curl enters its "ignore response body" loop believing the body is chunked, and waits forever for
chunked bytes that never come -- it never finishes processing the 407, never performs the next auth
round, and the transfer hangs (until the proxy or a timeout kills the connection).
Repro
Fake proxy (Digest, persistent connection):
CONNECT(no auth) ->407+Transfer-Encoding: chunked, chunked bodyHELLOCONNECT(with auth) ->407+Proxy-Authenticate: Digest ... nonce="n2" ... stale=true+Content-Length: 0(no body)CONNECT(with auth) ->200 Connection establishedClient:
curl --proxy-digest -U user:pass -x http://127.0.0.1:8080/ https://example.invalid/Observed: after response 2, curl never sends the third CONNECT -- it hangs in "ignore chunked
response body" (the proxy never sees CONNECT 3). Control run where response 1 is
Content-Length: 0instead of chunked: all three CONNECTs proceed normally and the tunnel is established.
Why
lib/cf-h1-proxy.c,tunnel_reinit(): resetsrcvbuf,request_data,keepon,cl,close_connection,maybe_folded,leading_unfold,nsent,headerlines-- but notts->chunked_encoding, and it does not re-initts->ch(the chunk decoder stays at theCHUNK_DONEstate after the previous body was ignored).single_header()/ theKEEPON_IGNOREloop then treat the next response's (empty) body aschunked:
Curl_httpchunk_read()on a completed decoder consumes nothing, sois_doneis onlyreached when data arrives -- which never happens for a body-less 407.
Expected vs actual
Expected: each CONNECT response is processed with a fresh per-response body state.
Actual: a stale
chunked_encodingfrom a previous 407 stalls the auth retry (or, when bytes doarrive, swallows the first byte of the next response and corrupts its parse). Connection/protocol
state corruption class, low-to-medium severity.
Fix direction: clear
ts->chunked_encodingand reset (re-init)ts->chintunnel_reinit()(and/orbefore starting to read each new CONNECT response).