Fix two c-ares resource leaks in SRV support - #3666
Open
szarta wants to merge 2 commits into
Open
Conversation
On a successful SRV lookup, ares_parse_srv_reply() allocates a struct ares_srv_reply list into reply, which srv_callback() consumes but never releases. c-ares requires this to be freed with ares_free_data(), which appears nowhere in the tree, so every successful SRV-based connection (mosquitto_connect_srv) leaks the reply list. Free reply with ares_free_data() once consumed. reply is initialised to NULL and ares_free_data(NULL) is a no-op, so this is safe on the parse-failure sub-branch as well. WITH_SRV builds only. Signed-off-by: Brandon Arrendondo <barrendo@gmail.com>
mosquitto_connect_srv() initialises a c-ares resolver channel with ares_init(&mosq->achan), but mosquitto__destroy() never releases it and ares_destroy() is called nowhere in the library. Every client that uses SRV-based connection leaks the entire c-ares channel (tens of KB) until process exit; an application that repeatedly creates and destroys SRV clients leaks it each time. Destroy the channel in mosquitto__destroy() when it has been initialised. mosq is calloc'd so achan is NULL when SRV was never used, making the guard safe. WITH_SRV builds only. Signed-off-by: Brandon Arrendondo <barrendo@gmail.com>
szarta
force-pushed
the
fix/srv-ares-resource-cleanup
branch
from
June 16, 2026 01:25
1905a30 to
bf1b735
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two related memory leaks in SRV (
WITH_SRV) support, both reproduced andfixed:
srv_callback()never frees thestruct ares_srv_replylist returned byares_parse_srv_reply().mosq->achancreated bymosquitto_connect_srv()(ares_init) is never released;ares_destroy()is called nowhere in the library.
Two commits, one per file (
lib/srv_mosq.c,lib/libmosquitto.c).Bug 1 — reply list leaked in
srv_callback(lib/srv_mosq.c)ares_parse_srv_reply()allocates a list intoreplythat the c-ares APIrequires the caller to release with
ares_free_data(); it is never freed(
ares_free_dataappears nowhere in the tree). Every successful SRV lookupleaks the reply list.
Bug 2 — c-ares channel leaked (
lib/libmosquitto.c)mosquitto_connect_srv()doesares_init(&mosq->achan). The channel is used bythe event loop (
ares_fds/ares_processinlib/loop.c) butares_destroy()is never called, and
mosquitto__destroy()does not touchachan. Every clientthat uses SRV connection leaks the whole channel (tens of KB) until process
exit; repeatedly creating/destroying SRV clients leaks one channel each.
Reproduction
WITH_SRVbuild, a resolvable_mqtt._tcp.<domain>SRV record, and a minimalclient that calls
mosquitto_connect_srv()then pumps the loop so the resolvercallback fires:
Validation (valgrind, c-ares 1.18.1)
Fixes
lib/srv_mosq.c— free the reply once consumed (replyis NULL-initialised andares_free_data(NULL)is a no-op, safe on the parse-failure sub-branch):if(status == ARES_SUCCESS){ mosquitto_connect(mosq, reply->host, reply->port, mosq->keepalive); } + ares_free_data(reply); }else{lib/libmosquitto.c— destroy the channel inmosquitto__destroy()(mosq iscalloc'd, soachanis NULL when SRV was unused;<ares.h>is alreadyincluded under
WITH_SRV):if(!mosq){ return; } +#ifdef WITH_SRV + if(mosq->achan){ + ares_destroy(mosq->achan); + mosq->achan = NULL; + } +#endifDisclosure: these fixes were identified with the help of AI-assisted static
analysis and the description was drafted with AI assistance. Both defects were
reproduced and validated under valgrind (output above), and I have reviewed and
verified the changes myself and sign off on them under the DCO/ECA.