]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolve: fix use after free in DnsAnswer
authorYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 5 Jan 2021 06:27:11 +0000 (15:27 +0900)
committerFrantisek Sumsal <frantisek@sumsal.cz>
Fri, 8 Jan 2021 18:06:00 +0000 (19:06 +0100)
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

index f5e50fcd844284e64ec1556a4854115ca96e1763..a2878ec2bb87127eb4d665468addedc2bb7748cd 100644 (file)
@@ -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)