summary refs log tree commit diff
path: root/gnu/packages/patches/qemu-CVE-2020-8608.patch
blob: 4cb017c795c63eeb790536acb9ac1995d1f286b3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
Fix CVE-2020-8608:

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-8608
https://www.openwall.com/lists/oss-security/2020/02/06/2

Patches copied from upstream dependency repository:

https://gitlab.freedesktop.org/slirp/libslirp/commit/68ccb8021a838066f0951d4b2817eb6b6f10a843
https://gitlab.freedesktop.org/slirp/libslirp/commit/30648c03b27fb8d9611b723184216cd3174b6775

From 68ccb8021a838066f0951d4b2817eb6b6f10a843 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com>
Date: Mon, 27 Jan 2020 10:24:14 +0100
Subject: [PATCH] tcp_emu: fix unsafe snprintf() usages
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Various calls to snprintf() assume that snprintf() returns "only" the
number of bytes written (excluding terminating NUL).

https://pubs.opengroup.org/onlinepubs/9699919799/functions/snprintf.html#tag_16_159_04

"Upon successful completion, the snprintf() function shall return the
number of bytes that would be written to s had n been sufficiently
large excluding the terminating null byte."

Before patch ce131029, if there isn't enough room in "m_data" for the
"DCC ..." message, we overflow "m_data".

After the patch, if there isn't enough room for the same, we don't
overflow "m_data", but we set "m_len" out-of-bounds. The next time an
access is bounded by "m_len", we'll have a buffer overflow then.

Use slirp_fmt*() to fix potential OOB memory access.

Reported-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
Message-Id: <20200127092414.169796-7-marcandre.lureau@redhat.com>
---
 src/tcp_subr.c | 44 +++++++++++++++++++++-----------------------
 1 file changed, 21 insertions(+), 23 deletions(-)

diff --git a/src/tcp_subr.c b/src/tcp_subr.c
index a699117..a72c86b 100644
--- a/slirp/src/tcp_subr.c
+++ b/slirp/src/tcp_subr.c
@@ -643,8 +643,7 @@ int tcp_emu(struct socket *so, struct mbuf *m)
                 NTOHS(n1);
                 NTOHS(n2);
                 m_inc(m, snprintf(NULL, 0, "%d,%d\r\n", n1, n2) + 1);
-                m->m_len = snprintf(m->m_data, M_ROOM(m), "%d,%d\r\n", n1, n2);
-                assert(m->m_len < M_ROOM(m));
+                m->m_len = slirp_fmt(m->m_data, M_ROOM(m), "%d,%d\r\n", n1, n2);
             } else {
                 *eol = '\r';
             }
@@ -684,9 +683,9 @@ int tcp_emu(struct socket *so, struct mbuf *m)
             n4 = (laddr & 0xff);
 
             m->m_len = bptr - m->m_data; /* Adjust length */
-            m->m_len += snprintf(bptr, M_FREEROOM(m),
-                                 "ORT %d,%d,%d,%d,%d,%d\r\n%s", n1, n2, n3, n4,
-                                 n5, n6, x == 7 ? buff : "");
+            m->m_len += slirp_fmt(bptr, M_FREEROOM(m),
+                                  "ORT %d,%d,%d,%d,%d,%d\r\n%s",
+                                  n1, n2, n3, n4, n5, n6, x == 7 ? buff : "");
             return 1;
         } else if ((bptr = (char *)strstr(m->m_data, "27 Entering")) != NULL) {
             /*
@@ -719,10 +718,9 @@ int tcp_emu(struct socket *so, struct mbuf *m)
             n4 = (laddr & 0xff);
 
             m->m_len = bptr - m->m_data; /* Adjust length */
-            m->m_len += snprintf(bptr, M_FREEROOM(m),
-                         "27 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n%s",
-                         n1, n2, n3, n4, n5, n6, x == 7 ? buff : "");
-
+            m->m_len += slirp_fmt(bptr, M_FREEROOM(m),
+                                  "27 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n%s",
+                                  n1, n2, n3, n4, n5, n6, x == 7 ? buff : "");
             return 1;
         }
 
@@ -745,8 +743,8 @@ int tcp_emu(struct socket *so, struct mbuf *m)
         if (m->m_data[m->m_len - 1] == '\0' && lport != 0 &&
             (so = tcp_listen(slirp, INADDR_ANY, 0, so->so_laddr.s_addr,
                              htons(lport), SS_FACCEPTONCE)) != NULL)
-            m->m_len = snprintf(m->m_data, M_ROOM(m),
-                                "%d", ntohs(so->so_fport)) + 1;
+            m->m_len = slirp_fmt0(m->m_data, M_ROOM(m),
+                                  "%d", ntohs(so->so_fport));
         return 1;
 
     case EMU_IRC:
@@ -765,10 +763,10 @@ int tcp_emu(struct socket *so, struct mbuf *m)
                 return 1;
             }
             m->m_len = bptr - m->m_data; /* Adjust length */
-            m->m_len += snprintf(bptr, M_FREEROOM(m),
-                                 "DCC CHAT chat %lu %u%c\n",
-                                 (unsigned long)ntohl(so->so_faddr.s_addr),
-                                 ntohs(so->so_fport), 1);
+            m->m_len += slirp_fmt(bptr, M_FREEROOM(m),
+                                  "DCC CHAT chat %lu %u%c\n",
+                                  (unsigned long)ntohl(so->so_faddr.s_addr),
+                                  ntohs(so->so_fport), 1);
         } else if (sscanf(bptr, "DCC SEND %256s %u %u %u", buff, &laddr, &lport,
                           &n1) == 4) {
             if ((so = tcp_listen(slirp, INADDR_ANY, 0, htonl(laddr),
@@ -776,10 +774,10 @@ int tcp_emu(struct socket *so, struct mbuf *m)
                 return 1;
             }
             m->m_len = bptr - m->m_data; /* Adjust length */
-            m->m_len += snprintf(bptr, M_FREEROOM(m),
-                         "DCC SEND %s %lu %u %u%c\n", buff,
-                         (unsigned long)ntohl(so->so_faddr.s_addr),
-                         ntohs(so->so_fport), n1, 1);
+            m->m_len += slirp_fmt(bptr, M_FREEROOM(m),
+                                  "DCC SEND %s %lu %u %u%c\n", buff,
+                                  (unsigned long)ntohl(so->so_faddr.s_addr),
+                                  ntohs(so->so_fport), n1, 1);
         } else if (sscanf(bptr, "DCC MOVE %256s %u %u %u", buff, &laddr, &lport,
                           &n1) == 4) {
             if ((so = tcp_listen(slirp, INADDR_ANY, 0, htonl(laddr),
@@ -787,10 +785,10 @@ int tcp_emu(struct socket *so, struct mbuf *m)
                 return 1;
             }
             m->m_len = bptr - m->m_data; /* Adjust length */
-            m->m_len += snprintf(bptr, M_FREEROOM(m),
-                         "DCC MOVE %s %lu %u %u%c\n", buff,
-                         (unsigned long)ntohl(so->so_faddr.s_addr),
-                         ntohs(so->so_fport), n1, 1);
+            m->m_len += slirp_fmt(bptr, M_FREEROOM(m),
+                                  "DCC MOVE %s %lu %u %u%c\n", buff,
+                                  (unsigned long)ntohl(so->so_faddr.s_addr),
+                                  ntohs(so->so_fport), n1, 1);
         }
         return 1;
 
-- 
2.25.1

From 30648c03b27fb8d9611b723184216cd3174b6775 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com>
Date: Mon, 27 Jan 2020 10:24:09 +0100
Subject: [PATCH] util: add slirp_fmt() helpers
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Various calls to snprintf() in libslirp assume that snprintf() returns
"only" the number of bytes written (excluding terminating NUL).

https://pubs.opengroup.org/onlinepubs/9699919799/functions/snprintf.html#tag_16_159_04

"Upon successful completion, the snprintf() function shall return the
number of bytes that would be written to s had n been sufficiently
large excluding the terminating null byte."

Introduce slirp_fmt() that handles several pathological cases the
way libslirp usually expect:

- treat error as fatal (instead of silently returning -1)

- fmt0() will always \0 end

- return the number of bytes actually written (instead of what would
have been written, which would usually result in OOB later), including
the ending \0 for fmt0()

- warn if truncation happened (instead of ignoring)

Other less common cases can still be handled with strcpy/snprintf() etc.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
Message-Id: <20200127092414.169796-2-marcandre.lureau@redhat.com>
---
 src/util.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/util.h |  3 +++
 2 files changed, 65 insertions(+)

diff --git a/src/util.c b/src/util.c
index e596087..e3b6257 100644
--- a/slirp/src/util.c
+++ b/slirp/src/util.c
@@ -364,3 +364,65 @@ void slirp_pstrcpy(char *buf, int buf_size, const char *str)
     }
     *q = '\0';
 }
+
+static int slirp_vsnprintf(char *str, size_t size,
+                           const char *format, va_list args)
+{
+    int rv = vsnprintf(str, size, format, args);
+
+    if (rv < 0) {
+        g_error("vsnprintf() failed: %s", g_strerror(errno));
+    }
+
+    return rv;
+}
+
+/*
+ * A snprintf()-like function that:
+ * - returns the number of bytes written (excluding optional \0-ending)
+ * - dies on error
+ * - warn on truncation
+ */
+int slirp_fmt(char *str, size_t size, const char *format, ...)
+{
+    va_list args;
+    int rv;
+
+    va_start(args, format);
+    rv = slirp_vsnprintf(str, size, format, args);
+    va_end(args);
+
+    if (rv > size) {
+        g_critical("vsnprintf() truncation");
+    }
+
+    return MIN(rv, size);
+}
+
+/*
+ * A snprintf()-like function that:
+ * - always \0-end (unless size == 0)
+ * - returns the number of bytes actually written, including \0 ending
+ * - dies on error
+ * - warn on truncation
+ */
+int slirp_fmt0(char *str, size_t size, const char *format, ...)
+{
+    va_list args;
+    int rv;
+
+    va_start(args, format);
+    rv = slirp_vsnprintf(str, size, format, args);
+    va_end(args);
+
+    if (rv >= size) {
+        g_critical("vsnprintf() truncation");
+        if (size > 0)
+            str[size - 1] = '\0';
+        rv = size;
+    } else {
+        rv += 1; /* include \0 */
+    }
+
+    return rv;
+}
diff --git a/src/util.h b/src/util.h
index e9c3073..5530c46 100644
--- a/slirp/src/util.h
+++ b/slirp/src/util.h
@@ -181,4 +181,7 @@ static inline int slirp_socket_set_fast_reuse(int fd)
 
 void slirp_pstrcpy(char *buf, int buf_size, const char *str);
 
+int slirp_fmt(char *str, size_t size, const char *format, ...);
+int slirp_fmt0(char *str, size_t size, const char *format, ...);
+
 #endif
-- 
2.25.1