mem leak due to `ares__hosts_entry_to_hostent()` allocation strategy (#824)

`ares__hosts_entry_to_hostent()` would allocate a separate buffer for
each address, but `ares_free_hostent()` expects a single allocation to
hold all addresses.

This PR fixes this issue and simplifies the logic by using the
already-existing `ares__addrinfo2hostent()` to write the hostent instead
of coming up with yet another way to write the structure.

Fixes #823
Fix By: Brad House (@bradh352)
v1.27
Brad House 4 months ago
parent fafdae6dfc
commit 47f7d6b7d3
  1. 160
      src/lib/ares__hosts_file.c

@ -857,124 +857,6 @@ ares_status_t ares__hosts_search_host(ares_channel_t *channel,
return ARES_SUCCESS;
}
ares_status_t ares__hosts_entry_to_hostent(const ares_hosts_entry_t *entry,
int family, struct hostent **hostent)
{
ares_status_t status;
size_t naliases;
ares__llist_node_t *node;
size_t idx;
*hostent = ares_malloc_zero(sizeof(**hostent));
if (*hostent == NULL) {
status = ARES_ENOMEM;
goto fail;
}
(*hostent)->h_addrtype = (HOSTENT_ADDRTYPE_TYPE)family;
/* Copy IP addresses that match the address family */
idx = 0;
for (node = ares__llist_node_first(entry->ips); node != NULL;
node = ares__llist_node_next(node)) {
struct ares_addr addr;
const void *ptr = NULL;
size_t ptr_len = 0;
const char *ipaddr = ares__llist_node_val(node);
char **temp = NULL;
memset(&addr, 0, sizeof(addr));
addr.family = family;
ptr = ares_dns_pton(ipaddr, &addr, &ptr_len);
if (ptr == NULL) {
continue;
}
/* If family == AF_UNSPEC, then we want to inherit this for future
* conversions as we can only support a single address class */
if (family == AF_UNSPEC) {
family = addr.family;
(*hostent)->h_addrtype = (HOSTENT_ADDRTYPE_TYPE)addr.family;
}
temp = ares_realloc_zero((*hostent)->h_addr_list,
(idx + 1) * sizeof(*(*hostent)->h_addr_list),
(idx + 2) * sizeof(*(*hostent)->h_addr_list));
if (temp == NULL) {
status = ARES_ENOMEM;
goto fail;
}
(*hostent)->h_addr_list = temp;
(*hostent)->h_addr_list[idx] = ares_malloc(ptr_len);
if ((*hostent)->h_addr_list[idx] == NULL) {
status = ARES_ENOMEM;
goto fail;
}
memcpy((*hostent)->h_addr_list[idx], ptr, ptr_len);
idx++;
(*hostent)->h_length = (HOSTENT_LENGTH_TYPE)ptr_len;
}
/* entry didn't match address class */
if (idx == 0) {
status = ARES_ENOTFOUND;
goto fail;
}
/* Copy main hostname */
(*hostent)->h_name = ares_strdup(ares__llist_first_val(entry->hosts));
if ((*hostent)->h_name == NULL) {
status = ARES_ENOMEM;
goto fail;
}
/* Copy aliases */
naliases = ares__llist_len(entry->hosts) - 1;
/* Cap at 100, some people use https://github.com/StevenBlack/hosts and we
* don't need 200k+ aliases */
if (naliases > 100) {
naliases = 100;
}
(*hostent)->h_aliases =
ares_malloc_zero((naliases + 1) * sizeof(*(*hostent)->h_aliases));
if ((*hostent)->h_aliases == NULL) {
status = ARES_ENOMEM;
goto fail;
}
/* Copy all entries to the alias except the first */
idx = 0;
node = ares__llist_node_first(entry->hosts);
node = ares__llist_node_next(node);
while (node != NULL) {
(*hostent)->h_aliases[idx] = ares_strdup(ares__llist_node_val(node));
if ((*hostent)->h_aliases[idx] == NULL) {
status = ARES_ENOMEM;
goto fail;
}
idx++;
/* Break out if artificially capped */
if (idx == naliases) {
break;
}
node = ares__llist_node_next(node);
}
return ARES_SUCCESS;
fail:
ares_free_hostent(*hostent);
*hostent = NULL;
return status;
}
static ares_status_t
ares__hosts_ai_append_cnames(const ares_hosts_entry_t *entry,
struct ares_addrinfo_cname **cnames_out)
@ -1068,10 +950,12 @@ ares_status_t ares__hosts_entry_to_addrinfo(const ares_hosts_entry_t *entry,
return ARES_EBADFAMILY;
}
ai->name = ares_strdup(name);
if (ai->name == NULL) {
status = ARES_ENOMEM;
goto done;
if (name != NULL) {
ai->name = ares_strdup(name);
if (ai->name == NULL) {
status = ARES_ENOMEM;
goto done;
}
}
for (node = ares__llist_node_first(entry->ips); node != NULL;
@ -1117,3 +1001,35 @@ done:
return status;
}
ares_status_t ares__hosts_entry_to_hostent(const ares_hosts_entry_t *entry,
int family, struct hostent **hostent)
{
ares_status_t status;
struct ares_addrinfo *ai = ares_malloc_zero(sizeof(*ai));
*hostent = NULL;
if (ai == NULL) {
return ARES_ENOMEM;
}
status = ares__hosts_entry_to_addrinfo(entry, NULL, family, 0, ARES_TRUE, ai);
if (status != ARES_SUCCESS) {
goto done;
}
status = ares__addrinfo2hostent(ai, family, hostent);
if (status != ARES_SUCCESS) {
goto done;
}
done:
ares_freeaddrinfo(ai);
if (status != ARES_SUCCESS) {
ares_free_hostent(*hostent);
*hostent = NULL;
}
return status;
}

Loading…
Cancel
Save