1 From 1a8a7c12950a0026a3c406a7cb1608f96aa1460e Mon Sep 17 00:00:00 2001
2 From: Florian Weimer <fweimer@redhat.com>
3 Date: Fri, 29 Apr 2016 10:35:34 +0200
4 Subject: [PATCH] CVE-2016-3706: getaddrinfo: stack overflow in hostent
7 When converting a struct hostent response to struct gaih_addrtuple, the
8 gethosts macro (which is called from gaih_inet) used alloca, without
9 malloc fallback for large responses. This commit changes this code to
10 use calloc unconditionally.
12 This commit also consolidated a second hostent-to-gaih_addrtuple
13 conversion loop (in gaih_inet) to use the new conversion function.
15 (cherry picked from commit 4ab2ab03d4351914ee53248dc5aef4a8c88ff8b9)
18 sysdeps/posix/getaddrinfo.c | 130 +++++++++++++++++++++++---------------------
19 2 files changed, 79 insertions(+), 61 deletions(-)
21 diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
22 index 1ef3f20..fed2d3b 100644
23 --- a/sysdeps/posix/getaddrinfo.c
24 +++ b/sysdeps/posix/getaddrinfo.c
25 @@ -168,9 +168,58 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
29 +/* Convert struct hostent to a list of struct gaih_addrtuple objects.
30 + h_name is not copied, and the struct hostent object must not be
31 + deallocated prematurely. *RESULT must be NULL or a pointer to an
32 + object allocated using malloc, which is freed. */
34 +convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
37 + struct gaih_addrtuple **result)
42 + /* Count the number of addresses in h->h_addr_list. */
44 + for (char **p = h->h_addr_list; *p != NULL; ++p)
47 + /* Report no data if no addresses are available, or if the incoming
48 + address size is larger than what we can store. */
49 + if (count == 0 || h->h_length > sizeof (((struct gaih_addrtuple) {}).addr))
52 + struct gaih_addrtuple *array = calloc (count, sizeof (*array));
56 + for (size_t i = 0; i < count; ++i)
58 + if (family == AF_INET && req->ai_family == AF_INET6)
60 + /* Perform address mapping. */
61 + array[i].family = AF_INET6;
62 + memcpy(array[i].addr + 3, h->h_addr_list[i], sizeof (uint32_t));
63 + array[i].addr[2] = htonl (0xffff);
67 + array[i].family = family;
68 + memcpy (array[i].addr, h->h_addr_list[i], h->h_length);
70 + array[i].next = array + i + 1;
72 + array[0].name = h->h_name;
73 + array[count - 1].next = NULL;
79 #define gethosts(_family, _type) \
85 @@ -219,36 +268,23 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
89 - for (i = 0; h->h_addr_list[i]; i++) \
90 + /* Make sure that addrmem can be freed. */ \
91 + if (!malloc_addrmem) \
93 + if (!convert_hostent_to_gaih_addrtuple (req, _family,h, &addrmem)) \
97 - *pat = __alloca (sizeof (struct gaih_addrtuple)); \
98 - (*pat)->scopeid = 0; \
100 - uint32_t *addr = (*pat)->addr; \
101 - (*pat)->next = NULL; \
102 - (*pat)->name = i == 0 ? strdupa (h->h_name) : NULL; \
103 - if (_family == AF_INET && req->ai_family == AF_INET6) \
105 - (*pat)->family = AF_INET6; \
106 - addr[3] = *(uint32_t *) h->h_addr_list[i]; \
107 - addr[2] = htonl (0xffff); \
113 - (*pat)->family = _family; \
114 - memcpy (addr, h->h_addr_list[i], sizeof(_type)); \
116 - pat = &((*pat)->next); \
117 + _res.options |= old_res_options & RES_USE_INET6; \
118 + result = -EAI_SYSTEM; \
119 + goto free_and_return; \
122 + /* The conversion uses malloc unconditionally. */ \
123 + malloc_addrmem = true; \
125 if (localcanon != NULL && canon == NULL) \
126 canon = strdupa (localcanon); \
128 - if (_family == AF_INET6 && i > 0) \
129 + if (_family == AF_INET6 && *pat != NULL) \
133 @@ -612,44 +648,16 @@ gaih_inet (const char *name, const struct gaih_service *service,
138 - /* We found data, count the number of addresses. */
139 - for (i = 0; h->h_addr_list[i]; ++i)
141 - if (i > 0 && *pat != NULL)
144 - if (__libc_use_alloca (alloca_used
145 - + i * sizeof (struct gaih_addrtuple)))
146 - addrmem = alloca_account (i * sizeof (struct gaih_addrtuple),
150 - addrmem = malloc (i
151 - * sizeof (struct gaih_addrtuple));
152 - if (addrmem == NULL)
154 - result = -EAI_MEMORY;
155 - goto free_and_return;
157 - malloc_addrmem = true;
160 - /* Now convert it into the list. */
161 - struct gaih_addrtuple *addrfree = addrmem;
162 - for (i = 0; h->h_addr_list[i]; ++i)
163 + /* We found data, convert it. */
164 + if (!convert_hostent_to_gaih_addrtuple
165 + (req, AF_INET, h, &addrmem))
170 - (*pat)->scopeid = 0;
172 - (*pat)->next = NULL;
173 - (*pat)->family = AF_INET;
174 - memcpy ((*pat)->addr, h->h_addr_list[i],
176 - pat = &((*pat)->next);
177 + result = -EAI_MEMORY;
178 + goto free_and_return;
181 + /* The conversion uses malloc unconditionally. */
182 + malloc_addrmem = true;