From 0c2c0fd256d2be652910d65e5a4aacc08c240714 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 5 Jan 2021 15:27:11 +0900 Subject: [PATCH] resolve: fix use after free in DnsAnswer This fixes a bug introduced by ae45e1a3832fbb6c96707687e42f0b4aaab52c9b. The set DnsAnswer::set_items contains the reference to the array in DnsAnswer. So, the set must be reconstructed when we realloc() the object. Fixes #18132. --- src/resolve/resolved-dns-answer.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c index f5e50fcd84..a2878ec2bb 100644 --- a/src/resolve/resolved-dns-answer.c +++ b/src/resolve/resolved-dns-answer.c @@ -664,6 +664,7 @@ int dns_answer_reserve(DnsAnswer **a, size_t n_free) { if (*a) { size_t ns; + int r; if ((*a)->n_ref > 1) return -EBUSY; @@ -680,11 +681,23 @@ int dns_answer_reserve(DnsAnswer **a, size_t n_free) { if (ns > UINT16_MAX) ns = UINT16_MAX; + /* This must be done before realloc() below. Otherwise, the original DnsAnswer object + * may be broken. */ + r = set_reserve((*a)->set_items, ns); + if (r < 0) + return r; + n = realloc(*a, offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * ns); if (!n) return -ENOMEM; n->n_allocated = ns; + + /* Previously all items are stored in the set, and the enough memory area is allocated + * in the above. So set_put() in the below cannot fail. */ + set_clear(n->set_items); + for (size_t i = 0; i < n->n_rrs; i++) + assert_se(set_put(n->set_items, &n->items[i]) > 0); } else { n = dns_answer_new(n_free); if (!n)