summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2016-01-04 22:22:47 +0100
committerLennart Poettering <lennart@poettering.net>2016-01-04 22:42:10 +0100
commit51e399bcebefb27d6b147d90de84d07f010fa170 (patch)
tree7c20b6da45f363454de9fa89beac189cfeb71efe /src
parent3347e6a299983557d27b7ff950199f2eda6d9bd9 (diff)
resolved: block transaction GC'ing while dns_transaction_request_dnssec_keys() is running
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.
Diffstat (limited to 'src')
-rw-r--r--src/resolve/resolved-dns-transaction.c27
-rw-r--r--src/resolve/resolved-dns-transaction.h2
2 files changed, 25 insertions, 4 deletions
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 5fe92d701a..c0626626f2 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -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;
diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h
index faf3ce6fb9..21e453218a 100644
--- a/src/resolve/resolved-dns-transaction.h
+++ b/src/resolve/resolved-dns-transaction.h
@@ -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);