From: Luca Boccassi Date: Mon, 13 Jul 2026 14:04:35 +0000 (+0100) Subject: dns-answer: preserve shared aliases when removing records X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=e509a6a4cb9846c9337f60b019b3d29a2bc8f7a3;p=thirdparty%2Fsystemd.git dns-answer: preserve shared aliases when removing records dns_answer_remove_by_rr() mutats the backing OrderedSet even when the answer has multiple references. Callers such as trust-anchor revocation could thus change snapshots held elsewhere in the resolver. Clone a shared answer before removal. Follow-up for 71aee23dba7faeef68e7232f444626267a6c90d7 --- diff --git a/src/resolve/test-dns-answer.c b/src/resolve/test-dns-answer.c index 2454ca1c06b..aea1ebcc3c3 100644 --- a/src/resolve/test-dns-answer.c +++ b/src/resolve/test-dns-answer.c @@ -371,6 +371,23 @@ TEST(dns_answer_remove_by_rr) { ASSERT_NULL(answer); } +TEST(dns_answer_remove_shared) { + _cleanup_(dns_answer_unrefp) DnsAnswer *answer = prepare_answer(), *alias = NULL; + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + alias = dns_answer_ref(answer); + ASSERT_PTR_EQ(alias, answer); + + rr = ASSERT_NOT_NULL(dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "b.example.com")); + rr->a.in_addr.s_addr = htobe32(0xc0a8017f); + + ASSERT_OK_POSITIVE(dns_answer_remove_by_rr(&answer, rr)); + ASSERT_EQ(dns_answer_size(answer), 2u); + ASSERT_EQ(dns_answer_size(alias), 3u); + ASSERT_FALSE(dns_answer_contains(answer, rr)); + ASSERT_TRUE(dns_answer_contains(alias, rr)); +} + TEST(dns_answer_remove_by_answer_keys) { _cleanup_(dns_answer_unrefp) DnsAnswer *a = prepare_answer(), *b = prepare_answer(); DnsResourceKey *key; diff --git a/src/shared/dns-answer.c b/src/shared/dns-answer.c index 24174d2bd2c..037ad207eff 100644 --- a/src/shared/dns-answer.c +++ b/src/shared/dns-answer.c @@ -555,14 +555,28 @@ int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rr) { if (r < 0) return r; if (r > 0) { - dns_answer_item_unref(ordered_set_remove((*a)->items, item)); found = true; + break; } } if (!found) return 0; + if ((*a)->n_ref > 1) { + r = dns_answer_reserve_or_clone(a, 1); + if (r < 0) + return r; + } + + DNS_ANSWER_FOREACH_ITEM(item, *a) { + r = dns_resource_record_equal(item->rr, rr); + if (r < 0) + return r; + if (r > 0) + dns_answer_item_unref(ordered_set_remove((*a)->items, item)); + } + if (dns_answer_isempty(*a)) *a = dns_answer_unref(*a); /* Return NULL for the empty answer */