summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2021-05-14 10:49:24 +0200
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2021-05-15 20:04:55 +0200
commitc8d7fab2286384b19ff6328cece107c4c02d7bb8 (patch)
tree08710052c242c4dcfae90ee15ea653807d9e5437
parentdb85ea172f9b4e6cd2f46bffd63164a09404001f (diff)
downloadsystemd-c8d7fab2286384b19ff6328cece107c4c02d7bb8.tar.gz
resolved: fix braino with reference counting and linked lists
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. (cherry picked from commit c856ef0457c35e9edfdbf085b69ec81c126d48e5) (cherry picked from commit 89324e233eef767334d9bfe5eed96956c973c2ad)
-rw-r--r--src/resolve/resolved-dns-query.c42
1 files changed, 32 insertions, 10 deletions
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
index 9d3aa677a0..65a0a73cb8 100644
--- a/src/resolve/resolved-dns-query.c
+++ b/src/resolve/resolved-dns-query.c
@@ -43,6 +43,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);
@@ -50,21 +52,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);
}
@@ -103,6 +118,7 @@ static int dns_query_candidate_add_transaction(DnsQueryCandidate *c, DnsResource
assert(c);
assert(key);
+ assert(c->query); /* We shan't add transactions to a candidate that has been detached already */
t = dns_scope_find_transaction(c->scope, key, true);
if (!t) {
@@ -211,6 +227,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);
@@ -253,6 +270,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))
@@ -303,11 +323,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) {
@@ -336,7 +358,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);
@@ -932,7 +954,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);
dns_query_reset_answer(q);
q->state = DNS_TRANSACTION_NULL;