fix(curl): synchronize shared peer and credential references

Protect counts shared by detached easy handles and cached connections with curl platform locks. Decide the last release under the same lock without holding it across allocation, destruction, or callbacks.
This commit is contained in:
ldm0
2026-11-03 18:08:21 +08:00
parent 3d1d97cbc5
commit 3e2aabd081
3 changed files with 54 additions and 16 deletions
+4 -8
View File
@@ -29,6 +29,7 @@
#include "curl_trc.h"
#include "strcase.h"
#include "urldata.h"
#include "moli_refcount.h"
CURLcode Curl_creds_create(const char *user,
@@ -132,10 +133,8 @@ void Curl_creds_link(struct Curl_creds **pdest, struct Curl_creds *src)
if(*pdest != src) {
Curl_creds_unlink(pdest);
*pdest = src;
if(src) {
DEBUGASSERT(src->refcount < UINT32_MAX);
src->refcount++;
}
if(src)
moli_refcount_link(&src->refcount);
}
}
@@ -144,11 +143,8 @@ void Curl_creds_unlink(struct Curl_creds **pcreds)
if(*pcreds) {
struct Curl_creds *creds = *pcreds;
DEBUGASSERT(creds->refcount);
*pcreds = NULL;
if(creds->refcount)
creds->refcount--;
if(!creds->refcount) {
if(moli_refcount_unlink(&creds->refcount)) {
curlx_memzero(creds, sizeof(*creds) + creds->bufsize);
curlx_free(creds);
}
+46
View File
@@ -0,0 +1,46 @@
#ifndef HEADER_CURL_MOLI_REFCOUNT_H
#define HEADER_CURL_MOLI_REFCOUNT_H
/* SPDX-License-Identifier: curl */
#include "easy_lock.h"
/* A removed easy may be cleaned up while its former multi reuses a cached
* connection. Both retain peers and credentials. Protect their counts even
* though the application never uses the same easy from two threads.
*
* Each including translation unit owns one object family and its lock. Only
* the count is protected; no allocation, destruction or curl callbacks run
* under the lock. Use curl's existing platform lock and single-thread fallback.
*/
#ifdef GLOBAL_INIT_IS_THREADSAFE
static curl_simple_lock moli_refcount_lock = CURL_SIMPLE_LOCK_INIT;
#endif
static CURL_INLINE void moli_refcount_link(uint32_t *count)
{
#ifdef GLOBAL_INIT_IS_THREADSAFE
curl_simple_lock_lock(&moli_refcount_lock);
#endif
DEBUGASSERT(*count && *count < UINT32_MAX);
++*count;
#ifdef GLOBAL_INIT_IS_THREADSAFE
curl_simple_lock_unlock(&moli_refcount_lock);
#endif
}
static CURL_INLINE bool moli_refcount_unlink(uint32_t *count)
{
bool last;
#ifdef GLOBAL_INIT_IS_THREADSAFE
curl_simple_lock_lock(&moli_refcount_lock);
#endif
DEBUGASSERT(*count);
last = !--*count;
#ifdef GLOBAL_INIT_IS_THREADSAFE
curl_simple_lock_unlock(&moli_refcount_lock);
#endif
/* Do not read the count again: another owner may already have freed it. */
return last;
}
#endif /* HEADER_CURL_MOLI_REFCOUNT_H */
+4 -8
View File
@@ -76,6 +76,7 @@
#include "url.h"
#include "urlapi-int.h"
#include "vtls/vtls.h"
#include "moli_refcount.h"
struct peer_parse {
const struct Curl_scheme *scheme;
@@ -296,10 +297,8 @@ void Curl_peer_link(struct Curl_peer **pdest, struct Curl_peer *src)
if(*pdest != src) {
Curl_peer_unlink(pdest);
*pdest = src;
if(src) {
DEBUGASSERT(src->refcount < UINT32_MAX);
src->refcount++;
}
if(src)
moli_refcount_link(&src->refcount);
}
}
@@ -308,11 +307,8 @@ void Curl_peer_unlink(struct Curl_peer **ppeer)
if(*ppeer) {
struct Curl_peer *peer = *ppeer;
DEBUGASSERT(peer->refcount);
*ppeer = NULL;
if(peer->refcount)
peer->refcount--;
if(!peer->refcount) {
if(moli_refcount_unlink(&peer->refcount)) {
curlx_free(peer);
}
}