|
|
a7d3f6 |
commit b66d837bb5398795c6b0f651bd5a5d66091d8577
|
|
|
a7d3f6 |
Author: Florian Weimer <fweimer@redhat.com>
|
|
|
a7d3f6 |
Date: Fri Mar 25 11:49:51 2016 +0100
|
|
|
a7d3f6 |
|
|
|
a7d3f6 |
resolv: Always set *resplen2 out parameter in send_dg [BZ #19791]
|
|
|
a7d3f6 |
|
|
|
a7d3f6 |
Since commit 44d20bca52ace85850012b0ead37b360e3ecd96e (Implement
|
|
|
a7d3f6 |
second fallback mode for DNS requests), there is a code path which
|
|
|
a7d3f6 |
returns early, before *resplen2 is initialized. This happens if the
|
|
|
a7d3f6 |
name server address is immediately recognized as invalid (because of
|
|
|
a7d3f6 |
lack of protocol support, or if it is a broadcast address such
|
|
|
a7d3f6 |
255.255.255.255, or another invalid address).
|
|
|
a7d3f6 |
|
|
|
a7d3f6 |
If this happens and *resplen2 was non-zero (which is the case if a
|
|
|
a7d3f6 |
previous query resulted in a failure), __libc_res_nquery would reuse
|
|
|
a7d3f6 |
an existing second answer buffer. This answer has been previously
|
|
|
a7d3f6 |
identified as unusable (for example, it could be an NXDOMAIN
|
|
|
a7d3f6 |
response). Due to the presence of a second answer, no name server
|
|
|
a7d3f6 |
switching will occur. The result is a name resolution failure,
|
|
|
a7d3f6 |
although a successful resolution would have been possible if name
|
|
|
a7d3f6 |
servers have been switched and queries had proceeded along the search
|
|
|
a7d3f6 |
path.
|
|
|
a7d3f6 |
|
|
|
a7d3f6 |
The above paragraph still simplifies the situation. Before glibc
|
|
|
a7d3f6 |
2.23, if the second answer needed malloc, the stub resolver would
|
|
|
a7d3f6 |
still attempt to reuse the second answer, but this is not possible
|
|
|
a7d3f6 |
because __libc_res_nsearch has freed it, after the unsuccessful call
|
|
|
a7d3f6 |
to __libc_res_nquerydomain, and set the buffer pointer to NULL. This
|
|
|
a7d3f6 |
eventually leads to an assertion failure in __libc_res_nquery:
|
|
|
a7d3f6 |
|
|
|
a7d3f6 |
/* Make sure both hp and hp2 are defined */
|
|
|
a7d3f6 |
assert((hp != NULL) && (hp2 != NULL));
|
|
|
a7d3f6 |
|
|
|
a7d3f6 |
If assertions are disabled, the consequence is a NULL pointer
|
|
|
a7d3f6 |
dereference on the next line.
|
|
|
a7d3f6 |
|
|
|
a7d3f6 |
Starting with glibc 2.23, as a result of commit
|
|
|
a7d3f6 |
e9db92d3acfe1822d56d11abcea5bfc4c41cf6ca (CVE-2015-7547: getaddrinfo()
|
|
|
a7d3f6 |
stack-based buffer overflow (Bug 18665)), the second answer is always
|
|
|
a7d3f6 |
allocated with malloc. This means that the assertion failure happens
|
|
|
a7d3f6 |
with small responses as well because there is no buffer to reuse, as
|
|
|
a7d3f6 |
soon as there is a name resolution failure which triggers a search for
|
|
|
a7d3f6 |
an answer along the search path.
|
|
|
a7d3f6 |
|
|
|
a7d3f6 |
This commit addresses the issue by ensuring that *resplen2 is
|
|
|
a7d3f6 |
initialized before the send_dg function returns.
|
|
|
a7d3f6 |
|
|
|
a7d3f6 |
This commit also addresses a bug where an invalid second reply is
|
|
|
a7d3f6 |
incorrectly returned as a valid to the caller.
|
|
|
a7d3f6 |
|
|
|
a7d3f6 |
Index: glibc-2.17-c758a686/resolv/res_send.c
|
|
|
a7d3f6 |
===================================================================
|
|
|
a7d3f6 |
--- glibc-2.17-c758a686.orig/resolv/res_send.c
|
|
|
a7d3f6 |
+++ glibc-2.17-c758a686/resolv/res_send.c
|
|
|
a7d3f6 |
@@ -672,6 +672,18 @@ libresolv_hidden_def (res_nsend)
|
|
|
a7d3f6 |
|
|
|
a7d3f6 |
/* Private */
|
|
|
a7d3f6 |
|
|
|
a7d3f6 |
+/* Close the resolver structure, assign zero to *RESPLEN2 if RESPLEN2
|
|
|
a7d3f6 |
+ is not NULL, and return zero. */
|
|
|
a7d3f6 |
+static int
|
|
|
a7d3f6 |
+__attribute__ ((warn_unused_result))
|
|
|
a7d3f6 |
+close_and_return_error (res_state statp, int *resplen2)
|
|
|
a7d3f6 |
+{
|
|
|
a7d3f6 |
+ __res_iclose(statp, false);
|
|
|
a7d3f6 |
+ if (resplen2 != NULL)
|
|
|
a7d3f6 |
+ *resplen2 = 0;
|
|
|
a7d3f6 |
+ return 0;
|
|
|
a7d3f6 |
+}
|
|
|
a7d3f6 |
+
|
|
|
a7d3f6 |
/* The send_vc function is responsible for sending a DNS query over TCP
|
|
|
a7d3f6 |
to the nameserver numbered NS from the res_state STATP i.e.
|
|
|
a7d3f6 |
EXT(statp).nssocks[ns]. The function supports sending both IPv4 and
|
|
|
a7d3f6 |
@@ -1159,7 +1171,11 @@ send_dg(res_state statp,
|
|
|
a7d3f6 |
retry_reopen:
|
|
|
a7d3f6 |
retval = reopen (statp, terrno, ns);
|
|
|
a7d3f6 |
if (retval <= 0)
|
|
|
a7d3f6 |
- return retval;
|
|
|
a7d3f6 |
+ {
|
|
|
a7d3f6 |
+ if (resplen2 != NULL)
|
|
|
a7d3f6 |
+ *resplen2 = 0;
|
|
|
a7d3f6 |
+ return retval;
|
|
|
a7d3f6 |
+ }
|
|
|
a7d3f6 |
retry:
|
|
|
a7d3f6 |
evNowTime(&now;;
|
|
|
a7d3f6 |
evConsTime(&timeout, seconds, 0);
|
|
|
a7d3f6 |
@@ -1172,8 +1188,6 @@ send_dg(res_state statp,
|
|
|
a7d3f6 |
int recvresp2 = buf2 == NULL;
|
|
|
a7d3f6 |
pfd[0].fd = EXT(statp).nssocks[ns];
|
|
|
a7d3f6 |
pfd[0].events = POLLOUT;
|
|
|
a7d3f6 |
- if (resplen2 != NULL)
|
|
|
a7d3f6 |
- *resplen2 = 0;
|
|
|
a7d3f6 |
wait:
|
|
|
a7d3f6 |
if (need_recompute) {
|
|
|
a7d3f6 |
recompute_resend:
|
|
|
a7d3f6 |
@@ -1181,9 +1195,7 @@ send_dg(res_state statp,
|
|
|
a7d3f6 |
if (evCmpTime(finish, now) <= 0) {
|
|
|
a7d3f6 |
poll_err_out:
|
|
|
a7d3f6 |
Perror(statp, stderr, "poll", errno);
|
|
|
a7d3f6 |
- err_out:
|
|
|
a7d3f6 |
- __res_iclose(statp, false);
|
|
|
a7d3f6 |
- return (0);
|
|
|
a7d3f6 |
+ return close_and_return_error (statp, resplen2);
|
|
|
a7d3f6 |
}
|
|
|
a7d3f6 |
evSubTime(&timeout, &finish, &now;;
|
|
|
a7d3f6 |
need_recompute = 0;
|
|
|
a7d3f6 |
@@ -1230,7 +1242,9 @@ send_dg(res_state statp,
|
|
|
a7d3f6 |
}
|
|
|
a7d3f6 |
|
|
|
a7d3f6 |
*gotsomewhere = 1;
|
|
|
a7d3f6 |
- return (0);
|
|
|
a7d3f6 |
+ if (resplen2 != NULL)
|
|
|
a7d3f6 |
+ *resplen2 = 0;
|
|
|
a7d3f6 |
+ return 0;
|
|
|
a7d3f6 |
}
|
|
|
a7d3f6 |
if (n < 0) {
|
|
|
a7d3f6 |
if (errno == EINTR)
|
|
|
a7d3f6 |
@@ -1298,7 +1312,7 @@ send_dg(res_state statp,
|
|
|
a7d3f6 |
|
|
|
a7d3f6 |
fail_sendmmsg:
|
|
|
a7d3f6 |
Perror(statp, stderr, "sendmmsg", errno);
|
|
|
a7d3f6 |
- goto err_out;
|
|
|
a7d3f6 |
+ return close_and_return_error (statp, resplen2);
|
|
|
a7d3f6 |
}
|
|
|
a7d3f6 |
}
|
|
|
a7d3f6 |
else
|
|
|
a7d3f6 |
@@ -1316,7 +1330,7 @@ send_dg(res_state statp,
|
|
|
a7d3f6 |
if (errno == EINTR || errno == EAGAIN)
|
|
|
a7d3f6 |
goto recompute_resend;
|
|
|
a7d3f6 |
Perror(statp, stderr, "send", errno);
|
|
|
a7d3f6 |
- goto err_out;
|
|
|
a7d3f6 |
+ return close_and_return_error (statp, resplen2);
|
|
|
a7d3f6 |
}
|
|
|
a7d3f6 |
just_one:
|
|
|
a7d3f6 |
if (nwritten != 0 || buf2 == NULL || single_request)
|
|
|
a7d3f6 |
@@ -1392,7 +1406,7 @@ send_dg(res_state statp,
|
|
|
a7d3f6 |
goto wait;
|
|
|
a7d3f6 |
}
|
|
|
a7d3f6 |
Perror(statp, stderr, "recvfrom", errno);
|
|
|
a7d3f6 |
- goto err_out;
|
|
|
a7d3f6 |
+ return close_and_return_error (statp, resplen2);
|
|
|
a7d3f6 |
}
|
|
|
a7d3f6 |
*gotsomewhere = 1;
|
|
|
a7d3f6 |
if (__builtin_expect (*thisresplenp < HFIXEDSZ, 0)) {
|
|
|
a7d3f6 |
@@ -1403,7 +1417,7 @@ send_dg(res_state statp,
|
|
|
a7d3f6 |
(stdout, ";; undersized: %d\n",
|
|
|
a7d3f6 |
*thisresplenp));
|
|
|
a7d3f6 |
*terrno = EMSGSIZE;
|
|
|
a7d3f6 |
- goto err_out;
|
|
|
a7d3f6 |
+ return close_and_return_error (statp, resplen2);
|
|
|
a7d3f6 |
}
|
|
|
a7d3f6 |
if ((recvresp1 || hp->id != anhp->id)
|
|
|
a7d3f6 |
&& (recvresp2 || hp2->id != anhp->id)) {
|
|
|
a7d3f6 |
@@ -1452,7 +1466,7 @@ send_dg(res_state statp,
|
|
|
a7d3f6 |
? *thisanssizp : *thisresplenp);
|
|
|
a7d3f6 |
/* record the error */
|
|
|
a7d3f6 |
statp->_flags |= RES_F_EDNS0ERR;
|
|
|
a7d3f6 |
- goto err_out;
|
|
|
a7d3f6 |
+ return close_and_return_error (statp, resplen2);
|
|
|
a7d3f6 |
}
|
|
|
a7d3f6 |
#endif
|
|
|
a7d3f6 |
if (!(statp->options & RES_INSECURE2)
|
|
|
a7d3f6 |
@@ -1504,10 +1518,10 @@ send_dg(res_state statp,
|
|
|
a7d3f6 |
goto wait;
|
|
|
a7d3f6 |
}
|
|
|
a7d3f6 |
|
|
|
a7d3f6 |
- __res_iclose(statp, false);
|
|
|
a7d3f6 |
/* don't retry if called from dig */
|
|
|
a7d3f6 |
if (!statp->pfcode)
|
|
|
a7d3f6 |
- return (0);
|
|
|
a7d3f6 |
+ return close_and_return_error (statp, resplen2);
|
|
|
a7d3f6 |
+ __res_iclose(statp, false);
|
|
|
a7d3f6 |
}
|
|
|
a7d3f6 |
if (anhp->rcode == NOERROR && anhp->ancount == 0
|
|
|
a7d3f6 |
&& anhp->aa == 0 && anhp->ra == 0 && anhp->arcount == 0) {
|
|
|
a7d3f6 |
@@ -1529,6 +1543,8 @@ send_dg(res_state statp,
|
|
|
a7d3f6 |
__res_iclose(statp, false);
|
|
|
a7d3f6 |
// XXX if we have received one reply we could
|
|
|
a7d3f6 |
// XXX use it and not repeat it over TCP...
|
|
|
a7d3f6 |
+ if (resplen2 != NULL)
|
|
|
a7d3f6 |
+ *resplen2 = 0;
|
|
|
a7d3f6 |
return (1);
|
|
|
a7d3f6 |
}
|
|
|
a7d3f6 |
/* Mark which reply we received. */
|
|
|
a7d3f6 |
@@ -1544,21 +1560,22 @@ send_dg(res_state statp,
|
|
|
a7d3f6 |
__res_iclose (statp, false);
|
|
|
a7d3f6 |
retval = reopen (statp, terrno, ns);
|
|
|
a7d3f6 |
if (retval <= 0)
|
|
|
a7d3f6 |
- return retval;
|
|
|
a7d3f6 |
+ {
|
|
|
a7d3f6 |
+ if (resplen2 != NULL)
|
|
|
a7d3f6 |
+ *resplen2 = 0;
|
|
|
a7d3f6 |
+ return retval;
|
|
|
a7d3f6 |
+ }
|
|
|
a7d3f6 |
pfd[0].fd = EXT(statp).nssocks[ns];
|
|
|
a7d3f6 |
}
|
|
|
a7d3f6 |
}
|
|
|
a7d3f6 |
goto wait;
|
|
|
a7d3f6 |
}
|
|
|
a7d3f6 |
- /*
|
|
|
a7d3f6 |
- * All is well, or the error is fatal. Signal that the
|
|
|
a7d3f6 |
- * next nameserver ought not be tried.
|
|
|
a7d3f6 |
- */
|
|
|
a7d3f6 |
+ /* All is well. We have received both responses (if
|
|
|
a7d3f6 |
+ two responses were requested). */
|
|
|
a7d3f6 |
return (resplen);
|
|
|
a7d3f6 |
- } else if (pfd[0].revents & (POLLERR | POLLHUP | POLLNVAL)) {
|
|
|
a7d3f6 |
- /* Something went wrong. We can stop trying. */
|
|
|
a7d3f6 |
- goto err_out;
|
|
|
a7d3f6 |
- }
|
|
|
a7d3f6 |
+ } else if (pfd[0].revents & (POLLERR | POLLHUP | POLLNVAL))
|
|
|
a7d3f6 |
+ /* Something went wrong. We can stop trying. */
|
|
|
a7d3f6 |
+ return close_and_return_error (statp, resplen2);
|
|
|
a7d3f6 |
else {
|
|
|
a7d3f6 |
/* poll should not have returned > 0 in this case. */
|
|
|
a7d3f6 |
abort ();
|