File glibc-CVE-2015-7547-patch_3of3_res_send.c.diff of Package glibc.4601
diff -rNU 30 ../glibc-2.18-orig/resolv/res_send.c ./resolv/res_send.c
--- ../glibc-2.18-orig/resolv/res_send.c 2016-02-17 19:02:50.000000000 +0100
+++ ./resolv/res_send.c 2016-02-17 19:25:30.000000000 +0100
@@ -634,65 +634,70 @@
if (!v_circuit) {
if (!gotsomewhere)
__set_errno (ECONNREFUSED); /* no nameservers found */
else
__set_errno (ETIMEDOUT); /* no answer obtained */
} else
__set_errno (terrno);
return (-1);
}
int
res_nsend(res_state statp,
const u_char *buf, int buflen, u_char *ans, int anssiz)
{
return __libc_res_nsend(statp, buf, buflen, NULL, 0, ans, anssiz,
NULL, NULL, NULL, NULL);
}
libresolv_hidden_def (res_nsend)
/* Private */
static int
send_vc(res_state statp,
const u_char *buf, int buflen, const u_char *buf2, int buflen2,
u_char **ansp, int *anssizp,
int *terrno, int ns, u_char **anscp, u_char **ansp2, int *anssizp2,
int *resplen2)
{
const HEADER *hp = (HEADER *) buf;
const HEADER *hp2 = (HEADER *) buf2;
+#if 0
u_char *ans = *ansp;
int orig_anssizp = *anssizp;
// XXX REMOVE
// int anssiz = *anssizp;
HEADER *anhp = (HEADER *) ans;
+#endif
+/* CVE-2015-7547 */
+ HEADER *anhp = (HEADER *) *ansp;
+/* end CVE-2015-7547 for now... */
struct sockaddr_in6 *nsap = EXT(statp).nsaddrs[ns];
int truncating, connreset, resplen, n;
struct iovec iov[4];
u_short len;
u_short len2;
u_char *cp;
if (resplen2 != NULL)
*resplen2 = 0;
connreset = 0;
same_ns:
truncating = 0;
/* Are we still talking to whom we want to talk to? */
if (statp->_vcsock >= 0 && (statp->_flags & RES_F_VC) != 0) {
struct sockaddr_in6 peer;
socklen_t size = sizeof peer;
if (getpeername(statp->_vcsock,
(struct sockaddr *)&peer, &size) < 0 ||
!sock_eq(&peer, nsap)) {
__res_iclose(statp, false);
statp->_flags &= ~RES_F_VC;
}
}
if (statp->_vcsock < 0 || (statp->_flags & RES_F_VC) == 0) {
if (statp->_vcsock >= 0)
__res_iclose(statp, false);
@@ -755,93 +760,103 @@
if (n <= 0) {
*terrno = errno;
Perror(statp, stderr, "read failed", errno);
__res_iclose(statp, false);
/*
* A long running process might get its TCP
* connection reset if the remote server was
* restarted. Requery the server instead of
* trying a new one. When there is only one
* server, this means that a query might work
* instead of failing. We only allow one reset
* per query to prevent looping.
*/
if (*terrno == ECONNRESET && !connreset) {
connreset = 1;
goto same_ns;
}
return (0);
}
int rlen = ntohs (rlen16);
int *thisanssizp;
u_char **thisansp;
int *thisresplenp;
if ((recvresp1 | recvresp2) == 0 || buf2 == NULL) {
thisanssizp = anssizp;
thisansp = anscp ?: ansp;
assert (anscp != NULL || ansp2 == NULL);
thisresplenp = &resplen;
} else {
+#if 0 /* CVE-2015-7547 until end of #if 0 */
if (*anssizp != MAXPACKET) {
/* No buffer allocated for the first
reply. We can try to use the rest
of the user-provided buffer. */
#ifdef _STRING_ARCH_unaligned
*anssizp2 = orig_anssizp - resplen;
*ansp2 = *ansp + resplen;
#else
int aligned_resplen
= ((resplen + __alignof__ (HEADER) - 1)
& ~(__alignof__ (HEADER) - 1));
*anssizp2 = orig_anssizp - aligned_resplen;
*ansp2 = *ansp + aligned_resplen;
#endif
} else {
/* The first reply did not fit into the
user-provided buffer. Maybe the second
answer will. */
*anssizp2 = orig_anssizp;
*ansp2 = *ansp;
}
-
+#endif
thisanssizp = anssizp2;
thisansp = ansp2;
thisresplenp = resplen2;
}
anhp = (HEADER *) *thisansp;
*thisresplenp = rlen;
+#if 0
if (rlen > *thisanssizp) {
/* Yes, we test ANSCP here. If we have two buffers
both will be allocatable. */
if (__builtin_expect (anscp != NULL, 1)) {
+#endif
+ if (*thisanssizp < rlen) {
+ /* If the current buffer is not the the static
+ user-supplied buffer then we can reallocate
+ it. */
+ if (thisansp != NULL && thisansp != ansp) {
+ /* Always allocate MAXPACKET, callers expect
+ this specific size. */
u_char *newp = malloc (MAXPACKET);
if (newp == NULL) {
*terrno = ENOMEM;
__res_iclose(statp, false);
return (0);
}
*thisanssizp = MAXPACKET;
*thisansp = newp;
anhp = (HEADER *) newp;
len = rlen;
} else {
Dprint(statp->options & RES_DEBUG,
(stdout, ";; response truncated\n")
);
truncating = 1;
len = *thisanssizp;
}
} else
len = rlen;
if (__builtin_expect (len < HFIXEDSZ, 0)) {
/*
* Undersized message.
*/
Dprint(statp->options & RES_DEBUG,
(stdout, ";; undersized: %d\n", len));
*terrno = EMSGSIZE;
__res_iclose(statp, false);
return (0);
}
@@ -969,62 +984,64 @@
* the absence of a nameserver without timing out.
*/
if (connect(EXT(statp).nssocks[ns], nsap, slen) < 0) {
Aerror(statp, stderr, "connect(dg)", errno, nsap);
__res_iclose(statp, false);
return (0);
}
if (__builtin_expect (__have_o_nonblock < 0, 0)) {
/* Make socket non-blocking. */
int fl = __fcntl (EXT(statp).nssocks[ns], F_GETFL);
if (fl != -1)
__fcntl (EXT(statp).nssocks[ns], F_SETFL,
fl | O_NONBLOCK);
Dprint(statp->options & RES_DEBUG,
(stdout, ";; new DG socket\n"))
}
}
return 1;
}
static int
send_dg(res_state statp,
const u_char *buf, int buflen, const u_char *buf2, int buflen2,
u_char **ansp, int *anssizp,
int *terrno, int ns, int *v_circuit, int *gotsomewhere, u_char **anscp,
u_char **ansp2, int *anssizp2, int *resplen2)
{
const HEADER *hp = (HEADER *) buf;
const HEADER *hp2 = (HEADER *) buf2;
+#if 0 /* CVE-2015-7547 */
u_char *ans = *ansp;
int orig_anssizp = *anssizp;
+#endif
struct timespec now, timeout, finish;
struct pollfd pfd[1];
int ptimeout;
struct sockaddr_in6 from;
int resplen = 0;
int n;
/*
* Compute time for the total operation.
*/
int seconds = (statp->retrans << ns);
if (ns > 0)
seconds /= statp->nscount;
if (seconds <= 0)
seconds = 1;
bool single_request_reopen = (statp->options & RES_SNGLKUPREOP) != 0;
bool single_request = (((statp->options & RES_SNGLKUP) != 0)
| single_request_reopen);
int save_gotsomewhere = *gotsomewhere;
int retval;
retry_reopen:
retval = reopen (statp, terrno, ns);
if (retval <= 0)
return retval;
retry:
evNowTime(&now);
evConsTime(&timeout, seconds, 0);
evAddTime(&finish, &now, &timeout);
int need_recompute = 0;
@@ -1171,99 +1188,128 @@
if (nwritten != 0)
sr = send (pfd[0].fd, buf2, buflen2, MSG_NOSIGNAL);
else
sr = send (pfd[0].fd, buf, buflen, MSG_NOSIGNAL);
if (sr != (nwritten != 0 ? buflen2 : buflen)) {
if (errno == EINTR || errno == EAGAIN)
goto recompute_resend;
Perror(statp, stderr, "send", errno);
goto err_out;
}
just_one:
if (nwritten != 0 || buf2 == NULL || single_request)
pfd[0].events = POLLIN;
else
pfd[0].events = POLLIN | POLLOUT;
++nwritten;
}
goto wait;
} else if (pfd[0].revents & POLLIN) {
int *thisanssizp;
u_char **thisansp;
int *thisresplenp;
if ((recvresp1 | recvresp2) == 0 || buf2 == NULL) {
thisanssizp = anssizp;
thisansp = anscp ?: ansp;
assert (anscp != NULL || ansp2 == NULL);
thisresplenp = &resplen;
} else {
+#if 0 /* CVE-2015-7547 */
if (*anssizp != MAXPACKET) {
/* No buffer allocated for the first
reply. We can try to use the rest
of the user-provided buffer. */
#ifdef _STRING_ARCH_unaligned
*anssizp2 = orig_anssizp - resplen;
*ansp2 = *ansp + resplen;
#else
int aligned_resplen
= ((resplen + __alignof__ (HEADER) - 1)
& ~(__alignof__ (HEADER) - 1));
*anssizp2 = orig_anssizp - aligned_resplen;
*ansp2 = *ansp + aligned_resplen;
#endif
} else {
/* The first reply did not fit into the
user-provided buffer. Maybe the second
answer will. */
*anssizp2 = orig_anssizp;
*ansp2 = *ansp;
}
-
+#endif
thisanssizp = anssizp2;
thisansp = ansp2;
thisresplenp = resplen2;
}
if (*thisanssizp < MAXPACKET
+#if 0 /* CVE-2015-7547 */
/* Yes, we test ANSCP here. If we have two buffers
both will be allocatable. */
&& anscp
+#endif
+ /* If the current buffer is not the the static
+ user-supplied buffer then we can reallocate it.
+ */
+ && (thisansp != NULL && thisansp != ansp)
+ /* too small? */
&& (ioctl (pfd[0].fd, FIONREAD, thisresplenp) < 0
|| *thisanssizp < *thisresplenp)) {
+ /* Always allocate MAXPACKET, callers expect
+ this specific size. */
u_char *newp = malloc (MAXPACKET);
if (newp != NULL) {
+#if 0 /* CVE-2015-7547 */
*anssizp = MAXPACKET;
*thisansp = ans = newp;
+#endif
+ *thisanssizp = MAXPACKET;
+ *thisansp = newp;
}
}
+ /* We could end up with truncation if anscp was NULL
+ (not allowed to change caller's buffer) and the
+ response buffer size is too small. This isn't a
+ reliable way to detect truncation because the ioctl
+ may be an inaccurate report of the UDP message size.
+ Therefore we use this only to issue debug output.
+ To do truncation accurately with UDP we need
+ MSG_TRUNC which is only available on Linux. We
+ can abstract out the Linux-specific feature in the
+ future to detect truncation. */
+ if (*thisanssizp < *thisresplenp) {
+ Dprint(statp->options & RES_DEBUG,
+ (stdout, ";; response may be truncated (UDP)\n")
+ );
+ }
HEADER *anhp = (HEADER *) *thisansp;
socklen_t fromlen = sizeof(struct sockaddr_in6);
assert (sizeof(from) <= fromlen);
*thisresplenp = recvfrom(pfd[0].fd, (char*)*thisansp,
*thisanssizp, 0,
(struct sockaddr *)&from, &fromlen);
if (__builtin_expect (*thisresplenp <= 0, 0)) {
if (errno == EINTR || errno == EAGAIN) {
need_recompute = 1;
goto wait;
}
Perror(statp, stderr, "recvfrom", errno);
goto err_out;
}
*gotsomewhere = 1;
if (__builtin_expect (*thisresplenp < HFIXEDSZ, 0)) {
/*
* Undersized message.
*/
Dprint(statp->options & RES_DEBUG,
(stdout, ";; undersized: %d\n",
*thisresplenp));
*terrno = EMSGSIZE;
goto err_out;
}
if ((recvresp1 || hp->id != anhp->id)
&& (recvresp2 || hp2->id != anhp->id)) {
/*
* response from old query, ignore it.
* XXX - potential security hazard could