1 /* Test reporting of Safe-Linking caught errors.
2 Copyright (C) 2020-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19 #include <signal.h>
20 #include <stdint.h>
21 #include <stdlib.h>
22 #include <memory.h>
23 #include <string.h>
24 #include <time.h>
25 #include <stdbool.h>
26 #include <support/capture_subprocess.h>
27 #include <support/check.h>
28
29 /* Run CALLBACK and check that the data on standard error equals
30 EXPECTED. */
31 static void
check(const char * test,void (* callback)(void *),const char * expected)32 check (const char *test, void (*callback) (void *),
33 const char *expected)
34 {
35 int i, rand_mask;
36 int success = 0; /* 0 == fail, 1 == other check 2 == safe linking */
37 /* There is a chance of 1/16 that a corrupted pointer will be aligned.
38 Try multiple times so that statistical failure will be improbable. */
39 for (i = 0; i < 16; ++i)
40 {
41 rand_mask = rand () & 0xFF;
42 struct support_capture_subprocess result
43 = support_capture_subprocess (callback, &rand_mask);
44 printf ("%s\n", result.out.buffer);
45 /* Did not crash, could happen. Try again. */
46 if (strlen (result.err.buffer) == 0)
47 continue;
48 /* Crashed, it may either be safe linking or some other check. If it's
49 not safe linking then try again. */
50 if (strcmp (result.err.buffer, expected) != 0)
51 {
52 printf ("test %s failed with a different error\n"
53 " expected: %s\n"
54 " actual: %s\n",
55 test, expected, result.err.buffer);
56 success = 1;
57 continue;
58 }
59 TEST_VERIFY (WIFSIGNALED (result.status));
60 if (WIFSIGNALED (result.status))
61 TEST_VERIFY (WTERMSIG (result.status) == SIGABRT);
62 support_capture_subprocess_free (&result);
63 success = 2;
64 break;
65 }
66 /* The test fails only if the corruption was not caught by any of the malloc
67 mechanisms in all those iterations. This has a lower than 1 in 2^64
68 chance of a false positive. */
69 TEST_VERIFY (success);
70 }
71
72 /* Implementation details must be kept in sync with malloc. */
73 #define TCACHE_FILL_COUNT 7
74 #define TCACHE_ALLOC_SIZE 0x20
75 #define MALLOC_CONSOLIDATE_SIZE 256*1024
76
77 /* Try corrupting the tcache list. */
78 static void
test_tcache(void * closure)79 test_tcache (void *closure)
80 {
81 int mask = ((int *)closure)[0];
82 size_t size = TCACHE_ALLOC_SIZE;
83
84 printf ("++ tcache ++\n");
85
86 /* Populate the tcache list. */
87 void * volatile a = malloc (size);
88 void * volatile b = malloc (size);
89 void * volatile c = malloc (size);
90 printf ("a=%p, b=%p, c=%p\n", a, b, c);
91 free (a);
92 free (b);
93 free (c);
94
95 /* Corrupt the pointer with a random value, and avoid optimizations. */
96 printf ("Before: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
97 memset (c, mask & 0xFF, size);
98 printf ("After: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
99
100 c = malloc (size);
101 printf ("Allocated: c=%p\n", c);
102 /* This line will trigger the Safe-Linking check. */
103 b = malloc (size);
104 printf ("b=%p\n", b);
105 }
106
107 /* Try corrupting the fastbin list. */
108 static void
test_fastbin(void * closure)109 test_fastbin (void *closure)
110 {
111 int i;
112 int mask = ((int *)closure)[0];
113 size_t size = TCACHE_ALLOC_SIZE;
114
115 printf ("++ fastbin ++\n");
116
117 /* Take the tcache out of the game. */
118 for (i = 0; i < TCACHE_FILL_COUNT; ++i)
119 {
120 void * volatile p = calloc (1, size);
121 printf ("p=%p\n", p);
122 free (p);
123 }
124
125 /* Populate the fastbin list. */
126 void * volatile a = calloc (1, size);
127 void * volatile b = calloc (1, size);
128 void * volatile c = calloc (1, size);
129 printf ("a=%p, b=%p, c=%p\n", a, b, c);
130 free (a);
131 free (b);
132 free (c);
133
134 /* Corrupt the pointer with a random value, and avoid optimizations. */
135 printf ("Before: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
136 memset (c, mask & 0xFF, size);
137 printf ("After: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
138
139 c = calloc (1, size);
140 printf ("Allocated: c=%p\n", c);
141 /* This line will trigger the Safe-Linking check. */
142 b = calloc (1, size);
143 printf ("b=%p\n", b);
144 }
145
146 /* Try corrupting the fastbin list and trigger a consolidate. */
147 static void
test_fastbin_consolidate(void * closure)148 test_fastbin_consolidate (void *closure)
149 {
150 int i;
151 int mask = ((int*)closure)[0];
152 size_t size = TCACHE_ALLOC_SIZE;
153
154 printf ("++ fastbin consolidate ++\n");
155
156 /* Take the tcache out of the game. */
157 for (i = 0; i < TCACHE_FILL_COUNT; ++i)
158 {
159 void * volatile p = calloc (1, size);
160 free (p);
161 }
162
163 /* Populate the fastbin list. */
164 void * volatile a = calloc (1, size);
165 void * volatile b = calloc (1, size);
166 void * volatile c = calloc (1, size);
167 printf ("a=%p, b=%p, c=%p\n", a, b, c);
168 free (a);
169 free (b);
170 free (c);
171
172 /* Corrupt the pointer with a random value, and avoid optimizations. */
173 printf ("Before: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
174 memset (c, mask & 0xFF, size);
175 printf ("After: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
176
177 /* This line will trigger the Safe-Linking check. */
178 b = malloc (MALLOC_CONSOLIDATE_SIZE);
179 printf ("b=%p\n", b);
180 }
181
182 static int
do_test(void)183 do_test (void)
184 {
185 /* Seed the random for the test. */
186 srand (time (NULL));
187
188 check ("test_tcache", test_tcache,
189 "malloc(): unaligned tcache chunk detected\n");
190 check ("test_fastbin", test_fastbin,
191 "malloc(): unaligned fastbin chunk detected 2\n");
192 check ("test_fastbin_consolidate", test_fastbin_consolidate,
193 "malloc_consolidate(): unaligned fastbin chunk detected\n");
194
195 return 0;
196 }
197
198 #include <support/test-driver.c>
199