mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-15 07:15:03 -04:00
Merge branch 'evdns_getaddrinfo-race-fix'
* evdns_getaddrinfo-race-fix: evdns: add regress test for getaddrinfo race evdns: fix race condition in evdns_getaddrinfo() Merges: #673 (cherry picked from commit ee12c516cf5766b2c32203dcc20c32b8691a8ebb)
This commit is contained in:
parent
b3af7bdde3
commit
3237d6972d
9
evdns.c
9
evdns.c
@ -4637,6 +4637,7 @@ evdns_getaddrinfo(struct evdns_base *dns_base,
|
|||||||
int err;
|
int err;
|
||||||
int port = 0;
|
int port = 0;
|
||||||
int want_cname = 0;
|
int want_cname = 0;
|
||||||
|
int started = 0;
|
||||||
|
|
||||||
if (!dns_base) {
|
if (!dns_base) {
|
||||||
dns_base = current_base;
|
dns_base = current_base;
|
||||||
@ -4715,6 +4716,8 @@ evdns_getaddrinfo(struct evdns_base *dns_base,
|
|||||||
* launching those requests. (XXX we don't do that yet.)
|
* launching those requests. (XXX we don't do that yet.)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
EVDNS_LOCK(dns_base);
|
||||||
|
|
||||||
if (hints.ai_family != PF_INET6) {
|
if (hints.ai_family != PF_INET6) {
|
||||||
log(EVDNS_LOG_DEBUG, "Sending request for %s on ipv4 as %p",
|
log(EVDNS_LOG_DEBUG, "Sending request for %s on ipv4 as %p",
|
||||||
nodename, &data->ipv4_request);
|
nodename, &data->ipv4_request);
|
||||||
@ -4741,7 +4744,11 @@ evdns_getaddrinfo(struct evdns_base *dns_base,
|
|||||||
evtimer_assign(&data->timeout, dns_base->event_base,
|
evtimer_assign(&data->timeout, dns_base->event_base,
|
||||||
evdns_getaddrinfo_timeout_cb, data);
|
evdns_getaddrinfo_timeout_cb, data);
|
||||||
|
|
||||||
if (data->ipv4_request.r || data->ipv6_request.r) {
|
started = (data->ipv4_request.r || data->ipv6_request.r);
|
||||||
|
|
||||||
|
EVDNS_UNLOCK(dns_base);
|
||||||
|
|
||||||
|
if (started) {
|
||||||
return data;
|
return data;
|
||||||
} else {
|
} else {
|
||||||
mm_free(data);
|
mm_free(data);
|
||||||
|
@ -72,9 +72,12 @@
|
|||||||
#include "event2/util.h"
|
#include "event2/util.h"
|
||||||
#include "event2/listener.h"
|
#include "event2/listener.h"
|
||||||
#include "event2/bufferevent.h"
|
#include "event2/bufferevent.h"
|
||||||
|
#include <event2/thread.h>
|
||||||
#include "log-internal.h"
|
#include "log-internal.h"
|
||||||
|
#include "evthread-internal.h"
|
||||||
#include "regress.h"
|
#include "regress.h"
|
||||||
#include "regress_testutils.h"
|
#include "regress_testutils.h"
|
||||||
|
#include "regress_thread.h"
|
||||||
|
|
||||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
||||||
|
|
||||||
@ -2126,6 +2129,146 @@ end:
|
|||||||
evdns_close_server_port(dns_port);
|
evdns_close_server_port(dns_port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef EVTHREAD_USE_PTHREADS_IMPLEMENTED
|
||||||
|
struct race_param
|
||||||
|
{
|
||||||
|
void *lock;
|
||||||
|
void *reqs_cmpl_cond;
|
||||||
|
int bw_threads;
|
||||||
|
void *bw_threads_exited_cond;
|
||||||
|
volatile int stopping;
|
||||||
|
void *base;
|
||||||
|
void *dns;
|
||||||
|
};
|
||||||
|
static void *
|
||||||
|
race_base_run(void *arg)
|
||||||
|
{
|
||||||
|
struct race_param *rp = (struct race_param *)arg;
|
||||||
|
event_base_loop(rp->base, EVLOOP_NO_EXIT_ON_EMPTY);
|
||||||
|
THREAD_RETURN();
|
||||||
|
}
|
||||||
|
static void *
|
||||||
|
race_busywait_run(void *arg)
|
||||||
|
{
|
||||||
|
struct race_param *rp = (struct race_param *)arg;
|
||||||
|
struct sockaddr_storage ss;
|
||||||
|
while (!rp->stopping)
|
||||||
|
evdns_base_get_nameserver_addr(rp->dns, 0, (struct sockaddr *)&ss, sizeof(ss));
|
||||||
|
EVLOCK_LOCK(rp->lock, 0);
|
||||||
|
if (--rp->bw_threads == 0)
|
||||||
|
EVTHREAD_COND_SIGNAL(rp->bw_threads_exited_cond);
|
||||||
|
EVLOCK_UNLOCK(rp->lock, 0);
|
||||||
|
THREAD_RETURN();
|
||||||
|
}
|
||||||
|
static void
|
||||||
|
race_gai_cb(int result, struct evutil_addrinfo *res, void *arg)
|
||||||
|
{
|
||||||
|
(void)result;
|
||||||
|
(void)res;
|
||||||
|
struct race_param *rp = arg;
|
||||||
|
|
||||||
|
--n_replies_left;
|
||||||
|
if (n_replies_left == 0) {
|
||||||
|
EVLOCK_LOCK(rp->lock, 0);
|
||||||
|
EVTHREAD_COND_SIGNAL(rp->reqs_cmpl_cond);
|
||||||
|
EVLOCK_UNLOCK(rp->lock, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void
|
||||||
|
getaddrinfo_race_gotresolve_test(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
struct race_param rp;
|
||||||
|
struct evdns_server_port *dns_port = NULL;
|
||||||
|
ev_uint16_t portnum = 0;
|
||||||
|
char buf[64];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// Some stress is needed to yield inside getaddrinfo between resolve_ipv4 and resolve_ipv6
|
||||||
|
int n_reqs = 16384;
|
||||||
|
#ifdef _SC_NPROCESSORS_ONLN
|
||||||
|
int n_threads = sysconf(_SC_NPROCESSORS_ONLN) + 1;
|
||||||
|
#else
|
||||||
|
int n_threads = 17;
|
||||||
|
#endif
|
||||||
|
THREAD_T thread[n_threads];
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
evthread_use_pthreads();
|
||||||
|
|
||||||
|
rp.base = event_base_new();
|
||||||
|
tt_assert(rp.base);
|
||||||
|
if (evthread_make_base_notifiable(rp.base) < 0)
|
||||||
|
tt_abort_msg("Couldn't make base notifiable!");
|
||||||
|
|
||||||
|
dns_port = regress_get_dnsserver(rp.base, &portnum, NULL,
|
||||||
|
regress_dns_server_cb, reissue_table);
|
||||||
|
tt_assert(dns_port);
|
||||||
|
|
||||||
|
evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", (int)portnum);
|
||||||
|
|
||||||
|
rp.dns = evdns_base_new(rp.base, 0);
|
||||||
|
tt_assert(!evdns_base_nameserver_ip_add(rp.dns, buf));
|
||||||
|
|
||||||
|
n_replies_left = n_reqs;
|
||||||
|
|
||||||
|
EVTHREAD_ALLOC_LOCK(rp.lock, 0);
|
||||||
|
EVTHREAD_ALLOC_COND(rp.reqs_cmpl_cond);
|
||||||
|
EVTHREAD_ALLOC_COND(rp.bw_threads_exited_cond);
|
||||||
|
tt_assert(rp.lock);
|
||||||
|
tt_assert(rp.reqs_cmpl_cond);
|
||||||
|
tt_assert(rp.bw_threads_exited_cond);
|
||||||
|
rp.bw_threads = 0;
|
||||||
|
rp.stopping = 0;
|
||||||
|
|
||||||
|
// Run resolver thread
|
||||||
|
THREAD_START(thread[0], race_base_run, &rp);
|
||||||
|
// Run busy-wait threads used to force yield this thread
|
||||||
|
for (i = 1; i < n_threads; i++) {
|
||||||
|
rp.bw_threads++;
|
||||||
|
THREAD_START(thread[i], race_busywait_run, &rp);
|
||||||
|
}
|
||||||
|
|
||||||
|
EVLOCK_LOCK(rp.lock, 0);
|
||||||
|
|
||||||
|
for (i = 0; i < n_reqs; ++i) {
|
||||||
|
tt_assert(evdns_getaddrinfo(rp.dns, "foof.example.com", "ssh", NULL, race_gai_cb, &rp));
|
||||||
|
// This magic along with busy-wait threads make this thread yield frequently
|
||||||
|
if (i % 100 == 0) {
|
||||||
|
tv.tv_sec = 0;
|
||||||
|
tv.tv_usec = 10000;
|
||||||
|
evutil_usleep_(&tv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit_base = rp.base;
|
||||||
|
|
||||||
|
// Wait for some time
|
||||||
|
tv.tv_sec = 5;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
EVTHREAD_COND_WAIT_TIMED(rp.reqs_cmpl_cond, rp.lock, &tv);
|
||||||
|
|
||||||
|
// Stop busy-wait threads
|
||||||
|
tv.tv_sec = 1;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
rp.stopping = 1;
|
||||||
|
tt_assert(EVTHREAD_COND_WAIT_TIMED(rp.bw_threads_exited_cond, rp.lock, &tv) == 0);
|
||||||
|
|
||||||
|
EVLOCK_UNLOCK(rp.lock, 0);
|
||||||
|
|
||||||
|
evdns_base_free(rp.dns, 1 /** fail requests */);
|
||||||
|
|
||||||
|
tt_int_op(n_replies_left, ==, 0);
|
||||||
|
|
||||||
|
end:
|
||||||
|
EVTHREAD_FREE_LOCK(rp.lock, 0);
|
||||||
|
EVTHREAD_FREE_COND(rp.reqs_cmpl_cond);
|
||||||
|
EVTHREAD_FREE_COND(rp.bw_threads_exited_cond);
|
||||||
|
evdns_close_server_port(dns_port);
|
||||||
|
event_base_loopbreak(rp.base);
|
||||||
|
event_base_free(rp.base);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#define DNS_LEGACY(name, flags) \
|
#define DNS_LEGACY(name, flags) \
|
||||||
{ #name, run_legacy_test_fn, flags|TT_LEGACY, &legacy_setup, \
|
{ #name, run_legacy_test_fn, flags|TT_LEGACY, &legacy_setup, \
|
||||||
@ -2183,6 +2326,11 @@ struct testcase_t dns_testcases[] = {
|
|||||||
{ "client_fail_requests_getaddrinfo",
|
{ "client_fail_requests_getaddrinfo",
|
||||||
dns_client_fail_requests_getaddrinfo_test,
|
dns_client_fail_requests_getaddrinfo_test,
|
||||||
TT_FORK|TT_NEED_BASE, &basic_setup, NULL },
|
TT_FORK|TT_NEED_BASE, &basic_setup, NULL },
|
||||||
|
#ifdef EVTHREAD_USE_PTHREADS_IMPLEMENTED
|
||||||
|
{ "getaddrinfo_race_gotresolve",
|
||||||
|
getaddrinfo_race_gotresolve_test,
|
||||||
|
TT_FORK, NULL, NULL },
|
||||||
|
#endif
|
||||||
|
|
||||||
END_OF_TESTCASES
|
END_OF_TESTCASES
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user