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
openSUSE Build Service is sponsored by