8cd64c
From 68ccb8021a838066f0951d4b2817eb6b6f10a843 Mon Sep 17 00:00:00 2001
8cd64c
From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com>
8cd64c
Date: Mon, 27 Jan 2020 10:24:14 +0100
8cd64c
Subject: [PATCH] tcp_emu: fix unsafe snprintf() usages
8cd64c
MIME-Version: 1.0
8cd64c
Content-Type: text/plain; charset=UTF-8
8cd64c
Content-Transfer-Encoding: 8bit
8cd64c
8cd64c
Various calls to snprintf() assume that snprintf() returns "only" the
8cd64c
number of bytes written (excluding terminating NUL).
8cd64c
8cd64c
https://pubs.opengroup.org/onlinepubs/9699919799/functions/snprintf.html#tag_16_159_04
8cd64c
8cd64c
"Upon successful completion, the snprintf() function shall return the
8cd64c
number of bytes that would be written to s had n been sufficiently
8cd64c
large excluding the terminating null byte."
8cd64c
8cd64c
Before patch ce131029, if there isn't enough room in "m_data" for the
8cd64c
"DCC ..." message, we overflow "m_data".
8cd64c
8cd64c
After the patch, if there isn't enough room for the same, we don't
8cd64c
overflow "m_data", but we set "m_len" out-of-bounds. The next time an
8cd64c
access is bounded by "m_len", we'll have a buffer overflow then.
8cd64c
8cd64c
Use slirp_fmt*() to fix potential OOB memory access.
8cd64c
8cd64c
Reported-by: Laszlo Ersek <lersek@redhat.com>
8cd64c
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
8cd64c
Reviewed-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
8cd64c
Message-Id: <20200127092414.169796-7-marcandre.lureau@redhat.com>
8cd64c
---
8cd64c
 src/tcp_subr.c | 44 +++++++++++++++++++++-----------------------
8cd64c
 1 file changed, 21 insertions(+), 23 deletions(-)
8cd64c
8cd64c
diff -up slirp4netns-4992082b2af77c09bca6bd8504e2ebfa5e118c18/qemu/slirp/src/tcp_subr.c.orig slirp4netns-4992082b2af77c09bca6bd8504e2ebfa5e118c18/qemu/slirp/src/tcp_subr.c
8cd64c
--- slirp4netns-4992082b2af77c09bca6bd8504e2ebfa5e118c18/qemu/slirp/src/tcp_subr.c.orig	2020-02-06 14:57:07.786013242 +0100
8cd64c
+++ slirp4netns-4992082b2af77c09bca6bd8504e2ebfa5e118c18/qemu/slirp/src/tcp_subr.c	2020-02-06 15:14:44.678746260 +0100
8cd64c
@@ -709,9 +709,9 @@ tcp_emu(struct socket *so, struct mbuf *
8cd64c
 			n4 =  (laddr & 0xff);
8cd64c
 
8cd64c
 			m->m_len = bptr - m->m_data; /* Adjust length */
8cd64c
-                        m->m_len += snprintf(bptr, M_FREEROOM(m),
8cd64c
-                                             "ORT %d,%d,%d,%d,%d,%d\r\n%s",
8cd64c
-                                             n1, n2, n3, n4, n5, n6, x==7?buff:"");
8cd64c
+			m->m_len += slirp_fmt(bptr, M_FREEROOM(m),
8cd64c
+					      "ORT %d,%d,%d,%d,%d,%d\r\n%s",
8cd64c
+					      n1, n2, n3, n4, n5, n6, x == 7 ? buff : "");
8cd64c
 			return 1;
8cd64c
 		} else if ((bptr = (char *)strstr(m->m_data, "27 Entering")) != NULL) {
8cd64c
 			/*
8cd64c
@@ -742,10 +742,9 @@ tcp_emu(struct socket *so, struct mbuf *
8cd64c
 			n4 =  (laddr & 0xff);
8cd64c
 
8cd64c
 			m->m_len = bptr - m->m_data; /* Adjust length */
8cd64c
-			m->m_len += snprintf(bptr, M_FREEROOM(m),
8cd64c
-                                             "27 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n%s",
8cd64c
-                                             n1, n2, n3, n4, n5, n6, x==7?buff:"");
8cd64c
-
8cd64c
+			m->m_len += slirp_fmt(bptr, M_FREEROOM(m),
8cd64c
+					      "27 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n%s",
8cd64c
+					      n1, n2, n3, n4, n5, n6, x == 7 ? buff : "");
8cd64c
 			return 1;
8cd64c
 		}
8cd64c
 
8cd64c
@@ -768,9 +767,8 @@ tcp_emu(struct socket *so, struct mbuf *
8cd64c
 		if (m->m_data[m->m_len-1] == '\0' && lport != 0 &&
8cd64c
 		    (so = tcp_listen(slirp, INADDR_ANY, 0, so->so_laddr.s_addr,
8cd64c
 		                     htons(lport), SS_FACCEPTONCE)) != NULL)
8cd64c
-		    m->m_len = snprintf(m->m_data, M_ROOM(m),
8cd64c
-					"%d", ntohs(so->so_fport)) + 1;
8cd64c
-
8cd64c
+		    m->m_len = slirp_fmt0(m->m_data, M_ROOM(m),
8cd64c
+					  "%d", ntohs(so->so_fport));
8cd64c
 		return 1;
8cd64c
 
8cd64c
 	 case EMU_IRC:
8cd64c
@@ -789,10 +787,10 @@ tcp_emu(struct socket *so, struct mbuf *
8cd64c
 				return 1;
8cd64c
 			}
8cd64c
 			m->m_len = bptr - m->m_data; /* Adjust length */
8cd64c
-                        m->m_len += snprintf(bptr, M_FREEROOM(m),
8cd64c
-                                             "DCC CHAT chat %lu %u%c\n",
8cd64c
-                                             (unsigned long)ntohl(so->so_faddr.s_addr),
8cd64c
-                                             ntohs(so->so_fport), 1);
8cd64c
+			m->m_len += slirp_fmt(bptr, M_FREEROOM(m),
8cd64c
+					      "DCC CHAT chat %lu %u%c\n",
8cd64c
+					      (unsigned long)ntohl(so->so_faddr.s_addr),
8cd64c
+					      ntohs(so->so_fport), 1);
8cd64c
 		} else if (sscanf(bptr, "DCC SEND %256s %u %u %u", buff, &laddr, &lport, &n1) == 4) {
8cd64c
 			if ((so = tcp_listen(slirp, INADDR_ANY, 0,
8cd64c
 			                     htonl(laddr), htons(lport),
8cd64c
@@ -800,10 +798,10 @@ tcp_emu(struct socket *so, struct mbuf *
8cd64c
 				return 1;
8cd64c
 			}
8cd64c
 			m->m_len = bptr - m->m_data; /* Adjust length */
8cd64c
-                        m->m_len += snprintf(bptr, M_FREEROOM(m),
8cd64c
-                                             "DCC SEND %s %lu %u %u%c\n", buff,
8cd64c
-                                             (unsigned long)ntohl(so->so_faddr.s_addr),
8cd64c
-                                             ntohs(so->so_fport), n1, 1);
8cd64c
+			m->m_len += slirp_fmt(bptr, M_FREEROOM(m),
8cd64c
+					      "DCC SEND %s %lu %u %u%c\n", buff,
8cd64c
+					      (unsigned long)ntohl(so->so_faddr.s_addr),
8cd64c
+					      ntohs(so->so_fport), n1, 1);
8cd64c
 		} else if (sscanf(bptr, "DCC MOVE %256s %u %u %u", buff, &laddr, &lport, &n1) == 4) {
8cd64c
 			if ((so = tcp_listen(slirp, INADDR_ANY, 0,
8cd64c
 			                     htonl(laddr), htons(lport),
8cd64c
@@ -811,10 +809,10 @@ tcp_emu(struct socket *so, struct mbuf *
8cd64c
 				return 1;
8cd64c
 			}
8cd64c
 			m->m_len = bptr - m->m_data; /* Adjust length */
8cd64c
-                        m->m_len += snprintf(bptr, M_FREEROOM(m),
8cd64c
-                                             "DCC MOVE %s %lu %u %u%c\n", buff,
8cd64c
-                                             (unsigned long)ntohl(so->so_faddr.s_addr),
8cd64c
-                                             ntohs(so->so_fport), n1, 1);
8cd64c
+			m->m_len += slirp_fmt(bptr, M_FREEROOM(m),
8cd64c
+					      "DCC MOVE %s %lu %u %u%c\n", buff,
8cd64c
+					      (unsigned long)ntohl(so->so_faddr.s_addr),
8cd64c
+					      ntohs(so->so_fport), n1, 1);
8cd64c
 		}
8cd64c
 		return 1;
8cd64c
 
8cd64c
From 30648c03b27fb8d9611b723184216cd3174b6775 Mon Sep 17 00:00:00 2001
8cd64c
From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com>
8cd64c
Date: Mon, 27 Jan 2020 10:24:09 +0100
8cd64c
Subject: [PATCH] util: add slirp_fmt() helpers
8cd64c
MIME-Version: 1.0
8cd64c
Content-Type: text/plain; charset=UTF-8
8cd64c
Content-Transfer-Encoding: 8bit
8cd64c
8cd64c
Various calls to snprintf() in libslirp assume that snprintf() returns
8cd64c
"only" the number of bytes written (excluding terminating NUL).
8cd64c
8cd64c
https://pubs.opengroup.org/onlinepubs/9699919799/functions/snprintf.html#tag_16_159_04
8cd64c
8cd64c
"Upon successful completion, the snprintf() function shall return the
8cd64c
number of bytes that would be written to s had n been sufficiently
8cd64c
large excluding the terminating null byte."
8cd64c
8cd64c
Introduce slirp_fmt() that handles several pathological cases the
8cd64c
way libslirp usually expect:
8cd64c
8cd64c
- treat error as fatal (instead of silently returning -1)
8cd64c
8cd64c
- fmt0() will always \0 end
8cd64c
8cd64c
- return the number of bytes actually written (instead of what would
8cd64c
have been written, which would usually result in OOB later), including
8cd64c
the ending \0 for fmt0()
8cd64c
8cd64c
- warn if truncation happened (instead of ignoring)
8cd64c
8cd64c
Other less common cases can still be handled with strcpy/snprintf() etc.
8cd64c
8cd64c
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
8cd64c
Reviewed-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
8cd64c
Message-Id: <20200127092414.169796-2-marcandre.lureau@redhat.com>
8cd64c
---
8cd64c
 src/util.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
8cd64c
 src/util.h |  3 +++
8cd64c
 2 files changed, 65 insertions(+)
8cd64c
8cd64c
diff -up slirp4netns-4992082b2af77c09bca6bd8504e2ebfa5e118c18/qemu/slirp/src/util.c.orig slirp4netns-4992082b2af77c09bca6bd8504e2ebfa5e118c18/qemu/slirp/src/util.c
8cd64c
--- slirp4netns-4992082b2af77c09bca6bd8504e2ebfa5e118c18/qemu/slirp/src/util.c.orig	2019-03-28 12:45:09.000000000 +0100
8cd64c
+++ slirp4netns-4992082b2af77c09bca6bd8504e2ebfa5e118c18/qemu/slirp/src/util.c	2020-02-06 14:57:07.786013242 +0100
8cd64c
@@ -366,3 +366,65 @@ void slirp_pstrcpy(char *buf, int buf_si
8cd64c
     }
8cd64c
     *q = '\0';
8cd64c
 }
8cd64c
+
8cd64c
+static int slirp_vsnprintf(char *str, size_t size,
8cd64c
+                           const char *format, va_list args)
8cd64c
+{
8cd64c
+    int rv = vsnprintf(str, size, format, args);
8cd64c
+
8cd64c
+    if (rv < 0) {
8cd64c
+        g_error("vsnprintf() failed: %s", g_strerror(errno));
8cd64c
+    }
8cd64c
+
8cd64c
+    return rv;
8cd64c
+}
8cd64c
+
8cd64c
+/*
8cd64c
+ * A snprintf()-like function that:
8cd64c
+ * - returns the number of bytes written (excluding optional \0-ending)
8cd64c
+ * - dies on error
8cd64c
+ * - warn on truncation
8cd64c
+ */
8cd64c
+int slirp_fmt(char *str, size_t size, const char *format, ...)
8cd64c
+{
8cd64c
+    va_list args;
8cd64c
+    int rv;
8cd64c
+
8cd64c
+    va_start(args, format);
8cd64c
+    rv = slirp_vsnprintf(str, size, format, args);
8cd64c
+    va_end(args);
8cd64c
+
8cd64c
+    if (rv > size) {
8cd64c
+        g_critical("vsnprintf() truncation");
8cd64c
+    }
8cd64c
+
8cd64c
+    return MIN(rv, size);
8cd64c
+}
8cd64c
+
8cd64c
+/*
8cd64c
+ * A snprintf()-like function that:
8cd64c
+ * - always \0-end (unless size == 0)
8cd64c
+ * - returns the number of bytes actually written, including \0 ending
8cd64c
+ * - dies on error
8cd64c
+ * - warn on truncation
8cd64c
+ */
8cd64c
+int slirp_fmt0(char *str, size_t size, const char *format, ...)
8cd64c
+{
8cd64c
+    va_list args;
8cd64c
+    int rv;
8cd64c
+
8cd64c
+    va_start(args, format);
8cd64c
+    rv = slirp_vsnprintf(str, size, format, args);
8cd64c
+    va_end(args);
8cd64c
+
8cd64c
+    if (rv >= size) {
8cd64c
+        g_critical("vsnprintf() truncation");
8cd64c
+        if (size > 0)
8cd64c
+            str[size - 1] = '\0';
8cd64c
+        rv = size;
8cd64c
+    } else {
8cd64c
+        rv += 1; /* include \0 */
8cd64c
+    }
8cd64c
+
8cd64c
+    return rv;
8cd64c
+}
8cd64c
diff -up slirp4netns-4992082b2af77c09bca6bd8504e2ebfa5e118c18/qemu/slirp/src/util.h.orig slirp4netns-4992082b2af77c09bca6bd8504e2ebfa5e118c18/qemu/slirp/src/util.h
8cd64c
--- slirp4netns-4992082b2af77c09bca6bd8504e2ebfa5e118c18/qemu/slirp/src/util.h.orig	2019-03-28 12:45:09.000000000 +0100
8cd64c
+++ slirp4netns-4992082b2af77c09bca6bd8504e2ebfa5e118c18/qemu/slirp/src/util.h	2020-02-06 14:57:07.786013242 +0100
8cd64c
@@ -172,4 +172,7 @@ static inline int slirp_socket_set_fast_
8cd64c
 
8cd64c
 void slirp_pstrcpy(char *buf, int buf_size, const char *str);
8cd64c
 
8cd64c
+int slirp_fmt(char *str, size_t size, const char *format, ...);
8cd64c
+int slirp_fmt0(char *str, size_t size, const char *format, ...);
8cd64c
+
8cd64c
 #endif