Wrong connection reuse case for Share interface
Dmitry Karpov via curl-library <[email protected]> Wed, 1 Jul 2026 00:51:27 +0000
| Newsgroups | gmane.comp.web.curl.library |
|---|---|
| Message-ID | <DM6PR01MB5611B1CEDB14F99AC1F81E1DC3F62@DM6PR01MB5611.prod.exchangelabs.com> |
--===============5725668390809538735==
Content-Language: en-US
Content-Type: multipart/alternative;
boundary="_000_DM6PR01MB5611B1CEDB14F99AC1F81E1DC3F62DM6PR01MB5611prod_"
--_000_DM6PR01MB5611B1CEDB14F99AC1F81E1DC3F62DM6PR01MB5611prod_
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: quoted-printable
Hi Folks,
I found one use case for share interface when a next transfer using the sam=
e share handle may use abandoned active connection from the previous transf=
er which wasn't completed.
The scenario is following:
* Create share handle with CURL_LOCK_DATA_CONNECT option.
* Create easy handle and set share via CURLOPT_SHARE option.
* Create multi-handle and add easy handle to it.
* Run the transfer using multi interface to some point where it will be=
abandoned (i.e. application may make this decision due to lack of space wh=
ere to write incoming data etc)
* Set share to NULL on "abandoned" easy handle to avoid using the share=
on that transfer further and remove it from the multi handle (via curl_mul=
ti_remove_handle()).
* Create a new easy handle, set the same share via CURLOPT_SHARE option=
, add easy handle to the multi-handle and run it.
On the last step, the new transfer may reuse the connection with partially =
received data from the previously abandoned transfer instead of creating a =
new one and get "weird server reply" error.
This happens because when share is set to NULL via CURLOPT_SHARE option, it=
calls the Curl_share_easy_unlink() function which just detaches the connec=
tion, but not closes it:
CURLcode Curl_share_easy_unlink(struct Curl_easy *data)
{
...
/* If data has a connection from this share, detach it. */
if(data->conn && (share->specifier & (1 << CURL_LOCK_DATA_CONNECT)))
Curl_detach_connection(data);
...
}
So, when curl_multi_remove_handle() is called later, it doesn't close the c=
onnection for the premature finished transfer because the connection was de=
tached,
and thus active connection may be taken from the connection pool own by the=
share and wrongly reused.
I think the Curl_share_easy_unlink() should close the active connection if =
it is called on some prematurely abandoned transfer.
I discovered this on the later libcurl versions (8.19+), the older versions=
like 8.6.0 which I used before don't have this issue, so it may be perceiv=
ed as a regression from the earlier builds.
It is interesting that sometimes libcurl may avoid reusing the connection i=
n such cases as it detects it as "dead" even though it doesn't close it exp=
licitly,
which allowed my connection sharing to pass in some cases and suspect flaki=
ness.
Thanks!
Dmitry Karpov
--_000_DM6PR01MB5611B1CEDB14F99AC1F81E1DC3F62DM6PR01MB5611prod_
Content-Type: text/html; charset="us-ascii"
Content-Transfer-Encoding: quoted-printable
<html xmlns:v=3D"urn:schemas-microsoft-com:vml" xmlns:o=3D"urn:schemas-micr=
osoft-com:office:office" xmlns:w=3D"urn:schemas-microsoft-com:office:word" =
xmlns:m=3D"http://schemas.microsoft.com/office/2004/12/omml" xmlns=3D"http:=
//www.w3.org/TR/REC-html40">
<head>
<meta http-equiv=3D"Content-Type" content=3D"text/html; charset=3Dus-ascii"=
>
<meta name=3D"Generator" content=3D"Microsoft Word 15 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
{font-family:Wingdings;
panose-1:5 0 0 0 0 0 0 0 0 0;}
@font-face
{font-family:"Cambria Math";
panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
{font-family:Aptos;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{margin:0in;
font-size:12.0pt;
font-family:"Aptos",sans-serif;
mso-ligatures:standardcontextual;}
p.MsoListParagraph, li.MsoListParagraph, div.MsoListParagraph
{mso-style-priority:34;
margin-top:0in;
margin-right:0in;
margin-bottom:0in;
margin-left:.5in;
font-size:12.0pt;
font-family:"Aptos",sans-serif;
mso-ligatures:standardcontextual;}
span.EmailStyle17
{mso-style-type:personal-compose;
font-family:"Aptos",sans-serif;
color:windowtext;}
.MsoChpDefault
{mso-style-type:export-only;}
@page WordSection1
{size:8.5in 11.0in;
margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
{page:WordSection1;}
/* List Definitions */
@list l0
{mso-list-id:467207797;
mso-list-type:hybrid;
mso-list-template-ids:-1766582054 -104953098 67698691 67698693 67698689 67=
698691 67698693 67698689 67698691 67698693;}
@list l0:level1
{mso-level-start-at:0;
mso-level-number-format:bullet;
mso-level-text:-;
mso-level-tab-stop:none;
mso-level-number-position:left;
text-indent:-.25in;
font-family:"Aptos",sans-serif;
mso-fareast-font-family:Aptos;
mso-bidi-font-family:"Times New Roman";}
@list l0:level2
{mso-level-number-format:bullet;
mso-level-text:o;
mso-level-tab-stop:none;
mso-level-number-position:left;
text-indent:-.25in;
font-family:"Courier New";}
@list l0:level3
{mso-level-number-format:bullet;
mso-level-text:\F0A7;
mso-level-tab-stop:none;
mso-level-number-position:left;
text-indent:-.25in;
font-family:Wingdings;}
@list l0:level4
{mso-level-number-format:bullet;
mso-level-text:\F0B7;
mso-level-tab-stop:none;
mso-level-number-position:left;
text-indent:-.25in;
font-family:Symbol;}
@list l0:level5
{mso-level-number-format:bullet;
mso-level-text:o;
mso-level-tab-stop:none;
mso-level-number-position:left;
text-indent:-.25in;
font-family:"Courier New";}
@list l0:level6
{mso-level-number-format:bullet;
mso-level-text:\F0A7;
mso-level-tab-stop:none;
mso-level-number-position:left;
text-indent:-.25in;
font-family:Wingdings;}
@list l0:level7
{mso-level-number-format:bullet;
mso-level-text:\F0B7;
mso-level-tab-stop:none;
mso-level-number-position:left;
text-indent:-.25in;
font-family:Symbol;}
@list l0:level8
{mso-level-number-format:bullet;
mso-level-text:o;
mso-level-tab-stop:none;
mso-level-number-position:left;
text-indent:-.25in;
font-family:"Courier New";}
@list l0:level9
{mso-level-number-format:bullet;
mso-level-text:\F0A7;
mso-level-tab-stop:none;
mso-level-number-position:left;
text-indent:-.25in;
font-family:Wingdings;}
ol
{margin-bottom:0in;}
ul
{margin-bottom:0in;}
--></style><!--[if gte mso 9]><xml>
<o:shapedefaults v:ext=3D"edit" spidmax=3D"1026" />
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext=3D"edit">
<o:idmap v:ext=3D"edit" data=3D"1" />
</o:shapelayout></xml><![endif]-->
</head>
<body lang=3D"EN-US" link=3D"#467886" vlink=3D"#96607D" style=3D"word-wrap:=
break-word">
<div class=3D"WordSection1">
<p class=3D"MsoNormal">Hi Folks,<br>
<br>
I found one use case for share interface when a next transfer using the sam=
e share handle may use abandoned active connection from the previous transf=
er which wasn’t completed.<br>
<br>
The scenario is following:<o:p></o:p></p>
<ul style=3D"margin-top:0in" type=3D"disc">
<li class=3D"MsoListParagraph" style=3D"margin-left:0in;mso-list:l0 level1 =
lfo1">Create share handle with CURL_LOCK_DATA_CONNECT option.<o:p></o:p></l=
i><li class=3D"MsoListParagraph" style=3D"margin-left:0in;mso-list:l0 level=
1 lfo1">Create easy handle and set share via
<i>CURLOPT_SHARE </i>option.<o:p></o:p></li><li class=3D"MsoListParagraph" =
style=3D"margin-left:0in;mso-list:l0 level1 lfo1">Create multi-handle and a=
dd easy handle to it.<o:p></o:p></li><li class=3D"MsoListParagraph" style=
=3D"margin-left:0in;mso-list:l0 level1 lfo1">Run the transfer using multi i=
nterface to some point where it will be abandoned (i.e. application may mak=
e this decision due to lack of space where to write incoming data etc)<o:p>=
</o:p></li><li class=3D"MsoListParagraph" style=3D"margin-left:0in;mso-list=
:l0 level1 lfo1">Set share to NULL on “abandoned” easy handle t=
o avoid using the share on that transfer further and remove it from the mul=
ti handle (via
<i>curl_multi_remove_handle()</i>).<o:p></o:p></li><li class=3D"MsoListPara=
graph" style=3D"margin-left:0in;mso-list:l0 level1 lfo1">Create a new easy =
handle, set the same share via
<i>CURLOPT_SHARE </i>option, add easy handle to the multi-handle and run it=
.<br>
<br>
<o:p></o:p></li></ul>
<p class=3D"MsoNormal">On the last step, the new transfer may reuse the con=
nection with partially received data from the previously abandoned transfer=
instead of creating a new one and get “weird server reply” err=
or.<br>
<br>
This happens because when share is set to NULL via <i>CURLOPT_SHARE </i>opt=
ion, it calls the
<i>Curl_share_easy_unlink()</i> function which just detaches the connection=
, but not closes it:<br>
<br>
CURLcode Curl_share_easy_unlink(struct Curl_easy *data)<o:p></o:p></p>
<p class=3D"MsoNormal">{<o:p></o:p></p>
<p class=3D"MsoNormal"> ...<o:p></o:p></p>
<p class=3D"MsoNormal"><o:p> </o:p></p>
<p class=3D"MsoNormal"> /* If data has a connection from =
this share, detach it. */<o:p></o:p></p>
<p class=3D"MsoNormal"> if(data->conn && (shar=
e->specifier & (1 << CURL_LOCK_DATA_CONNECT)))<o:p></o:p></p>
<p class=3D"MsoNormal"> Curl_detach_connectio=
n(data);<o:p></o:p></p>
<p class=3D"MsoNormal"> ...<o:p></o:p></p>
<p class=3D"MsoNormal">}<br>
<br>
So, when <i>curl_multi_remove_handle() </i>is called later, it doesn’=
t close the connection for the premature finished transfer because the conn=
ection was detached,<br>
and thus active connection may be taken from the connection pool own by the=
share and wrongly reused.<br>
<br>
<o:p></o:p></p>
<p class=3D"MsoNormal">I think the <i>Curl_share_easy_unlink()</i> should c=
lose the active connection if it is called on some prematurely abandoned tr=
ansfer.<o:p></o:p></p>
<p class=3D"MsoNormal"><br>
I discovered this on the later libcurl versions (8.19+), the older versions=
like 8.6.0 which I used before don’t have this issue, so it may be p=
erceived as a regression from the earlier builds.<o:p></o:p></p>
<p class=3D"MsoNormal"><br>
It is interesting that sometimes libcurl may avoid reusing the connection i=
n such cases as it detects it as “dead” even though it doesn=
217;t close it explicitly,<br>
which allowed my connection sharing to pass in some cases and suspect flaki=
ness.<br>
<br>
<o:p></o:p></p>
<p class=3D"MsoNormal">Thanks!<br>
Dmitry Karpov<o:p></o:p></p>
</div>
</body>
</html>
--_000_DM6PR01MB5611B1CEDB14F99AC1F81E1DC3F62DM6PR01MB5611prod_--
--===============5725668390809538735==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
--
Unsubscribe: https://lists.haxx.se/mailman/listinfo/curl-library
Etiquette: https://curl.se/mail/etiquette.html
--===============5725668390809538735==--