resolve: fix use-after-free of the service browser in mDNS maintenance

mdns_maintenance_query_complete() takes a reference on
query->dnsservice_request and never drops it, so every successful mDNS
maintenance query leaks one reference to the discovered service.

The leak alone would be benign, but the service owns the one-shot event
source that drives its maintenance schedule, and it only borrows a plain
pointer to its service browser. Once the extra reference is outstanding,
dns_service_browser_free() -> dns_remove_service() only drops the list's
reference, so dns_service_free() never runs, the maintenance timer is
never disabled, and the service outlives the browser it points at. The
next time the timer fires, mdns_maintenance_query() dereferences the
freed DnsServiceBrowser to reach the manager's event loop and to issue
the query.

Reaching it only takes a browse subscription whose record lives long
enough for one maintenance query to be answered, followed by the
subscription going away.

Make the reference a _cleanup_ one, the way the sibling
mdns_browse_service_query_complete() below already handles the reference
it takes on the service browser.
This commit is contained in:
Armaan Sandhu
2026-09-03 06:37:53 +09:00
committed by Yu Watanabe
parent a019396d2c
commit 2451b1a9c4
+1 -1
View File
@@ -79,9 +79,9 @@ static usec_t mdns_maintenance_jitter(uint32_t ttl) {
}
static void mdns_maintenance_query_complete(DnsQuery *q) {
_cleanup_(dnssd_discovered_service_unrefp) DnssdDiscoveredService *service = NULL;
_cleanup_(dns_service_browser_unrefp) DnsServiceBrowser *sb = NULL;
_cleanup_(dns_query_freep) DnsQuery *query = q;
DnssdDiscoveredService *service = NULL;
int r;
assert(query);