Fix segfault and logic error in remove_duplicate_orports()

This function tried to modify an array in place, but did it in a
pretty confusing and complicated way.  I've revised it to follow a
much more straightforward approach.

Fixes bug #40065.
This commit is contained in:
Nick Mathewson 2020-07-24 16:41:31 -04:00 committed by George Kadianakis
parent 010387e4bd
commit fc5fe094b1

View File

@ -186,8 +186,14 @@ describe_relay_port(const port_cfg_t *port)
static void static void
remove_duplicate_orports(smartlist_t *ports) remove_duplicate_orports(smartlist_t *ports)
{ {
/* First we'll decide what to remove, then we'll remove it. */
bool *removing = tor_calloc(smartlist_len(ports), sizeof(bool));
for (int i = 0; i < smartlist_len(ports); ++i) { for (int i = 0; i < smartlist_len(ports); ++i) {
port_cfg_t *current = smartlist_get(ports, i); const port_cfg_t *current = smartlist_get(ports, i);
if (removing[i]) {
continue;
}
/* Skip non ORPorts. */ /* Skip non ORPorts. */
if (current->type != CONN_TYPE_OR_LISTENER) { if (current->type != CONN_TYPE_OR_LISTENER) {
@ -195,26 +201,40 @@ remove_duplicate_orports(smartlist_t *ports)
} }
for (int j = 0; j < smartlist_len(ports); ++j) { for (int j = 0; j < smartlist_len(ports); ++j) {
port_cfg_t *next = smartlist_get(ports, j); const port_cfg_t *next = smartlist_get(ports, j);
/* Avoid comparing the same object. */ /* Avoid comparing the same object. */
if (current == next) { if (current == next) {
continue; continue;
} }
if (removing[j]) {
continue;
}
/* Same address family and same port number, we have a match. */ /* Same address family and same port number, we have a match. */
if (!current->explicit_addr && next->explicit_addr && if (!current->explicit_addr && next->explicit_addr &&
tor_addr_family(&current->addr) == tor_addr_family(&next->addr) && tor_addr_family(&current->addr) == tor_addr_family(&next->addr) &&
current->port == next->port) { current->port == next->port) {
/* Remove current because next is explicitly set. */ /* Remove current because next is explicitly set. */
smartlist_del_keeporder(ports, i--); removing[i] = true;
char *next_str = tor_strdup(describe_relay_port(next)); char *next_str = tor_strdup(describe_relay_port(next));
log_warn(LD_CONFIG, "Configuration port %s superseded by %s", log_warn(LD_CONFIG, "Configuration port %s superseded by %s",
describe_relay_port(current), next_str); describe_relay_port(current), next_str);
tor_free(next_str); tor_free(next_str);
port_cfg_free(current);
} }
} }
} }
/* Iterate over array in reverse order to keep indices valid. */
for (int i = smartlist_len(ports)-1; i >= 0; --i) {
tor_assert(i < smartlist_len(ports));
if (removing[i]) {
port_cfg_t *current = smartlist_get(ports, i);
smartlist_del_keeporder(ports, i);
port_cfg_free(current);
}
}
tor_free(removing);
} }
/** Given a list of <b>port_cfg_t</b> in <b>ports</b>, check them for internal /** Given a list of <b>port_cfg_t</b> in <b>ports</b>, check them for internal