From d3f5b5d5ab7d5b8288722c1dd8dfc69de04ae966 Mon Sep 17 00:00:00 2001 From: Mike Gelfand Date: Thu, 31 Dec 2015 14:17:37 +0000 Subject: [PATCH] #5891: Fix crash on session shutdown (evdns_getaddrinfo_cancel) --- libtransmission/announcer-udp.c | 16 ++++++---- libtransmission/session.c | 53 +++++++++++++++++++++++++-------- libtransmission/trevent.c | 2 ++ 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/libtransmission/announcer-udp.c b/libtransmission/announcer-udp.c index a09e8c392..3b7c41341 100644 --- a/libtransmission/announcer-udp.c +++ b/libtransmission/announcer-udp.c @@ -451,10 +451,10 @@ static void tau_tracker_upkeep (struct tau_tracker *); static void tau_tracker_free (struct tau_tracker * t) { + assert (t->dns_request == NULL); + if (t->addr) evutil_freeaddrinfo (t->addr); - if (t->dns_request != NULL) - evdns_getaddrinfo_cancel (t->dns_request); tr_ptrArrayDestruct (&t->announces, (PtrArrayForeachFunc)tau_announce_request_free); tr_ptrArrayDestruct (&t->scrapes, (PtrArrayForeachFunc)tau_scrape_request_free); tr_free (t->host); @@ -500,7 +500,7 @@ tau_tracker_on_dns (int errcode, struct evutil_addrinfo *addr, void * vtracker) if (errcode) { char * errmsg = tr_strdup_printf (_("DNS Lookup failed: %s"), - evdns_err_to_string (errcode)); + evutil_gai_strerror (errcode)); dbgmsg (tracker->key, "%s", errmsg); tau_tracker_fail_all (tracker, false, false, errmsg); tr_free (errmsg); @@ -654,16 +654,18 @@ static bool tau_tracker_is_idle (const struct tau_tracker * tracker) { return tr_ptrArrayEmpty (&tracker->announces) - && tr_ptrArrayEmpty (&tracker->scrapes); + && tr_ptrArrayEmpty (&tracker->scrapes) + && tracker->dns_request == NULL; } static void tau_tracker_upkeep (struct tau_tracker * tracker) { const time_t now = tr_time (); + const bool closing = tracker->close_at != 0; /* if the address info is too old, expire it */ - if (tracker->addr && (tracker->addr_expiration_time <= now)) { + if (tracker->addr != NULL && (closing || tracker->addr_expiration_time <= now)) { dbgmsg (tracker->host, "Expiring old DNS result"); evutil_freeaddrinfo (tracker->addr); tracker->addr = NULL; @@ -674,7 +676,7 @@ tau_tracker_upkeep (struct tau_tracker * tracker) return; /* if we don't have an address yet, try & get one now. */ - if (!tracker->addr && (tracker->dns_request == NULL)) + if (!closing && tracker->addr == NULL && tracker->dns_request == NULL) { struct evutil_addrinfo hints; memset (&hints, 0, sizeof (hints)); @@ -853,6 +855,8 @@ tr_tracker_udp_start_shutdown (tr_session * session) for (i=0, n=tr_ptrArraySize (&tau->trackers); itrackers, i); + if (tracker->dns_request != NULL) + evdns_getaddrinfo_cancel (tracker->dns_request); tracker->close_at = now + 3; tau_tracker_upkeep (tracker); } diff --git a/libtransmission/session.c b/libtransmission/session.c index 2fe45a2be..146c0fd02 100644 --- a/libtransmission/session.c +++ b/libtransmission/session.c @@ -1781,13 +1781,10 @@ compareTorrentByCur (const void * va, const void * vb) static void closeBlocklists (tr_session *); static void -sessionCloseImpl (void * vsession) +sessionCloseImplStart (tr_session * session) { int i, n; tr_torrent ** torrents; - tr_session * session = vsession; - - assert (tr_isSession (session)); session->isClosing = true; @@ -1829,15 +1826,11 @@ sessionCloseImpl (void * vsession) tr_cacheFree (session->cache); session->cache = NULL; +} - /* gotta keep udp running long enough to send out all - the &event=stopped UDP tracker messages */ - while (!tr_tracker_udp_is_idle (session)) - { - tr_tracker_udp_upkeep (session); - tr_wait_msec (100); - } - +static void +sessionCloseImplFinish (tr_session * session) +{ /* we had to wait until UDP trackers were closed before closing these: */ evdns_base_free (session->evdns_base, 0); session->evdns_base = NULL; @@ -1854,6 +1847,42 @@ sessionCloseImpl (void * vsession) session->isClosed = true; } +static void +sessionCloseImplWaitForIdleUdp (evutil_socket_t foo UNUSED, + short bar UNUSED, + void * vsession) +{ + tr_session * session = vsession; + + assert (tr_isSession (session)); + + /* gotta keep udp running long enough to send out all + the &event=stopped UDP tracker messages */ + if (!tr_tracker_udp_is_idle (session)) + { + tr_tracker_udp_upkeep (session); + tr_timerAdd (session->saveTimer, 0, 100000); + return; + } + + sessionCloseImplFinish (session); +} + +static void +sessionCloseImpl (void * vsession) +{ + tr_session * session = vsession; + + assert (tr_isSession (session)); + + sessionCloseImplStart (session); + + /* saveTimer is not used at this point, reusing for UDP shutdown wait */ + assert (session->saveTimer == NULL); + session->saveTimer = evtimer_new (session->event_base, sessionCloseImplWaitForIdleUdp, session); + tr_timerAdd (session->saveTimer, 0, 0); +} + static int deadlineReached (const time_t deadline) { diff --git a/libtransmission/trevent.c b/libtransmission/trevent.c index d3cfbe446..8fc0d06c8 100644 --- a/libtransmission/trevent.c +++ b/libtransmission/trevent.c @@ -203,6 +203,8 @@ readFromPipe (evutil_socket_t fd, { dbgmsg ("pipe eof reached... removing event listener"); event_free (eh->pipeEvent); + tr_netCloseSocket (eh->fds[0]); + event_base_loopexit (eh->base, NULL); break; }