]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: fix braino with reference counting and linked lists
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Fri, 14 May 2021 08:49:24 +0000 (10:49 +0200)
committerLuca Boccassi <luca.boccassi@gmail.com>
Fri, 14 May 2021 22:18:10 +0000 (23:18 +0100)
In 0e0fd08fc832b8f42e567d722d388eba086da5ff I added reference counts to keep
track of the DnsQueryCandidate objects. Unfortunately, dns_query_unref_candidates()
was written as

     while (q->candidates)
           dns_query_candidate_unref(q->candidates);

i.e. it would keep dropping the reference count as many times as needed for it
to hit 0, making the patch less than fully effective.

dns_query_unref_candidates() is renamed to dns_query_detach_candidates() and
changed to drop exactly one reference from each of the linked candidates.

Example failure:
==463== Invalid read of size 8
==463==    at 0x419C93: dns_query_candidate_go (resolved-dns-query.c:159)
==463==    by 0x41A143: dns_query_candidate_notify (resolved-dns-query.c:304)
==463==    by 0x434BD6: dns_transaction_complete (resolved-dns-transaction.c:437)
==463==    by 0x436A0F: dns_transaction_process_dnssec (resolved-dns-transaction.c:976)
==463==    by 0x4378C1: dns_transaction_process_reply (resolved-dns-transaction.c:1387)
==463==    by 0x437CE9: on_dns_packet (resolved-dns-transaction.c:1444)
==463==    by 0x4B2DC9B: source_dispatch (sd-event.c:3512)
==463==    by 0x4B2FB1F: sd_event_dispatch (sd-event.c:4077)
==463==    by 0x4B2FFFA: sd_event_run (sd-event.c:4138)
==463==    by 0x4B301D6: sd_event_loop (sd-event.c:4159)
==463==    by 0x464A24: run (resolved.c:92)
==463==    by 0x464B3C: main (resolved.c:99)
==463==  Address 0x5f409d0 is 32 bytes inside a block of size 72 free'd
==463==    at 0x48410E4: free (vg_replace_malloc.c:755)
==463==    by 0x418EDF: mfree (alloc-util.h:48)
==463==    by 0x4197E8: dns_query_candidate_free (resolved-dns-query.c:67)
==463==    by 0x4198B7: dns_query_candidate_unref (resolved-dns-query.c:70)
==463==    by 0x41A2E3: dns_query_unref_candidates (resolved-dns-query.c:337)
==463==    by 0x41C5FE: dns_query_cname_redirect (resolved-dns-query.c:1028)
==463==    by 0x41CA04: dns_query_process_cname_one (resolved-dns-query.c:1128)
==463==    by 0x41CA80: dns_query_process_cname_many (resolved-dns-query.c:1157)
==463==    by 0x40C0BD: bus_method_resolve_hostname_complete (resolved-bus.c:198)
==463==    by 0x41B312: dns_query_complete (resolved-dns-query.c:562)
==463==    by 0x41C1AC: dns_query_accept (resolved-dns-query.c:922)
==463==    by 0x41C2C4: dns_query_ready (resolved-dns-query.c:955)
==463==    by 0x41A162: dns_query_candidate_notify (resolved-dns-query.c:314)
==463==    by 0x434BD6: dns_transaction_complete (resolved-dns-transaction.c:437)
==463==    by 0x438995: dns_transaction_prepare (resolved-dns-transaction.c:1728)
==463==    by 0x43921D: dns_transaction_go (resolved-dns-transaction.c:1928)
==463==    by 0x419C7C: dns_query_candidate_go (resolved-dns-query.c:163)
==463==    by 0x41A143: dns_query_candidate_notify (resolved-dns-query.c:304)
==463==    by 0x434BD6: dns_transaction_complete (resolved-dns-transaction.c:437)
==463==    by 0x436A0F: dns_transaction_process_dnssec (resolved-dns-transaction.c:976)
==463==    by 0x4378C1: dns_transaction_process_reply (resolved-dns-transaction.c:1387)
==463==    by 0x437CE9: on_dns_packet (resolved-dns-transaction.c:1444)
==463==    by 0x4B2DC9B: source_dispatch (sd-event.c:3512)
==463==    by 0x4B2FB1F: sd_event_dispatch (sd-event.c:4077)
==463==    by 0x4B2FFFA: sd_event_run (sd-event.c:4138)
==463==    by 0x4B301D6: sd_event_loop (sd-event.c:4159)
==463==    by 0x464A24: run (resolved.c:92)
==463==    by 0x464B3C: main (resolved.c:99)
==463==  Block was alloc'd at
==463==    at 0x483E86F: malloc (vg_replace_malloc.c:380)
==463==    by 0x418F81: malloc_multiply (alloc-util.h:96)
==463==    by 0x419378: dns_query_candidate_new (resolved-dns-query.c:23)
==463==    by 0x41B42C: dns_query_add_candidate (resolved-dns-query.c:582)
==463==    by 0x41BB7A: dns_query_go (resolved-dns-query.c:762)
==463==    by 0x40CE3A: bus_method_resolve_hostname (resolved-bus.c:464)
==463==    by 0x4A84B86: method_callbacks_run (bus-objects.c:414)
==463==    by 0x4A87961: object_find_and_run (bus-objects.c:1323)
==463==    by 0x4A87FEE: bus_process_object (bus-objects.c:1443)
==463==    by 0x4AA3434: process_message (sd-bus.c:2964)
==463==    by 0x4AA3623: process_running (sd-bus.c:3006)
==463==    by 0x4AA4110: bus_process_internal (sd-bus.c:3226)
==463==    by 0x4AA41EF: sd_bus_process (sd-bus.c:3253)
==463==    by 0x4AA5343: io_callback (sd-bus.c:3604)
==463==    by 0x4B2DC9B: source_dispatch (sd-event.c:3512)
==463==    by 0x4B2FB1F: sd_event_dispatch (sd-event.c:4077)
==463==    by 0x4B2FFFA: sd_event_run (sd-event.c:4138)
==463==    by 0x4B301D6: sd_event_loop (sd-event.c:4159)
==463==    by 0x464A24: run (resolved.c:92)
==463==    by 0x464B3C: main (resolved.c:99)

Fixes #19376.

src/resolve/resolved-dns-query.c

index 5517db149d0827465686c1f8d09b5b4e596df751..2a806e48d3aa39b145f2b5e28ee6943f7e7a00c9 100644 (file)
@@ -42,6 +42,8 @@ static void dns_query_candidate_stop(DnsQueryCandidate *c) {
 
         assert(c);
 
+        /* Detach all the DnsTransactions attached to this query */
+
         while ((t = set_steal_first(c->transactions))) {
                 set_remove(t->notify_query_candidates, c);
                 set_remove(t->notify_query_candidates_done, c);
@@ -49,21 +51,34 @@ static void dns_query_candidate_stop(DnsQueryCandidate *c) {
         }
 }
 
+static DnsQueryCandidate* dns_query_candidate_unlink(DnsQueryCandidate *c) {
+        assert(c);
+
+        /* Detach this DnsQueryCandidate from the Query and Scope objects */
+
+        if (c->query) {
+                LIST_REMOVE(candidates_by_query, c->query->candidates, c);
+                c->query = NULL;
+        }
+
+        if (c->scope) {
+                LIST_REMOVE(candidates_by_scope, c->scope->query_candidates, c);
+                c->scope = NULL;
+        }
+
+        return c;
+}
+
 static DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c) {
         if (!c)
                 return NULL;
 
         dns_query_candidate_stop(c);
+        dns_query_candidate_unlink(c);
 
         set_free(c->transactions);
         dns_search_domain_unref(c->search_domain);
 
-        if (c->query)
-                LIST_REMOVE(candidates_by_query, c->query->candidates, c);
-
-        if (c->scope)
-                LIST_REMOVE(candidates_by_scope, c->scope->query_candidates, c);
-
         return mfree(c);
 }
 
@@ -105,6 +120,7 @@ static int dns_query_candidate_add_transaction(
         int r;
 
         assert(c);
+        assert(c->query); /* We shan't add transactions to a candidate that has been detached already */
 
         if (key) {
                 /* Regular lookup with a resource key */
@@ -223,6 +239,7 @@ static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) {
         int n = 0, r;
 
         assert(c);
+        assert(c->query); /* We shan't add transactions to a candidate that has been detached already */
 
         dns_query_candidate_stop(c);
 
@@ -280,6 +297,9 @@ void dns_query_candidate_notify(DnsQueryCandidate *c) {
 
         assert(c);
 
+        if (!c->query) /* This candidate has been abandoned, do nothing. */
+                return;
+
         state = dns_query_candidate_state(c);
 
         if (DNS_TRANSACTION_IS_LIVE(state))
@@ -330,11 +350,13 @@ static void dns_query_stop(DnsQuery *q) {
                 dns_query_candidate_stop(c);
 }
 
-static void dns_query_unref_candidates(DnsQuery *q) {
+static void dns_query_unlink_candidates(DnsQuery *q) {
         assert(q);
 
         while (q->candidates)
-                dns_query_candidate_unref(q->candidates);
+                /* Here we drop *our* references to each of the candidates. If we had the only reference, the
+                 * DnsQueryCandidate object will be freed. */
+                dns_query_candidate_unref(dns_query_candidate_unlink(q->candidates));
 }
 
 static void dns_query_reset_answer(DnsQuery *q) {
@@ -364,7 +386,7 @@ DnsQuery *dns_query_free(DnsQuery *q) {
                 LIST_REMOVE(auxiliary_queries, q->auxiliary_for->auxiliary_queries, q);
         }
 
-        dns_query_unref_candidates(q);
+        dns_query_unlink_candidates(q);
 
         dns_question_unref(q->question_idna);
         dns_question_unref(q->question_utf8);
@@ -1025,7 +1047,7 @@ static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname)
         dns_question_unref(q->question_utf8);
         q->question_utf8 = TAKE_PTR(nq_utf8);
 
-        dns_query_unref_candidates(q);
+        dns_query_unlink_candidates(q);
 
         /* Note that we do *not* reset the answer here, because the answer we previously got might already
          * include everything we need, let's check that first */