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