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. */
31static void
32check (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, closure: &rand_mask);
44 printf (format: "%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 (format: "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. */
78static void
79test_tcache (void *closure)
80{
81 int mask = ((int *)closure)[0];
82 size_t size = TCACHE_ALLOC_SIZE;
83
84 printf (format: "++ tcache ++\n");
85
86 /* Populate the tcache list. */
87 void * volatile a = malloc (size: size);
88 void * volatile b = malloc (size: size);
89 void * volatile c = malloc (size: size);
90 printf (format: "a=%p, b=%p, c=%p\n", a, b, c);
91 free (ptr: a);
92 free (ptr: b);
93 free (ptr: c);
94
95 /* Corrupt the pointer with a random value, and avoid optimizations. */
96 printf (format: "Before: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
97 memset (c, mask & 0xFF, size);
98 printf (format: "After: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
99
100 c = malloc (size: size);
101 printf (format: "Allocated: c=%p\n", c);
102 /* This line will trigger the Safe-Linking check. */
103 b = malloc (size: size);
104 printf (format: "b=%p\n", b);
105}
106
107/* Try corrupting the fastbin list. */
108static void
109test_fastbin (void *closure)
110{
111 int i;
112 int mask = ((int *)closure)[0];
113 size_t size = TCACHE_ALLOC_SIZE;
114
115 printf (format: "++ 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 (nmemb: 1, size: size);
121 printf (format: "p=%p\n", p);
122 free (ptr: p);
123 }
124
125 /* Populate the fastbin list. */
126 void * volatile a = calloc (nmemb: 1, size: size);
127 void * volatile b = calloc (nmemb: 1, size: size);
128 void * volatile c = calloc (nmemb: 1, size: size);
129 printf (format: "a=%p, b=%p, c=%p\n", a, b, c);
130 free (ptr: a);
131 free (ptr: b);
132 free (ptr: c);
133
134 /* Corrupt the pointer with a random value, and avoid optimizations. */
135 printf (format: "Before: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
136 memset (c, mask & 0xFF, size);
137 printf (format: "After: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
138
139 c = calloc (nmemb: 1, size: size);
140 printf (format: "Allocated: c=%p\n", c);
141 /* This line will trigger the Safe-Linking check. */
142 b = calloc (nmemb: 1, size: size);
143 printf (format: "b=%p\n", b);
144}
145
146/* Try corrupting the fastbin list and trigger a consolidate. */
147static void
148test_fastbin_consolidate (void *closure)
149{
150 int i;
151 int mask = ((int*)closure)[0];
152 size_t size = TCACHE_ALLOC_SIZE;
153
154 printf (format: "++ 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 (nmemb: 1, size: size);
160 free (ptr: p);
161 }
162
163 /* Populate the fastbin list. */
164 void * volatile a = calloc (nmemb: 1, size: size);
165 void * volatile b = calloc (nmemb: 1, size: size);
166 void * volatile c = calloc (nmemb: 1, size: size);
167 printf (format: "a=%p, b=%p, c=%p\n", a, b, c);
168 free (ptr: a);
169 free (ptr: b);
170 free (ptr: c);
171
172 /* Corrupt the pointer with a random value, and avoid optimizations. */
173 printf (format: "Before: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
174 memset (c, mask & 0xFF, size);
175 printf (format: "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 (format: "b=%p\n", b);
180}
181
182static int
183do_test (void)
184{
185 /* Seed the random for the test. */
186 srand (seed: time (NULL));
187
188 check (test: "test_tcache", callback: test_tcache,
189 expected: "malloc(): unaligned tcache chunk detected\n");
190 check (test: "test_fastbin", callback: test_fastbin,
191 expected: "malloc(): unaligned fastbin chunk detected 2\n");
192 check (test: "test_fastbin_consolidate", callback: test_fastbin_consolidate,
193 expected: "malloc_consolidate(): unaligned fastbin chunk detected\n");
194
195 return 0;
196}
197
198#include <support/test-driver.c>
199

source code of glibc/malloc/tst-safe-linking.c