On Wed, Nov 3, 2010 at 10:44 PM, Denis Bilenko <denis.bilenko@xxxxxxxxx> wrote: > On Thu, Nov 4, 2010 at 1:24 AM, Nick Mathewson <nickm@xxxxxxxxxxxxx> wrote: >>> You free req->handle and then access it. >> >> Ugh. What if we move "req->handle->current_req =NULL" to *above* that "if" ? > > Still crashes. Assuming that it now crashes in a different place from where it used to crash -- that is, at ASSERT_VALID_REQUEST in evdns_cancel, I've got an updated patch, attached below. Third time is maybe the charm. I'm also attaching a quick-and-dirty stress-test program I used to provoke the crash, and to verify that the fix works. If the fix doesn't work for you, see if you can hack up this test program (or write your own program) to make the failure reproducible? thanks, -- Nick
#include <event2/dns.h> #include <event2/dns_struct.h> #include <event2/event.h> #include <event2/event_struct.h> #include <assert.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> struct request_status { int magic; struct event_base *base; struct evdns_base *dns_base; struct evdns_request *request; struct event cancel_event; int canceled; }; #define MAGIC 0x1234abcd int pending = 0; void cancel_request_cb(evutil_socket_t fd, short what, void *arg) { struct request_status *status = arg; assert(status->magic == MAGIC); evdns_cancel_request(status->dns_base, status->request); status->canceled = 1; } void server_cb(struct evdns_server_request *req, void *arg) { ev_uint32_t answer = 0x7f000001; assert(req->nquestions); evdns_server_request_add_a_reply(req, req->questions[0]->name, 1, &answer, 100); evdns_server_request_respond(req, 0); } void answer_cb(int result, char type, int count, int ttl, void *addresses, void *arg) { struct request_status *status = arg; struct event_base *base = status->base; assert(status->magic == MAGIC); if (result == DNS_ERR_CANCEL) { assert(status->canceled); } event_del(&status->cancel_event); memset(status, 0xf0, sizeof(*status)); free(status); if (--pending <= 0) event_base_loopexit(base, NULL); } void launch(struct event_base *base, struct evdns_base *dns_base) { struct request_status *status = calloc(1,sizeof(*status)); struct timeval tv = { 0, 1001 }; status->magic = MAGIC; status->base = base; status->dns_base = dns_base; event_assign(&status->cancel_event, base, -1, 0, cancel_request_cb, status); status->request = evdns_base_resolve_ipv4( dns_base, "foobar.bazquux.example.com", 0, answer_cb, status); event_add(&status->cancel_event, &tv); ++pending; } int main(int c, char **v) { int i; struct event_base *base; struct evdns_base *dns_base; struct evdns_server_port *server; evutil_socket_t fd; struct sockaddr_in sin; struct sockaddr_storage ss; socklen_t slen; base = event_base_new(); dns_base = evdns_base_new(base, 0); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = 0; sin.sin_addr.s_addr = htonl(0x7f000001); if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket"); return 1; } evutil_make_socket_nonblocking(fd); if (bind(fd, (struct sockaddr*)&sin, sizeof(sin))<0) { perror("bind"); return 1; } server = evdns_add_server_port_with_base(base, fd, 0, server_cb, base); memset(&ss, 0, sizeof(ss)); slen = sizeof(ss); if (getsockname(fd, (struct sockaddr*)&ss, &slen)<0) { perror("getsockname"); return 1; } evdns_base_nameserver_sockaddr_add(dns_base, (struct sockaddr*)&ss, slen, 0); for (i = 0; i < 1000; ++i) { launch(base, dns_base); } event_base_dispatch(base); return 0; }
Attachment:
0001-Don-t-free-evdns_request-handles-until-after-the-cal.patch_v2
Description: Binary data