From d938c104e1f27deea44a99f2936ad753eab19d7a Mon Sep 17 00:00:00 2001 From: Wayne Zhang Date: Tue, 17 May 2022 12:18:18 -0400 Subject: [PATCH] zos: improve memory management of ip addresses (#3640 3/3) Fixes an issue where under certain conditions, dynamically allocated ip addresses, strings, and buffers were either leaking memory, being incorrectly freed, or not performing error checks. Also fixes an issue where the uv_interface_address_t struct was not correctly initialized to 0, so use calloc() instead of malloc(). Co-authored-by: Igor Todorovski --- src/unix/os390.c | 104 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 83 insertions(+), 21 deletions(-) diff --git a/src/unix/os390.c b/src/unix/os390.c index 07ca7867..7b2fc6aa 100644 --- a/src/unix/os390.c +++ b/src/unix/os390.c @@ -279,6 +279,7 @@ static int uv__interface_addresses_v6(uv_interface_address_t** addresses, __net_ifconf6entry_t* ifr; __net_ifconf6entry_t* p; unsigned int i; + int count_names; unsigned char netmask[16] = {0}; *count = 0; @@ -288,17 +289,27 @@ static int uv__interface_addresses_v6(uv_interface_address_t** addresses, if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) return UV__ERR(errno); - ifc.__nif6h_version = 1; - ifc.__nif6h_buflen = maxsize; - ifc.__nif6h_buffer = uv__calloc(1, maxsize);; + ifc.__nif6h_buffer = uv__calloc(1, maxsize); - if (ioctl(sockfd, SIOCGIFCONF6, &ifc) == -1) { + if (ifc.__nif6h_buffer == NULL) { uv__close(sockfd); - return UV__ERR(errno); + return UV_ENOMEM; } + ifc.__nif6h_version = 1; + ifc.__nif6h_buflen = maxsize; + + if (ioctl(sockfd, SIOCGIFCONF6, &ifc) == -1) { + /* This will error on a system that does not support IPv6. However, we want + * to treat this as there being 0 interfaces so we can continue to get IPv4 + * interfaces in uv_interface_addresses(). So return 0 instead of the error. + */ + uv__free(ifc.__nif6h_buffer); + uv__close(sockfd); + errno = 0; + return 0; + } - *count = 0; ifr = (__net_ifconf6entry_t*)(ifc.__nif6h_buffer); while ((char*)ifr < (char*)ifc.__nif6h_buffer + ifc.__nif6h_buflen) { p = ifr; @@ -313,14 +324,22 @@ static int uv__interface_addresses_v6(uv_interface_address_t** addresses, ++(*count); } + if ((*count) == 0) { + uv__free(ifc.__nif6h_buffer); + uv__close(sockfd); + return 0; + } + /* Alloc the return interface structs */ - *addresses = uv__malloc(*count * sizeof(uv_interface_address_t)); + *addresses = uv__calloc(1, *count * sizeof(uv_interface_address_t)); if (!(*addresses)) { + uv__free(ifc.__nif6h_buffer); uv__close(sockfd); return UV_ENOMEM; } address = *addresses; + count_names = 0; ifr = (__net_ifconf6entry_t*)(ifc.__nif6h_buffer); while ((char*)ifr < (char*)ifc.__nif6h_buffer + ifc.__nif6h_buflen) { p = ifr; @@ -341,11 +360,16 @@ static int uv__interface_addresses_v6(uv_interface_address_t** addresses, p->__nif6e_name[i] != 0) ++i; address->name = uv__malloc(i + 1); - if (address->name == NULL) + if (address->name == NULL) { + uv_free_interface_addresses(*addresses, count_names); + uv__free(ifc.__nif6h_buffer); + uv__close(sockfd); return UV_ENOMEM; + } memcpy(address->name, p->__nif6e_name, i); address->name[i] = '\0'; __e2a_s(address->name); + count_names++; address->address.address6 = *((struct sockaddr_in6*) &p->__nif6e_addr); @@ -360,10 +384,10 @@ static int uv__interface_addresses_v6(uv_interface_address_t** addresses, address->netmask.netmask6.sin6_family = AF_INET6; address->is_internal = p->__nif6e_flags & _NIF6E_FLAGS_LOOPBACK ? 1 : 0; - memset(address->phys_addr, 0, sizeof(address->phys_addr)); address++; } + uv__free(ifc.__nif6h_buffer); uv__close(sockfd); return 0; } @@ -377,15 +401,18 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { struct ifreq flg; struct ifreq* ifr; struct ifreq* p; + uv_interface_address_t* addresses_v6; int count_v6; unsigned int i; + int rc; + int count_names; *count = 0; *addresses = NULL; /* get the ipv6 addresses first */ - uv_interface_address_t* addresses_v6; - uv__interface_addresses_v6(&addresses_v6, &count_v6); + if ((rc = uv__interface_addresses_v6(&addresses_v6, &count_v6)) != 0) + return rc; /* now get the ipv4 addresses */ @@ -393,12 +420,27 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { maxsize = 16384; sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - if (0 > sockfd) + if (0 > sockfd) { + if (count_v6) + uv_free_interface_addresses(addresses_v6, count_v6); return UV__ERR(errno); + } ifc.ifc_req = uv__calloc(1, maxsize); + + if (ifc.ifc_req == NULL) { + if (count_v6) + uv_free_interface_addresses(addresses_v6, count_v6); + uv__close(sockfd); + return UV_ENOMEM; + } + ifc.ifc_len = maxsize; + if (ioctl(sockfd, SIOCGIFCONF, &ifc) == -1) { + if (count_v6) + uv_free_interface_addresses(addresses_v6, count_v6); + uv__free(ifc.ifc_req); uv__close(sockfd); return UV__ERR(errno); } @@ -419,6 +461,9 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { + if (count_v6) + uv_free_interface_addresses(addresses_v6, count_v6); + uv__free(ifc.ifc_req); uv__close(sockfd); return UV__ERR(errno); } @@ -429,27 +474,35 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { (*count)++; } - if (*count == 0) { + if (*count == 0 && count_v6 == 0) { + uv__free(ifc.ifc_req); uv__close(sockfd); return 0; } /* Alloc the return interface structs */ - *addresses = uv__malloc((*count + count_v6) * + *addresses = uv__calloc(1, (*count + count_v6) * sizeof(uv_interface_address_t)); if (!(*addresses)) { + if (count_v6) + uv_free_interface_addresses(addresses_v6, count_v6); + uv__free(ifc.ifc_req); uv__close(sockfd); return UV_ENOMEM; } address = *addresses; - /* copy over the ipv6 addresses */ - memcpy(address, addresses_v6, count_v6 * sizeof(uv_interface_address_t)); - address += count_v6; - *count += count_v6; - uv__free(addresses_v6); + /* copy over the ipv6 addresses if any are found */ + if (count_v6) { + memcpy(address, addresses_v6, count_v6 * sizeof(uv_interface_address_t)); + address += count_v6; + *count += count_v6; + /* free ipv6 addresses, but keep address names */ + uv__free(addresses_v6); + } + count_names = *count; ifr = ifc.ifc_req; while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { p = ifr; @@ -462,6 +515,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { + uv_free_interface_addresses(*addresses, count_names); + uv__free(ifc.ifc_req); uv__close(sockfd); return UV_ENOSYS; } @@ -478,15 +533,22 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { p->ifr_name[i] != 0) ++i; address->name = uv__malloc(i + 1); - if (address->name == NULL) + if (address->name == NULL) { + uv_free_interface_addresses(*addresses, count_names); + uv__free(ifc.ifc_req); + uv__close(sockfd); return UV_ENOMEM; + } memcpy(address->name, p->ifr_name, i); address->name[i] = '\0'; __e2a_s(address->name); + count_names++; address->address.address4 = *((struct sockaddr_in*) &p->ifr_addr); if (ioctl(sockfd, SIOCGIFNETMASK, p) == -1) { + uv_free_interface_addresses(*addresses, count_names); + uv__free(ifc.ifc_req); uv__close(sockfd); return UV__ERR(errno); } @@ -494,13 +556,13 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { address->netmask.netmask4 = *((struct sockaddr_in*) &p->ifr_addr); address->netmask.netmask4.sin_family = AF_INET; address->is_internal = flg.ifr_flags & IFF_LOOPBACK ? 1 : 0; - memset(address->phys_addr, 0, sizeof(address->phys_addr)); address++; } #undef ADDR_SIZE #undef MAX + uv__free(ifc.ifc_req); uv__close(sockfd); return 0; }