doh: fix cleanup

When removing an easy handle that had DoH sub-easy handles going, those
were not removed from the multi handle. Their memory was reclaimed on
curl_easy_cleanup() of the owning handle, but multi still had them in
their list.

Add `Curl_doh_close()` and `Curl_doh_cleanup()` as common point for
handling the DoH resource management. Use the `multi` present in the doh
handles (if so), for removal, as the `data->multi` might already have
been NULLed at this time.

Reported-by: 罗朝辉
Fixes #14207
Closes #14212
This commit is contained in:
Stefan Eissing 2024-07-17 14:46:47 +02:00 committed by Daniel Stenberg
parent 5eba0a4b37
commit d8696dc8c0
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
4 changed files with 37 additions and 24 deletions

View File

@ -400,7 +400,6 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
int *waitp)
{
CURLcode result = CURLE_OK;
int slot;
struct dohdata *dohp;
struct connectdata *conn = data->conn;
#ifdef USE_HTTPSRR
@ -484,13 +483,7 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
return NULL;
error:
curl_slist_free_all(dohp->headers);
data->req.doh->headers = NULL;
for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
(void)curl_multi_remove_handle(data->multi, dohp->probe[slot].easy);
Curl_close(&dohp->probe[slot].easy);
}
Curl_safefree(data->req.doh);
Curl_doh_cleanup(data);
return NULL;
}
@ -1325,10 +1318,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
struct dohentry de;
int slot;
/* remove DoH handles from multi handle and close them */
for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
curl_multi_remove_handle(data->multi, dohp->probe[slot].easy);
Curl_close(&dohp->probe[slot].easy);
}
Curl_doh_close(data);
/* parse the responses, create the struct and return it! */
de_init(&de);
for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
@ -1415,4 +1405,32 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
return CURLE_OK;
}
void Curl_doh_close(struct Curl_easy *data)
{
struct dohdata *doh = data->req.doh;
if(doh) {
size_t slot;
for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
if(!doh->probe[slot].easy)
continue;
/* data->multi might already be reset at this time */
if(doh->probe[slot].easy->multi)
curl_multi_remove_handle(doh->probe[slot].easy->multi,
doh->probe[slot].easy);
Curl_close(&doh->probe[slot].easy);
}
}
}
void Curl_doh_cleanup(struct Curl_easy *data)
{
struct dohdata *doh = data->req.doh;
if(doh) {
Curl_doh_close(data);
curl_slist_free_all(doh->headers);
data->req.doh->headers = NULL;
Curl_safefree(data->req.doh);
}
}
#endif /* CURL_DISABLE_DOH */

View File

@ -140,6 +140,8 @@ struct dohentry {
#endif
};
void Curl_doh_close(struct Curl_easy *data);
void Curl_doh_cleanup(struct Curl_easy *data);
#ifdef UNITTESTS
UNITTEST DOHcode doh_encode(const char *host,

View File

@ -2799,6 +2799,9 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
/* First remove all remaining easy handles */
data = multi->easyp;
while(data) {
if(!GOOD_EASY_HANDLE(data))
return CURLM_BAD_HANDLE;
nextdata = data->next;
if(!data->state.done && data->conn)
/* if DONE was never called for this handle */

View File

@ -116,10 +116,7 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data)
Curl_bufq_reset(&req->sendbuf);
#ifndef CURL_DISABLE_DOH
if(req->doh) {
Curl_close(&req->doh->probe[0].easy);
Curl_close(&req->doh->probe[1].easy);
}
Curl_doh_close(data);
#endif
/* Can no longer memset() this struct as we need to keep some state */
req->size = -1;
@ -173,14 +170,7 @@ void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data)
Curl_client_cleanup(data);
#ifndef CURL_DISABLE_DOH
if(req->doh) {
Curl_close(&req->doh->probe[0].easy);
Curl_close(&req->doh->probe[1].easy);
Curl_dyn_free(&req->doh->probe[0].serverdoh);
Curl_dyn_free(&req->doh->probe[1].serverdoh);
curl_slist_free_all(req->doh->headers);
Curl_safefree(req->doh);
}
Curl_doh_cleanup(data);
#endif
}