189 lines
6.1 KiB
Diff
189 lines
6.1 KiB
Diff
From 1a8a7c12950a0026a3c406a7cb1608f96aa1460e Mon Sep 17 00:00:00 2001
|
|
From: Florian Weimer <fweimer@redhat.com>
|
|
Date: Fri, 29 Apr 2016 10:35:34 +0200
|
|
Subject: [PATCH] CVE-2016-3706: getaddrinfo: stack overflow in hostent
|
|
conversion [BZ #20010]
|
|
|
|
When converting a struct hostent response to struct gaih_addrtuple, the
|
|
gethosts macro (which is called from gaih_inet) used alloca, without
|
|
malloc fallback for large responses. This commit changes this code to
|
|
use calloc unconditionally.
|
|
|
|
This commit also consolidated a second hostent-to-gaih_addrtuple
|
|
conversion loop (in gaih_inet) to use the new conversion function.
|
|
|
|
(cherry picked from commit 4ab2ab03d4351914ee53248dc5aef4a8c88ff8b9)
|
|
---
|
|
ChangeLog | 10 ++++
|
|
sysdeps/posix/getaddrinfo.c | 130 +++++++++++++++++++++++---------------------
|
|
2 files changed, 79 insertions(+), 61 deletions(-)
|
|
|
|
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
|
|
index 1ef3f20..fed2d3b 100644
|
|
--- a/sysdeps/posix/getaddrinfo.c
|
|
+++ b/sysdeps/posix/getaddrinfo.c
|
|
@@ -168,9 +168,58 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
|
|
return 0;
|
|
}
|
|
|
|
+/* Convert struct hostent to a list of struct gaih_addrtuple objects.
|
|
+ h_name is not copied, and the struct hostent object must not be
|
|
+ deallocated prematurely. *RESULT must be NULL or a pointer to an
|
|
+ object allocated using malloc, which is freed. */
|
|
+static bool
|
|
+convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
|
|
+ int family,
|
|
+ struct hostent *h,
|
|
+ struct gaih_addrtuple **result)
|
|
+{
|
|
+ free (*result);
|
|
+ *result = NULL;
|
|
+
|
|
+ /* Count the number of addresses in h->h_addr_list. */
|
|
+ size_t count = 0;
|
|
+ for (char **p = h->h_addr_list; *p != NULL; ++p)
|
|
+ ++count;
|
|
+
|
|
+ /* Report no data if no addresses are available, or if the incoming
|
|
+ address size is larger than what we can store. */
|
|
+ if (count == 0 || h->h_length > sizeof (((struct gaih_addrtuple) {}).addr))
|
|
+ return true;
|
|
+
|
|
+ struct gaih_addrtuple *array = calloc (count, sizeof (*array));
|
|
+ if (array == NULL)
|
|
+ return false;
|
|
+
|
|
+ for (size_t i = 0; i < count; ++i)
|
|
+ {
|
|
+ if (family == AF_INET && req->ai_family == AF_INET6)
|
|
+ {
|
|
+ /* Perform address mapping. */
|
|
+ array[i].family = AF_INET6;
|
|
+ memcpy(array[i].addr + 3, h->h_addr_list[i], sizeof (uint32_t));
|
|
+ array[i].addr[2] = htonl (0xffff);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ array[i].family = family;
|
|
+ memcpy (array[i].addr, h->h_addr_list[i], h->h_length);
|
|
+ }
|
|
+ array[i].next = array + i + 1;
|
|
+ }
|
|
+ array[0].name = h->h_name;
|
|
+ array[count - 1].next = NULL;
|
|
+
|
|
+ *result = array;
|
|
+ return true;
|
|
+}
|
|
+
|
|
#define gethosts(_family, _type) \
|
|
{ \
|
|
- int i; \
|
|
int herrno; \
|
|
struct hostent th; \
|
|
struct hostent *h; \
|
|
@@ -219,36 +268,23 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
|
|
} \
|
|
else if (h != NULL) \
|
|
{ \
|
|
- for (i = 0; h->h_addr_list[i]; i++) \
|
|
+ /* Make sure that addrmem can be freed. */ \
|
|
+ if (!malloc_addrmem) \
|
|
+ addrmem = NULL; \
|
|
+ if (!convert_hostent_to_gaih_addrtuple (req, _family,h, &addrmem)) \
|
|
{ \
|
|
- if (*pat == NULL) \
|
|
- { \
|
|
- *pat = __alloca (sizeof (struct gaih_addrtuple)); \
|
|
- (*pat)->scopeid = 0; \
|
|
- } \
|
|
- uint32_t *addr = (*pat)->addr; \
|
|
- (*pat)->next = NULL; \
|
|
- (*pat)->name = i == 0 ? strdupa (h->h_name) : NULL; \
|
|
- if (_family == AF_INET && req->ai_family == AF_INET6) \
|
|
- { \
|
|
- (*pat)->family = AF_INET6; \
|
|
- addr[3] = *(uint32_t *) h->h_addr_list[i]; \
|
|
- addr[2] = htonl (0xffff); \
|
|
- addr[1] = 0; \
|
|
- addr[0] = 0; \
|
|
- } \
|
|
- else \
|
|
- { \
|
|
- (*pat)->family = _family; \
|
|
- memcpy (addr, h->h_addr_list[i], sizeof(_type)); \
|
|
- } \
|
|
- pat = &((*pat)->next); \
|
|
+ _res.options |= old_res_options & RES_USE_INET6; \
|
|
+ result = -EAI_SYSTEM; \
|
|
+ goto free_and_return; \
|
|
} \
|
|
+ *pat = addrmem; \
|
|
+ /* The conversion uses malloc unconditionally. */ \
|
|
+ malloc_addrmem = true; \
|
|
\
|
|
if (localcanon != NULL && canon == NULL) \
|
|
canon = strdupa (localcanon); \
|
|
\
|
|
- if (_family == AF_INET6 && i > 0) \
|
|
+ if (_family == AF_INET6 && *pat != NULL) \
|
|
got_ipv6 = true; \
|
|
} \
|
|
}
|
|
@@ -612,44 +648,16 @@ gaih_inet (const char *name, const struct gaih_service *service,
|
|
{
|
|
if (h != NULL)
|
|
{
|
|
- int i;
|
|
- /* We found data, count the number of addresses. */
|
|
- for (i = 0; h->h_addr_list[i]; ++i)
|
|
- ;
|
|
- if (i > 0 && *pat != NULL)
|
|
- --i;
|
|
-
|
|
- if (__libc_use_alloca (alloca_used
|
|
- + i * sizeof (struct gaih_addrtuple)))
|
|
- addrmem = alloca_account (i * sizeof (struct gaih_addrtuple),
|
|
- alloca_used);
|
|
- else
|
|
- {
|
|
- addrmem = malloc (i
|
|
- * sizeof (struct gaih_addrtuple));
|
|
- if (addrmem == NULL)
|
|
- {
|
|
- result = -EAI_MEMORY;
|
|
- goto free_and_return;
|
|
- }
|
|
- malloc_addrmem = true;
|
|
- }
|
|
-
|
|
- /* Now convert it into the list. */
|
|
- struct gaih_addrtuple *addrfree = addrmem;
|
|
- for (i = 0; h->h_addr_list[i]; ++i)
|
|
+ /* We found data, convert it. */
|
|
+ if (!convert_hostent_to_gaih_addrtuple
|
|
+ (req, AF_INET, h, &addrmem))
|
|
{
|
|
- if (*pat == NULL)
|
|
- {
|
|
- *pat = addrfree++;
|
|
- (*pat)->scopeid = 0;
|
|
- }
|
|
- (*pat)->next = NULL;
|
|
- (*pat)->family = AF_INET;
|
|
- memcpy ((*pat)->addr, h->h_addr_list[i],
|
|
- h->h_length);
|
|
- pat = &((*pat)->next);
|
|
+ result = -EAI_MEMORY;
|
|
+ goto free_and_return;
|
|
}
|
|
+ *pat = addrmem;
|
|
+ /* The conversion uses malloc unconditionally. */
|
|
+ malloc_addrmem = true;
|
|
}
|
|
}
|
|
else
|
|
--
|
|
2.9.3
|
|
|