1 |
From a3a1f4163c4d0f9a36056c8640661a88674ae8a2 Mon Sep 17 00:00:00 2001 |
2 |
From: Jeff Law <law@redhat.com> |
3 |
Date: Mon, 15 Dec 2014 10:09:07 +0100 |
4 |
Subject: [PATCH] CVE-2012-3406: Stack overflow in vfprintf [BZ #16617] |
5 |
|
6 |
A larger number of format specifiers coudld cause a stack overflow, |
7 |
potentially allowing to bypass _FORTIFY_SOURCE format string |
8 |
protection. |
9 |
|
10 |
(cherry picked from commit a5357b7ce2a2982c5778435704bcdb55ce3667a0) |
11 |
(cherry picked from commit ae61fc7b33d9d99d2763c16de8275227dc9748ba) |
12 |
|
13 |
Conflicts: |
14 |
NEWS |
15 |
--- |
16 |
ChangeLog | 9 +++++++ |
17 |
NEWS | 4 ++- |
18 |
stdio-common/Makefile | 2 +- |
19 |
stdio-common/bug23-2.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++ |
20 |
stdio-common/bug23-3.c | 50 +++++++++++++++++++++++++++++++++++ |
21 |
stdio-common/bug23-4.c | 31 ++++++++++++++++++++++ |
22 |
stdio-common/vfprintf.c | 40 ++++++++++++++++++++++++++-- |
23 |
7 files changed, 202 insertions(+), 4 deletions(-) |
24 |
create mode 100644 stdio-common/bug23-2.c |
25 |
create mode 100644 stdio-common/bug23-3.c |
26 |
create mode 100644 stdio-common/bug23-4.c |
27 |
|
28 |
#diff --git a/ChangeLog b/ChangeLog |
29 |
#index ac7d980..88d2f1e 100644 |
30 |
#--- a/ChangeLog |
31 |
#+++ b/ChangeLog |
32 |
#@@ -1,3 +1,12 @@ |
33 |
#+2014-12-15 Jeff Law <law@redhat.com> |
34 |
#+ |
35 |
#+ [BZ #16617] |
36 |
#+ * stdio-common/vfprintf.c (vfprintf): Allocate large specs array |
37 |
#+ on the heap. (CVE-2012-3406) |
38 |
#+ * stdio-common/bug23-2.c, stdio-common/bug23-3.c: New file. |
39 |
#+ * stdio-common/bug23-4.c: New file. Test case by Joseph Myers. |
40 |
#+ * stdio-common/Makefile (tests): Add bug23-2, bug23-3, bug23-4. |
41 |
#+ |
42 |
# 2014-11-24 Siddhesh Poyarekar <siddhesh@redhat.com> |
43 |
# |
44 |
# [BZ #17266] |
45 |
#diff --git a/NEWS b/NEWS |
46 |
#index 3de92cd..f6cdb66 100644 |
47 |
#--- a/NEWS |
48 |
#+++ b/NEWS |
49 |
#@@ -9,7 +9,7 @@ Version 2.20.1 |
50 |
# |
51 |
# * The following bugs are resolved with this release: |
52 |
# |
53 |
#- 17266, 17370, 17371, 17460, 17485, 17555, 17625. |
54 |
#+ 16617, 17266, 17370, 17371, 17460, 17485, 17555, 17625. |
55 |
# |
56 |
# * CVE-2104-7817 The wordexp function could ignore the WRDE_NOCMD flag |
57 |
# under certain input conditions resulting in the execution of a shell for |
58 |
#@@ -17,6 +17,8 @@ Version 2.20.1 |
59 |
# implementation now checks WRDE_NOCMD immediately before executing the |
60 |
# shell and returns the error WRDE_CMDSUB as expected. |
61 |
# |
62 |
#+* CVE-2012-3406 printf-style functions could run into a stack overflow when |
63 |
#+ processing format strings with a large number of format specifiers.a |
64 |
# |
65 |
# Version 2.20 |
66 |
# |
67 |
diff --git a/stdio-common/Makefile b/stdio-common/Makefile |
68 |
index 5f8e534..e5e45b6 100644 |
69 |
--- a/stdio-common/Makefile |
70 |
+++ b/stdio-common/Makefile |
71 |
@@ -57,7 +57,7 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \ |
72 |
bug19 bug19a tst-popen2 scanf13 scanf14 scanf15 bug20 bug21 bug22 \ |
73 |
scanf16 scanf17 tst-setvbuf1 tst-grouping bug23 bug24 \ |
74 |
bug-vfprintf-nargs tst-long-dbl-fphex tst-fphex-wide tst-sprintf3 \ |
75 |
- bug25 tst-printf-round bug26 |
76 |
+ bug25 tst-printf-round bug23-2 bug23-3 bug23-4 bug26 |
77 |
|
78 |
test-srcs = tst-unbputc tst-printf |
79 |
|
80 |
diff --git a/stdio-common/bug23-2.c b/stdio-common/bug23-2.c |
81 |
new file mode 100644 |
82 |
index 0000000..9e0cfe6 |
83 |
--- /dev/null |
84 |
+++ b/stdio-common/bug23-2.c |
85 |
@@ -0,0 +1,70 @@ |
86 |
+#include <stdio.h> |
87 |
+#include <string.h> |
88 |
+#include <stdlib.h> |
89 |
+ |
90 |
+static const char expected[] = "\ |
91 |
+\n\ |
92 |
+a\n\ |
93 |
+abbcd55\ |
94 |
+\n\ |
95 |
+a\n\ |
96 |
+abbcd55\ |
97 |
+\n\ |
98 |
+a\n\ |
99 |
+abbcd55\ |
100 |
+\n\ |
101 |
+a\n\ |
102 |
+abbcd55\ |
103 |
+\n\ |
104 |
+a\n\ |
105 |
+abbcd55\ |
106 |
+\n\ |
107 |
+a\n\ |
108 |
+abbcd55\ |
109 |
+\n\ |
110 |
+a\n\ |
111 |
+abbcd55\ |
112 |
+\n\ |
113 |
+a\n\ |
114 |
+abbcd55\ |
115 |
+\n\ |
116 |
+a\n\ |
117 |
+abbcd55\ |
118 |
+\n\ |
119 |
+a\n\ |
120 |
+abbcd55\ |
121 |
+\n\ |
122 |
+a\n\ |
123 |
+abbcd55\ |
124 |
+\n\ |
125 |
+a\n\ |
126 |
+abbcd55\ |
127 |
+\n\ |
128 |
+a\n\ |
129 |
+abbcd55%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; |
130 |
+ |
131 |
+static int |
132 |
+do_test (void) |
133 |
+{ |
134 |
+ char *buf = malloc (strlen (expected) + 1); |
135 |
+ snprintf (buf, strlen (expected) + 1, |
136 |
+ "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" |
137 |
+ "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" |
138 |
+ "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" |
139 |
+ "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" |
140 |
+ "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" |
141 |
+ "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" |
142 |
+ "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" |
143 |
+ "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" |
144 |
+ "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" |
145 |
+ "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" |
146 |
+ "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" |
147 |
+ "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" |
148 |
+ "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" |
149 |
+ "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", |
150 |
+ "a", "b", "c", "d", 5); |
151 |
+ return strcmp (buf, expected) != 0; |
152 |
+} |
153 |
+ |
154 |
+#define TEST_FUNCTION do_test () |
155 |
+#include "../test-skeleton.c" |
156 |
diff --git a/stdio-common/bug23-3.c b/stdio-common/bug23-3.c |
157 |
new file mode 100644 |
158 |
index 0000000..57c8cef |
159 |
--- /dev/null |
160 |
+++ b/stdio-common/bug23-3.c |
161 |
@@ -0,0 +1,50 @@ |
162 |
+#include <stdio.h> |
163 |
+#include <string.h> |
164 |
+#include <stdlib.h> |
165 |
+ |
166 |
+int |
167 |
+do_test (void) |
168 |
+{ |
169 |
+ size_t instances = 16384; |
170 |
+#define X0 "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" |
171 |
+ const char *item = "\na\nabbcd55"; |
172 |
+#define X3 X0 X0 X0 X0 X0 X0 X0 X0 |
173 |
+#define X6 X3 X3 X3 X3 X3 X3 X3 X3 |
174 |
+#define X9 X6 X6 X6 X6 X6 X6 X6 X6 |
175 |
+#define X12 X9 X9 X9 X9 X9 X9 X9 X9 |
176 |
+#define X14 X12 X12 X12 X12 |
177 |
+#define TRAILER "%%%%%%%%%%%%%%%%%%%%%%%%%%" |
178 |
+#define TRAILER2 TRAILER TRAILER |
179 |
+ size_t length = instances * strlen (item) + strlen (TRAILER) + 1; |
180 |
+ |
181 |
+ char *buf = malloc (length + 1); |
182 |
+ snprintf (buf, length + 1, |
183 |
+ X14 TRAILER2 "\n", |
184 |
+ "a", "b", "c", "d", 5); |
185 |
+ |
186 |
+ const char *p = buf; |
187 |
+ size_t i; |
188 |
+ for (i = 0; i < instances; ++i) |
189 |
+ { |
190 |
+ const char *expected; |
191 |
+ for (expected = item; *expected; ++expected) |
192 |
+ { |
193 |
+ if (*p != *expected) |
194 |
+ { |
195 |
+ printf ("mismatch at offset %zu (%zu): expected %d, got %d\n", |
196 |
+ (size_t) (p - buf), i, *expected & 0xFF, *p & 0xFF); |
197 |
+ return 1; |
198 |
+ } |
199 |
+ ++p; |
200 |
+ } |
201 |
+ } |
202 |
+ if (strcmp (p, TRAILER "\n") != 0) |
203 |
+ { |
204 |
+ printf ("mismatch at trailer: [%s]\n", p); |
205 |
+ return 1; |
206 |
+ } |
207 |
+ free (buf); |
208 |
+ return 0; |
209 |
+} |
210 |
+#define TEST_FUNCTION do_test () |
211 |
+#include "../test-skeleton.c" |
212 |
diff --git a/stdio-common/bug23-4.c b/stdio-common/bug23-4.c |
213 |
new file mode 100644 |
214 |
index 0000000..a478564 |
215 |
--- /dev/null |
216 |
+++ b/stdio-common/bug23-4.c |
217 |
@@ -0,0 +1,31 @@ |
218 |
+#include <stdio.h> |
219 |
+#include <stdlib.h> |
220 |
+#include <string.h> |
221 |
+#include <sys/resource.h> |
222 |
+ |
223 |
+#define LIMIT 1000000 |
224 |
+ |
225 |
+int |
226 |
+main (void) |
227 |
+{ |
228 |
+ struct rlimit lim; |
229 |
+ getrlimit (RLIMIT_STACK, &lim); |
230 |
+ lim.rlim_cur = 1048576; |
231 |
+ setrlimit (RLIMIT_STACK, &lim); |
232 |
+ char *fmtstr = malloc (4 * LIMIT + 1); |
233 |
+ if (fmtstr == NULL) |
234 |
+ abort (); |
235 |
+ char *output = malloc (LIMIT + 1); |
236 |
+ if (output == NULL) |
237 |
+ abort (); |
238 |
+ for (size_t i = 0; i < LIMIT; i++) |
239 |
+ memcpy (fmtstr + 4 * i, "%1$d", 4); |
240 |
+ fmtstr[4 * LIMIT] = '\0'; |
241 |
+ int ret = snprintf (output, LIMIT + 1, fmtstr, 0); |
242 |
+ if (ret != LIMIT) |
243 |
+ abort (); |
244 |
+ for (size_t i = 0; i < LIMIT; i++) |
245 |
+ if (output[i] != '0') |
246 |
+ abort (); |
247 |
+ return 0; |
248 |
+} |
249 |
diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c |
250 |
index c4ff833..429a3d1 100644 |
251 |
--- a/stdio-common/vfprintf.c |
252 |
+++ b/stdio-common/vfprintf.c |
253 |
@@ -263,6 +263,12 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) |
254 |
/* For the argument descriptions, which may be allocated on the heap. */ |
255 |
void *args_malloced = NULL; |
256 |
|
257 |
+ /* For positional argument handling. */ |
258 |
+ struct printf_spec *specs; |
259 |
+ |
260 |
+ /* Track if we malloced the SPECS array and thus must free it. */ |
261 |
+ bool specs_malloced = false; |
262 |
+ |
263 |
/* This table maps a character into a number representing a |
264 |
class. In each step there is a destination label for each |
265 |
class. */ |
266 |
@@ -1679,8 +1685,8 @@ do_positional: |
267 |
size_t nspecs = 0; |
268 |
/* A more or less arbitrary start value. */ |
269 |
size_t nspecs_size = 32 * sizeof (struct printf_spec); |
270 |
- struct printf_spec *specs = alloca (nspecs_size); |
271 |
|
272 |
+ specs = alloca (nspecs_size); |
273 |
/* The number of arguments the format string requests. This will |
274 |
determine the size of the array needed to store the argument |
275 |
attributes. */ |
276 |
@@ -1721,11 +1727,39 @@ do_positional: |
277 |
if (nspecs * sizeof (*specs) >= nspecs_size) |
278 |
{ |
279 |
/* Extend the array of format specifiers. */ |
280 |
+ if (nspecs_size * 2 < nspecs_size) |
281 |
+ { |
282 |
+ __set_errno (ENOMEM); |
283 |
+ done = -1; |
284 |
+ goto all_done; |
285 |
+ } |
286 |
struct printf_spec *old = specs; |
287 |
- specs = extend_alloca (specs, nspecs_size, 2 * nspecs_size); |
288 |
+ if (__libc_use_alloca (2 * nspecs_size)) |
289 |
+ specs = extend_alloca (specs, nspecs_size, 2 * nspecs_size); |
290 |
+ else |
291 |
+ { |
292 |
+ nspecs_size *= 2; |
293 |
+ specs = malloc (nspecs_size); |
294 |
+ if (specs == NULL) |
295 |
+ { |
296 |
+ __set_errno (ENOMEM); |
297 |
+ specs = old; |
298 |
+ done = -1; |
299 |
+ goto all_done; |
300 |
+ } |
301 |
+ } |
302 |
|
303 |
/* Copy the old array's elements to the new space. */ |
304 |
memmove (specs, old, nspecs * sizeof (*specs)); |
305 |
+ |
306 |
+ /* If we had previously malloc'd space for SPECS, then |
307 |
+ release it after the copy is complete. */ |
308 |
+ if (specs_malloced) |
309 |
+ free (old); |
310 |
+ |
311 |
+ /* Now set SPECS_MALLOCED if needed. */ |
312 |
+ if (!__libc_use_alloca (nspecs_size)) |
313 |
+ specs_malloced = true; |
314 |
} |
315 |
|
316 |
/* Parse the format specifier. */ |
317 |
@@ -2046,6 +2080,8 @@ do_positional: |
318 |
} |
319 |
|
320 |
all_done: |
321 |
+ if (specs_malloced) |
322 |
+ free (specs); |
323 |
if (__glibc_unlikely (args_malloced != NULL)) |
324 |
free (args_malloced); |
325 |
if (__glibc_unlikely (workstart != NULL)) |
326 |
-- |
327 |
1.8.4.5 |
328 |
|