]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: block transaction GC'ing while dns_transaction_request_dnssec_keys() is...
authorLennart Poettering <lennart@poettering.net>
Mon, 4 Jan 2016 21:22:47 +0000 (22:22 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 4 Jan 2016 21:42:10 +0000 (22:42 +0100)
If any of the transactions started by
dns_transaction_request_dnssec_keys() finishes promptly without
requiring asynchronous operation this is reported back to the issuing
transaction from the same stackframe. This might ultimately result in
this transaction to be freed while we are still in its
_request_dnssec_keys() stack frame. To avoid memory corruption block the
transaction GC while in the call, and manually issue a GC after it
returned.

src/resolve/resolved-dns-transaction.c
src/resolve/resolved-dns-transaction.h

index 5fe92d701a35e4c49b5da14fde580d440a71b9c5..c0626626f29ed87284515525f09e8cd3c8ffecf6 100644 (file)
@@ -64,6 +64,8 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) {
         if (!t)
                 return NULL;
 
+        log_debug("Freeing transaction %" PRIu16 ".", t->id);
+
         dns_transaction_close_connection(t);
         dns_transaction_stop_timeout(t);
 
@@ -108,16 +110,20 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) {
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsTransaction*, dns_transaction_free);
 
-void dns_transaction_gc(DnsTransaction *t) {
+bool dns_transaction_gc(DnsTransaction *t) {
         assert(t);
 
         if (t->block_gc > 0)
-                return;
+                return true;
 
         if (set_isempty(t->notify_query_candidates) &&
             set_isempty(t->notify_zone_items) &&
-            set_isempty(t->notify_transactions))
+            set_isempty(t->notify_transactions)) {
                 dns_transaction_free(t);
+                return false;
+        }
+
+        return true;
 }
 
 int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) {
@@ -721,7 +727,22 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
                 t->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
                 t->answer_authenticated = false;
 
+                /* Block GC while starting requests for additional DNSSEC RRs */
+                t->block_gc++;
                 r = dns_transaction_request_dnssec_keys(t);
+                t->block_gc--;
+
+                /* Maybe the transaction is ready for GC'ing now? If so, free it and return. */
+                if (!dns_transaction_gc(t))
+                        return;
+
+                /* Requesting additional keys might have resulted in
+                 * this transaction to fail, since the auxiliary
+                 * request failed for some reason. If so, we are not
+                 * in pending state anymore, and we should exit
+                 * quickly. */
+                if (t->state != DNS_TRANSACTION_PENDING)
+                        return;
                 if (r < 0) {
                         dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
                         return;
index faf3ce6fb9819238c00866d2a505581d3791ad95..21e453218a4cd5167aded7c4ada0e0a69c2ff25d 100644 (file)
@@ -140,7 +140,7 @@ struct DnsTransaction {
 int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key);
 DnsTransaction* dns_transaction_free(DnsTransaction *t);
 
-void dns_transaction_gc(DnsTransaction *t);
+bool dns_transaction_gc(DnsTransaction *t);
 int dns_transaction_go(DnsTransaction *t);
 
 void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p);