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&#8217;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 &#8220;abandoned&#8221; 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 &#8220;weird server reply&#8221; 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">&nbsp;&nbsp; ...<o:p></o:p></p>
<p class=3D"MsoNormal"><o:p>&nbsp;</o:p></p>
<p class=3D"MsoNormal">&nbsp;&nbsp;&nbsp; /* If data has a connection from =
this share, detach it. */<o:p></o:p></p>
<p class=3D"MsoNormal">&nbsp;&nbsp;&nbsp; if(data-&gt;conn &amp;&amp; (shar=
e-&gt;specifier &amp; (1 &lt;&lt; CURL_LOCK_DATA_CONNECT)))<o:p></o:p></p>
<p class=3D"MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Curl_detach_connectio=
n(data);<o:p></o:p></p>
<p class=3D"MsoNormal">&nbsp;&nbsp; ...<o:p></o:p></p>
<p class=3D"MsoNormal">}<br>
<br>
So, when <i>curl_multi_remove_handle() </i>is called later, it doesn&#8217;=
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&#8217;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 &#8220;dead&#8221; even though it doesn&#8=
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==--