1/* Test reporting of Safe-Linking caught errors.
2 Copyright (C) 2020-2024 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#include "tst-malloc-aux.h"
30
31/* Run CALLBACK and check that the data on standard error equals
32 EXPECTED. */
33static void
34check (const char *test, void (*callback) (void *),
35 const char *expected)
36{
37 int i, rand_mask;
38 int success = 0; /* 0 == fail, 1 == other check 2 == safe linking */
39 /* There is a chance of 1/16 that a corrupted pointer will be aligned.
40 Try multiple times so that statistical failure will be improbable. */
41 for (i = 0; i < 16; ++i)
42 {
43 rand_mask = rand () & 0xFF;
44 struct support_capture_subprocess result
45 = support_capture_subprocess (callback, closure: &rand_mask);
46 printf (format: "%s\n", result.out.buffer);
47 /* Did not crash, could happen. Try again. */
48 if (strlen (result.err.buffer) == 0)
49 continue;
50 /* Crashed, it may either be safe linking or some other check. If it's
51 not safe linking then try again. */
52 if (strcmp (result.err.buffer, expected) != 0)
53 {
54 printf (format: "test %s failed with a different error\n"
55 " expected: %s\n"
56 " actual: %s\n",
57 test, expected, result.err.buffer);
58 success = 1;
59 continue;
60 }
61 TEST_VERIFY (WIFSIGNALED (result.status));
62 if (WIFSIGNALED (result.status))
63 TEST_VERIFY (WTERMSIG (result.status) == SIGABRT);
64 support_capture_subprocess_free (&result);
65 success = 2;
66 break;
67 }
68 /* The test fails only if the corruption was not caught by any of the malloc
69 mechanisms in all those iterations. This has a lower than 1 in 2^64
70 chance of a false positive. */
71 TEST_VERIFY (success);
72}
73
74/* Implementation details must be kept in sync with malloc. */
75#define TCACHE_FILL_COUNT 7
76#define TCACHE_ALLOC_SIZE 0x20
77#define MALLOC_CONSOLIDATE_SIZE 256*1024
78
79/* Try corrupting the tcache list. */
80static void
81test_tcache (void *closure)
82{
83 int mask = ((int *)closure)[0];
84 size_t size = TCACHE_ALLOC_SIZE;
85
86 printf (format: "++ tcache ++\n");
87
88 /* Populate the tcache list. */
89 void * volatile a = malloc (size);
90 void * volatile b = malloc (size);
91 void * volatile c = malloc (size);
92 printf (format: "a=%p, b=%p, c=%p\n", a, b, c);
93 free (ptr: a);
94 free (ptr: b);
95 free (ptr: c);
96
97 /* Corrupt the pointer with a random value, and avoid optimizations. */
98 printf (format: "Before: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
99 memset (c, mask & 0xFF, size);
100 printf (format: "After: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
101
102 c = malloc (size);
103 printf (format: "Allocated: c=%p\n", c);
104 /* This line will trigger the Safe-Linking check. */
105 b = malloc (size);
106 printf (format: "b=%p\n", b);
107}
108
109/* Try corrupting the fastbin list. */
110static void
111test_fastbin (void *closure)
112{
113 int i;
114 int mask = ((int *)closure)[0];
115 size_t size = TCACHE_ALLOC_SIZE;
116
117 printf (format: "++ fastbin ++\n");
118
119 /* Take the tcache out of the game. */
120 for (i = 0; i < TCACHE_FILL_COUNT; ++i)
121 {
122 void * volatile p = calloc (1, size);
123 printf (format: "p=%p\n", p);
124 free (ptr: p);
125 }
126
127 /* Populate the fastbin list. */
128 void * volatile a = calloc (1, size);
129 void * volatile b = calloc (1, size);
130 void * volatile c = calloc (1, size);
131 printf (format: "a=%p, b=%p, c=%p\n", a, b, c);
132 free (ptr: a);
133 free (ptr: b);
134 free (ptr: c);
135
136 /* Corrupt the pointer with a random value, and avoid optimizations. */
137 printf (format: "Before: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
138 memset (c, mask & 0xFF, size);
139 printf (format: "After: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
140
141 c = calloc (1, size);
142 printf (format: "Allocated: c=%p\n", c);
143 /* This line will trigger the Safe-Linking check. */
144 b = calloc (1, size);
145 printf (format: "b=%p\n", b);
146}
147
148/* Try corrupting the fastbin list and trigger a consolidate. */
149static void
150test_fastbin_consolidate (void *closure)
151{
152 int i;
153 int mask = ((int*)closure)[0];
154 size_t size = TCACHE_ALLOC_SIZE;
155
156 printf (format: "++ fastbin consolidate ++\n");
157
158 /* Take the tcache out of the game. */
159 for (i = 0; i < TCACHE_FILL_COUNT; ++i)
160 {
161 void * volatile p = calloc (1, size);
162 free (ptr: p);
163 }
164
165 /* Populate the fastbin list. */
166 void * volatile a = calloc (1, size);
167 void * volatile b = calloc (1, size);
168 void * volatile c = calloc (1, size);
169 printf (format: "a=%p, b=%p, c=%p\n", a, b, c);
170 free (ptr: a);
171 free (ptr: b);
172 free (ptr: c);
173
174 /* Corrupt the pointer with a random value, and avoid optimizations. */
175 printf (format: "Before: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
176 memset (c, mask & 0xFF, size);
177 printf (format: "After: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
178
179 /* This line will trigger the Safe-Linking check. */
180 b = malloc (MALLOC_CONSOLIDATE_SIZE);
181 printf (format: "b=%p\n", b);
182}
183
184static int
185do_test (void)
186{
187 /* Seed the random for the test. */
188 srand (seed: time (NULL));
189
190 check (test: "test_tcache", callback: test_tcache,
191 expected: "malloc(): unaligned tcache chunk detected\n");
192 check (test: "test_fastbin", callback: test_fastbin,
193 expected: "malloc(): unaligned fastbin chunk detected 2\n");
194 check (test: "test_fastbin_consolidate", callback: test_fastbin_consolidate,
195 expected: "malloc_consolidate(): unaligned fastbin chunk detected\n");
196
197 return 0;
198}
199
200#include <support/test-driver.c>
201

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