1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-07 21:21:31 +00:00

Consolidate event list management to nevent_unref()

Removes duplicate logic for PCAP_BSD_SELECT_HACK. May address accounting
problems that led to issues like #187 (macOS) and #2912 (Windows).
This commit is contained in:
dmiller
2024-11-13 19:15:12 +00:00
parent 222add87a6
commit cffc94e845
4 changed files with 100 additions and 189 deletions

View File

@@ -461,16 +461,9 @@ void iterate_through_event_lists(struct npool *nsp) {
process_event(nsp, evlist, nse, ev);
if (nse->event_done) {
/* event is done, remove it from the event list and update IOD pointers
* to the first events of each kind */
update_first_events(nse);
gh_list_remove(evlist, &nse->nodeq_io);
gh_list_append(&nsp->free_events, &nse->nodeq_io);
if (nse->timeout.tv_sec)
gh_heap_remove(&nsp->expirables, &nse->expire);
if (nse->eov)
terminate_overlapped_event(nsp, nse);
nevent_unref(nsp, nse);
}
else {
assert(nse->eov->forced_operation != IOCP_NOT_FORCED);

View File

@@ -258,16 +258,20 @@ static int iod_add_event(struct niod *iod, struct nevent *nse) {
char add_read = 0, add_pcap_read = 0;
#if PCAP_BSD_SELECT_HACK
/* BSD hack mode: add event to both read and pcap_read lists */
/* when using BSD hack we must do pcap_next() after select().
* Let's insert this pcap to bot queues, to selectable and nonselectable.
* This will result in doing pcap_next_ex() just before select() */
add_read = add_pcap_read = 1;
#else
if (((mspcap *)iod->pcap)->pcap_desc >= 0) {
add_read = 1;
} else {
/* pcap isn't selectable. Add it to pcap-specific queue. */
add_pcap_read = 1;
}
#endif
if (add_read) {
nsock_log_debug_all("PCAP NSE #%lu: Adding event to READ_EVENTS", nse->id);
if (iod->first_read)
gh_list_insert_before(&nsp->read_events, iod->first_read, &nse->nodeq_io);
else
@@ -275,6 +279,7 @@ static int iod_add_event(struct niod *iod, struct nevent *nse) {
iod->first_read = &nse->nodeq_io;
}
if (add_pcap_read) {
nsock_log_debug_all("PCAP NSE #%lu: Adding event to PCAP_READ_EVENTS", nse->id);
if (iod->first_pcap_read)
gh_list_insert_before(&nsp->pcap_read_events, iod->first_pcap_read,
&nse->nodeq_pcap);
@@ -1062,32 +1067,6 @@ void process_event(struct npool *nsp, gh_list_t *evlist, struct nevent *nse, int
if (event_timedout(nse))
handle_pcap_read_result(nsp, nse, NSE_STATUS_TIMEOUT);
#if PCAP_BSD_SELECT_HACK
/* If event occurred, and we're in BSD_HACK mode, then this event was added
* to two queues. read_event and pcap_read_event
* Of course we should destroy it only once.
* I assume we're now in read_event, so just unlink this event from
* pcap_read_event */
if (((mspcap *)nse->iod->pcap)->pcap_desc >= 0
&& nse->event_done
&& evlist == &nsp->read_events) {
/* event is done, list is read_events and we're in BSD_HACK mode.
* So unlink event from pcap_read_events */
update_first_events(nse);
gh_list_remove(&nsp->pcap_read_events, &nse->nodeq_pcap);
nsock_log_debug_all("PCAP NSE #%lu: Removing event from PCAP_READ_EVENTS",
nse->id);
}
if (((mspcap *)nse->iod->pcap)->pcap_desc >= 0
&& nse->event_done
&& evlist == &nsp->pcap_read_events) {
update_first_events(nse);
gh_list_remove(&nsp->read_events, &nse->nodeq_io);
nsock_log_debug_all("PCAP NSE #%lu: Removing event from READ_EVENTS",
nse->id);
}
#endif
break;
}
#endif
@@ -1174,68 +1153,12 @@ void process_iod_events(struct npool *nsp, struct niod *nsi, int ev) {
next = gh_lnode_next(current);
if (nse->event_done) {
/* event is done, remove it from the event list and update IOD pointers
* to the first events of each kind */
update_first_events(nse);
gh_list_remove(evlists[i], current);
gh_list_append(&nsp->free_events, &nse->nodeq_io);
if (nse->timeout.tv_sec)
gh_heap_remove(&nsp->expirables, &nse->expire);
nevent_unref(nsp, nse);
}
}
}
}
static int nevent_unref(struct npool *nsp, struct nevent *nse) {
switch (nse->type) {
case NSE_TYPE_CONNECT:
case NSE_TYPE_CONNECT_SSL:
gh_list_remove(&nsp->connect_events, &nse->nodeq_io);
break;
case NSE_TYPE_READ:
gh_list_remove(&nsp->read_events, &nse->nodeq_io);
break;
case NSE_TYPE_WRITE:
gh_list_remove(&nsp->write_events, &nse->nodeq_io);
break;
#if HAVE_PCAP
case NSE_TYPE_PCAP_READ: {
char read = 0;
char pcap = 0;
#if PCAP_BSD_SELECT_HACK
read = pcap = 1;
#else
if (((mspcap *)nse->iod->pcap)->pcap_desc >= 0)
read = 1;
else
pcap = 1;
#endif /* PCAP_BSD_SELECT_HACK */
if (read)
gh_list_remove(&nsp->read_events, &nse->nodeq_io);
if (pcap)
gh_list_remove(&nsp->pcap_read_events, &nse->nodeq_pcap);
break;
}
#endif /* HAVE_PCAP */
case NSE_TYPE_TIMER:
/* Nothing to do */
break;
default:
fatal("Unknown event type %d", nse->type);
}
gh_list_append(&nsp->free_events, &nse->nodeq_io);
return 0;
}
void process_expired_events(struct npool *nsp) {
for (;;) {
gh_hnode_t *hnode;
@@ -1249,10 +1172,8 @@ void process_expired_events(struct npool *nsp) {
if (!event_timedout(nse))
break;
gh_heap_remove(&nsp->expirables, hnode);
process_event(nsp, NULL, nse, EV_NONE);
assert(nse->event_done);
update_first_events(nse);
nevent_unref(nsp, nse);
}
}
@@ -1285,7 +1206,17 @@ void nsock_pool_add_event(struct npool *nsp, struct nevent *nse) {
nsp->events_pending++;
if (!nse->event_done && nse->timeout.tv_sec) {
/* It can happen that the event already completed. In which case we can
* already deliver it, even though we're probably not inside nsock_loop(). */
if (nse->event_done) {
// Quick validation, since we won't get to the switch below:
assert(nse->type >= 0 && nse->type < NSE_TYPE_MAX);
event_dispatch_and_delete(nsp, nse, 1);
// No need to call nevent_unref since we never added it to any lists!
return;
}
if (nse->timeout.tv_sec) {
/* This event is expirable, add it to the queue */
gh_heap_push(&nsp->expirables, &nse->expire);
}
@@ -1294,38 +1225,32 @@ void nsock_pool_add_event(struct npool *nsp, struct nevent *nse) {
switch (nse->type) {
case NSE_TYPE_CONNECT:
case NSE_TYPE_CONNECT_SSL:
if (!nse->event_done) {
assert(nse->iod->sd >= 0);
socket_count_read_inc(nse->iod);
socket_count_write_inc(nse->iod);
update_events(nse->iod, nsp, nse, EV_READ|EV_WRITE, EV_NONE);
}
assert(nse->iod->sd >= 0);
socket_count_read_inc(nse->iod);
socket_count_write_inc(nse->iod);
update_events(nse->iod, nsp, nse, EV_READ|EV_WRITE, EV_NONE);
iod_add_event(nse->iod, nse);
break;
case NSE_TYPE_READ:
if (!nse->event_done) {
assert(nse->iod->sd >= 0);
socket_count_read_inc(nse->iod);
update_events(nse->iod, nsp, nse, EV_READ, EV_NONE);
assert(nse->iod->sd >= 0);
socket_count_read_inc(nse->iod);
update_events(nse->iod, nsp, nse, EV_READ, EV_NONE);
#if HAVE_OPENSSL
if (nse->iod->ssl)
nse->sslinfo.ssl_desire = SSL_ERROR_WANT_READ;
if (nse->iod->ssl)
nse->sslinfo.ssl_desire = SSL_ERROR_WANT_READ;
#endif
}
iod_add_event(nse->iod, nse);
break;
case NSE_TYPE_WRITE:
if (!nse->event_done) {
assert(nse->iod->sd >= 0);
socket_count_write_inc(nse->iod);
update_events(nse->iod, nsp, nse, EV_WRITE, EV_NONE);
assert(nse->iod->sd >= 0);
socket_count_write_inc(nse->iod);
update_events(nse->iod, nsp, nse, EV_WRITE, EV_NONE);
#if HAVE_OPENSSL
if (nse->iod->ssl)
nse->sslinfo.ssl_desire = SSL_ERROR_WANT_WRITE;
if (nse->iod->ssl)
nse->sslinfo.ssl_desire = SSL_ERROR_WANT_WRITE;
#endif
}
iod_add_event(nse->iod, nse);
break;
@@ -1335,26 +1260,9 @@ void nsock_pool_add_event(struct npool *nsp, struct nevent *nse) {
#if HAVE_PCAP
case NSE_TYPE_PCAP_READ: {
mspcap *mp = (mspcap *)nse->iod->pcap;
assert(mp);
if (mp->pcap_desc >= 0) { /* pcap descriptor present */
if (!nse->event_done) {
socket_count_readpcap_inc(nse->iod);
update_events(nse->iod, nsp, nse, EV_READ, EV_NONE);
}
nsock_log_debug_all("PCAP NSE #%lu: Adding event to READ_EVENTS", nse->id);
#if PCAP_BSD_SELECT_HACK
/* when using BSD hack we must do pcap_next() after select().
* Let's insert this pcap to bot queues, to selectable and nonselectable.
* This will result in doing pcap_next_ex() just before select() */
nsock_log_debug_all("PCAP NSE #%lu: Adding event to PCAP_READ_EVENTS", nse->id);
#endif
} else {
/* pcap isn't selectable. Add it to pcap-specific queue. */
nsock_log_debug_all("PCAP NSE #%lu: Adding event to PCAP_READ_EVENTS", nse->id);
}
assert(nse->iod->pcap);
socket_count_readpcap_inc(nse->iod);
update_events(nse->iod, nsp, nse, EV_READ, EV_NONE);
iod_add_event(nse->iod, nse);
break;
}
@@ -1363,14 +1271,6 @@ void nsock_pool_add_event(struct npool *nsp, struct nevent *nse) {
default:
fatal("Unknown nsock event type (%d)", nse->type);
}
/* It can happen that the event already completed. In which case we can
* already deliver it, even though we're probably not inside nsock_loop(). */
if (nse->event_done) {
event_dispatch_and_delete(nsp, nse, 1);
update_first_events(nse);
nevent_unref(nsp, nse);
}
}
/* An event has been completed and the handler is about to be called. This

View File

@@ -138,8 +138,9 @@ static void first_ev_next(struct nevent *nse, gh_lnode_t **first, int nodeq2) {
}
}
void update_first_events(struct nevent *nse) {
switch (get_event_id_type(nse->id)) {
static void update_first_events(struct nevent *nse) {
assert(nse->iod || nse->type == NSE_TYPE_TIMER);
switch (nse->type) {
case NSE_TYPE_CONNECT:
case NSE_TYPE_CONNECT_SSL:
first_ev_next(nse, &nse->iod->first_connect, 0);
@@ -300,47 +301,7 @@ int nevent_delete(struct npool *nsp, struct nevent *nse, gh_list_t *event_list,
assert(nse->event_done);
if (nse->timeout.tv_sec)
gh_heap_remove(&nsp->expirables, &nse->expire);
if (event_list) {
update_first_events(nse);
gh_list_remove(event_list, elem);
}
gh_list_append(&nsp->free_events, &nse->nodeq_io);
nsock_log_debug_all("NSE #%lu: Removing event from list", nse->id);
#if HAVE_PCAP
#if PCAP_BSD_SELECT_HACK
if (nse->type == NSE_TYPE_PCAP_READ && ((mspcap *)nse->iod->pcap)->pcap_desc >= 0) {
nsock_log_debug_all("PCAP NSE #%lu: CANCEL TEST pcap=%p read=%p curr=%p sd=%i",
nse->id, &nsp->pcap_read_events, &nsp->read_events,
event_list,((mspcap *)nse->iod->pcap)->pcap_desc);
/* If event occurred, and we're in BSD_HACK mode, then this event was added to
* two queues. read_event and pcap_read_event Of course we should
* destroy it only once. I assume we're now in read_event, so just unlink
* this event from pcap_read_event */
gh_list_t *other_list = NULL;
const char *listname = NULL;
if (event_list == &nsp->read_events) {
other_list = &nsp->pcap_read_events;
listname = "PCAP_READ_EVENTS";
}
else if (event_list == &nsp->pcap_read_events) {
other_list = &nsp->read_events;
listname = "READ_EVENTS";
}
else
fatal("Bogus event list for NSE_TYPE_PCAP_READ");
nsock_log_debug_all("PCAP NSE #%lu: Removing event from %s", nse->id, listname);
gh_list_remove(other_list, &nse->nodeq_io);
}
#endif
#endif
nevent_unref(nsp, nse);
event_dispatch_and_delete(nsp, nse, notify);
return 1;
}
@@ -535,3 +496,59 @@ int event_timedout(struct nevent *nse) {
return (nse->timeout.tv_sec && !TIMEVAL_AFTER(nse->timeout, nsock_tod));
}
int nevent_unref(struct npool *nsp, struct nevent *nse) {
nsock_log_debug_all("NSE #%lu: Removing event from list", nse->id);
update_first_events(nse);
switch (nse->type) {
case NSE_TYPE_CONNECT:
case NSE_TYPE_CONNECT_SSL:
gh_list_remove(&nsp->connect_events, &nse->nodeq_io);
break;
case NSE_TYPE_READ:
gh_list_remove(&nsp->read_events, &nse->nodeq_io);
break;
case NSE_TYPE_WRITE:
gh_list_remove(&nsp->write_events, &nse->nodeq_io);
break;
#if HAVE_PCAP
case NSE_TYPE_PCAP_READ: {
char read = 0;
char pcap = 0;
#if PCAP_BSD_SELECT_HACK
read = pcap = 1;
#else
if (((mspcap *)nse->iod->pcap)->pcap_desc >= 0)
read = 1;
else
pcap = 1;
#endif /* PCAP_BSD_SELECT_HACK */
if (read)
gh_list_remove(&nsp->read_events, &nse->nodeq_io);
if (pcap)
gh_list_remove(&nsp->pcap_read_events, &nse->nodeq_pcap);
break;
}
#endif /* HAVE_PCAP */
case NSE_TYPE_TIMER:
/* Nothing to do */
break;
default:
fatal("Unknown event type %d", nse->type);
}
if (nse->timeout.tv_sec) {
nse->timeout.tv_sec = nse->timeout.tv_usec = 0;
gh_heap_remove(&nsp->expirables, &nse->expire);
}
gh_list_append(&nsp->free_events, &nse->nodeq_io);
return 0;
}

View File

@@ -460,6 +460,10 @@ struct nevent *event_new(struct npool *nsp, enum nse_type type, struct niod *iod
* been cancelled */
int nevent_delete(struct npool *nsp, struct nevent *nse, gh_list_t *event_list, gh_lnode_t *elem, int notify);
/* Unlink an event from any lists and add it to free_events.
* Calls update_first_events. Resets timeout and removes event from expirables */
int nevent_unref(struct npool *nsp, struct nevent *nse);
/* Adjust various statistics, dispatches the event handler (if notify is
* nonzero) and then deletes the event. This function does NOT delete the event
* from any lists it might be on (eg nsp->read_list etc.) nse->event_done
@@ -532,9 +536,6 @@ int pcap_read_on_nonselect(struct npool *nsp);
void iterate_through_pcap_events(struct npool *nsp);
#endif
/* defined in nsock_event.c */
void update_first_events(struct nevent *nse);
/* defined in nsock_engines.c */
struct io_engine *get_io_engine(void);