public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
From: Sinan Lin <sinan.lin@linux.alibaba.com>
To: libc-alpha@sourceware.org
Cc: Sinan Lin <sinan.lin@linux.alibaba.com>
Subject: [PATCH] resolv: Set a timeout for TCP-based query read (bug 19643)
Date: Fri, 29 Jul 2022 16:51:59 +0800	[thread overview]
Message-ID: <20220729085159.68458-1-sinan.lin@linux.alibaba.com> (raw)

There was no timeout set to tcp socket read during name resolution, and this
might lead to a hang on read when DNS response is too large. This patch proposes
a default 10 seconds timeout for TCP-based query read, and it currently does not
include the connection and write part.

Signed-off-by: Sinan Lin <sinan.lin@linux.alibaba.com>
---
 inet/deadline.c     | 15 +++++++++++++
 inet/net-internal.h |  5 +++++
 resolv/res_send.c   | 55 ++++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 70 insertions(+), 5 deletions(-)

diff --git a/inet/deadline.c b/inet/deadline.c
index 7deb39b0ec..138e7a3351 100644
--- a/inet/deadline.c
+++ b/inet/deadline.c
@@ -73,6 +73,21 @@ __deadline_from_timeval (struct deadline_current_time current,
   return (struct deadline) { { sec, nsec } };
 }
 
+struct deadline
+__deadline_from_sec (struct deadline_current_time current, int sec)
+{
+  assert (sec >= 0);
+
+  /* Compute second-based deadline.  Perform the addition in
+     uintmax_t, which is unsigned, to simply overflow detection.  */
+  uintmax_t deadline_sec = current.current.tv_sec;
+  deadline_sec += sec;
+  if (deadline_sec < (uintmax_t) sec)
+    return infinite_deadline ();
+
+  return (struct deadline) { { deadline_sec, 0 } };
+}
+
 int
 __deadline_to_ms (struct deadline_current_time current,
                   struct deadline deadline)
diff --git a/inet/net-internal.h b/inet/net-internal.h
index cdccdd3976..638a3dbd53 100644
--- a/inet/net-internal.h
+++ b/inet/net-internal.h
@@ -139,6 +139,11 @@ DIAG_POP_NEEDS_COMMENT;
 struct deadline __deadline_from_timeval (struct deadline_current_time,
                                          struct timeval tv) attribute_hidden;
 
+/* Add time in second to the current time and return it.  Returns a special
+   infinite absolute deadline on overflow.  */
+struct deadline __deadline_from_sec (struct deadline_current_time,
+                                     int sec) attribute_hidden;
+
 /* Compute the number of milliseconds until the specified deadline,
    from the current time in the argument.  The result is mainly for
    use with poll.  If the deadline has already passed, return 0.  If
diff --git a/resolv/res_send.c b/resolv/res_send.c
index 6a08e729a4..fee32451aa 100644
--- a/resolv/res_send.c
+++ b/resolv/res_send.c
@@ -110,6 +110,7 @@
 #include <kernel-features.h>
 #include <libc-diag.h>
 #include <random-bits.h>
+#include <inet/net-internal.h>
 
 #if PACKETSZ > 65536
 #define MAXPACKET       PACKETSZ
@@ -190,6 +191,30 @@ static int		send_dg(res_state, const u_char *, int,
 				int *, int *, u_char **,
 				u_char **, int *, int *, int *);
 static int		sock_eq(struct sockaddr_in6 *, struct sockaddr_in6 *);
+static int		wait_on_socket(struct pollfd *, struct deadline);
+
+static int
+wait_on_socket (struct pollfd *pfd, struct deadline wait_deadline)
+{
+  do
+    {
+      int timeout = __deadline_to_ms (__deadline_current_time(),
+				      wait_deadline);
+      switch (__poll (pfd, 1, timeout))
+	{
+	case -1:
+	  if (errno == EINTR)
+	    continue;
+	  /* FALLTHROUGH */
+	case 0:
+	  return -1;
+	default:
+	  break;
+	}
+  } while ((pfd->revents & POLLIN) == 0);
+
+  return 1;
+}
 
 /* Returns a shift value for the name server index.  Used to implement
    RES_ROTATE.  */
@@ -570,6 +595,8 @@ send_vc(res_state statp,
 	UHEADER *anhp = (UHEADER *) *ansp;
 	struct sockaddr *nsap = __res_get_nsaddr (statp, ns);
 	int truncating, connreset, n;
+	struct pollfd pfd[1] = { [0] = { .events = POLLIN } };
+	struct deadline read_deadline;
 	/* On some architectures compiler might emit a warning indicating
 	   'resplen' may be used uninitialized.  However if buf2 == NULL
 	   then this code won't be executed; if buf2 != NULL, then first
@@ -589,6 +616,16 @@ send_vc(res_state statp,
 	u_char *cp;
 
 	connreset = 0;
+
+	/* Compute and set timeout for each socket read. The timeout is
+	    currently twice the lengh of send_dg routine. */
+	int seconds = (statp->retrans << (ns+1));
+	if (ns > 0)
+		seconds /= statp->nscount;
+	if (seconds <= 0)
+		seconds = 1;
+	read_deadline =
+	    __deadline_from_sec (__deadline_current_time(), seconds);
  same_ns:
 	truncating = 0;
 
@@ -657,10 +694,13 @@ send_vc(res_state statp,
 	int recvresp2 = buf2 == NULL;
 	uint16_t rlen16;
  read_len:
+	pfd[0].fd = statp->_vcsock;
 	cp = (u_char *)&rlen16;
 	len = sizeof(rlen16);
-	while ((n = TEMP_FAILURE_RETRY (read(statp->_vcsock, cp,
-					     (int)len))) > 0) {
+
+	while ((n = wait_on_socket (pfd, read_deadline)) > 0
+	  && (n = TEMP_FAILURE_RETRY (read(statp->_vcsock, cp,
+					       (int)len))) > 0) {
 		cp += n;
 		if ((len -= n) <= 0)
 			break;
@@ -744,10 +784,14 @@ send_vc(res_state statp,
 	}
 
 	cp = *thisansp;
-	while (len != 0 && (n = read(statp->_vcsock, (char *)cp, (int)len)) > 0){
+
+	while (len != 0
+	    && (n = wait_on_socket (pfd, read_deadline)) > 0
+	    && (n = read(statp->_vcsock, (char *)cp, (int)len)) > 0) {
 		cp += n;
 		len -= n;
 	}
+
 	if (__glibc_unlikely (n <= 0))       {
 		*terrno = errno;
 		return close_and_return_error (statp, resplen2);
@@ -758,9 +802,10 @@ send_vc(res_state statp,
 		 */
 		anhp->tc = 1;
 		len = rlen - *thisanssizp;
-		while (len != 0) {
-			char junk[PACKETSZ];
 
+		while (len != 0
+		    && (n = wait_on_socket (pfd, read_deadline)) > 0) {
+			char junk[PACKETSZ];
 			n = read(statp->_vcsock, junk,
 				 (len > sizeof junk) ? sizeof junk : len);
 			if (n > 0)
-- 
2.25.1



                 reply	other threads:[~2022-07-29  8:54 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220729085159.68458-1-sinan.lin@linux.alibaba.com \
    --to=sinan.lin@linux.alibaba.com \
    --cc=libc-alpha@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).