1/* glibc.malloc.check implementation.
2 Copyright (C) 2001-2021 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Wolfram Gloger <wg@malloc.de>, 2001.
5
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
10
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; see the file COPYING.LIB. If
18 not, see <https://www.gnu.org/licenses/>. */
19
20#define __mremap mremap
21#include "malloc.c"
22
23/* When memory is tagged, the checking data is stored in the user part
24 of the chunk. We can't rely on the user not having modified the
25 tags, so fetch the tag at each location before dereferencing
26 it. */
27#define SAFE_CHAR_OFFSET(p,offset) \
28 ((unsigned char *) tag_at (((unsigned char *) p) + offset))
29
30/* A simple, standard set of debugging hooks. Overhead is `only' one
31 byte per chunk; still this will catch most cases of double frees or
32 overruns. The goal here is to avoid obscure crashes due to invalid
33 usage, unlike in the MALLOC_DEBUG code. */
34
35static unsigned char
36magicbyte (const void *p)
37{
38 unsigned char magic;
39
40 magic = (((uintptr_t) p >> 3) ^ ((uintptr_t) p >> 11)) & 0xFF;
41 /* Do not return 1. See the comment in mem2mem_check(). */
42 if (magic == 1)
43 ++magic;
44 return magic;
45}
46
47/* Visualize the chunk as being partitioned into blocks of 255 bytes from the
48 highest address of the chunk, downwards. The end of each block tells
49 us the size of that block, up to the actual size of the requested
50 memory. Our magic byte is right at the end of the requested size, so we
51 must reach it with this iteration, otherwise we have witnessed a memory
52 corruption. */
53static size_t
54malloc_check_get_size (void *mem)
55{
56 size_t size;
57 unsigned char c;
58 mchunkptr p = mem2chunk (mem);
59 unsigned char magic = magicbyte (p);
60
61 for (size = CHUNK_HDR_SZ + memsize (p) - 1;
62 (c = *SAFE_CHAR_OFFSET (p, size)) != magic;
63 size -= c)
64 {
65 if (c <= 0 || size < (c + CHUNK_HDR_SZ))
66 malloc_printerr ("malloc_check_get_size: memory corruption");
67 }
68
69 /* chunk2mem size. */
70 return size - CHUNK_HDR_SZ;
71}
72
73/* Instrument a chunk with overrun detector byte(s) and convert it
74 into a user pointer with requested size req_sz. */
75
76static void *
77mem2mem_check (void *ptr, size_t req_sz)
78{
79 mchunkptr p;
80 unsigned char *m_ptr = ptr;
81 size_t max_sz, block_sz, i;
82 unsigned char magic;
83
84 if (!ptr)
85 return ptr;
86
87 p = mem2chunk (ptr);
88 magic = magicbyte (p);
89 max_sz = memsize (p);
90
91 for (i = max_sz - 1; i > req_sz; i -= block_sz)
92 {
93 block_sz = MIN (i - req_sz, 0xff);
94 /* Don't allow the magic byte to appear in the chain of length bytes.
95 For the following to work, magicbyte cannot return 0x01. */
96 if (block_sz == magic)
97 --block_sz;
98
99 *SAFE_CHAR_OFFSET (m_ptr, i) = block_sz;
100 }
101 *SAFE_CHAR_OFFSET (m_ptr, req_sz) = magic;
102 return (void *) m_ptr;
103}
104
105/* Convert a pointer to be free()d or realloc()ed to a valid chunk
106 pointer. If the provided pointer is not valid, return NULL. */
107
108static mchunkptr
109mem2chunk_check (void *mem, unsigned char **magic_p)
110{
111 mchunkptr p;
112 INTERNAL_SIZE_T sz, c;
113 unsigned char magic;
114
115 if (!aligned_OK (mem))
116 return NULL;
117
118 p = mem2chunk (mem);
119 sz = chunksize (p);
120 magic = magicbyte (p);
121 if (!chunk_is_mmapped (p))
122 {
123 /* Must be a chunk in conventional heap memory. */
124 int contig = contiguous (&main_arena);
125 if ((contig &&
126 ((char *) p < mp_.sbrk_base ||
127 ((char *) p + sz) >= (mp_.sbrk_base + main_arena.system_mem))) ||
128 sz < MINSIZE || sz & MALLOC_ALIGN_MASK || !inuse (p) ||
129 (!prev_inuse (p) && ((prev_size (p) & MALLOC_ALIGN_MASK) != 0 ||
130 (contig && (char *) prev_chunk (p) < mp_.sbrk_base) ||
131 next_chunk (prev_chunk (p)) != p)))
132 return NULL;
133
134 for (sz = CHUNK_HDR_SZ + memsize (p) - 1;
135 (c = *SAFE_CHAR_OFFSET (p, sz)) != magic;
136 sz -= c)
137 {
138 if (c == 0 || sz < (c + CHUNK_HDR_SZ))
139 return NULL;
140 }
141 }
142 else
143 {
144 unsigned long offset, page_mask = GLRO (dl_pagesize) - 1;
145
146 /* mmap()ed chunks have MALLOC_ALIGNMENT or higher power-of-two
147 alignment relative to the beginning of a page. Check this
148 first. */
149 offset = (unsigned long) mem & page_mask;
150 if ((offset != MALLOC_ALIGNMENT && offset != 0 && offset != 0x10 &&
151 offset != 0x20 && offset != 0x40 && offset != 0x80 && offset != 0x100 &&
152 offset != 0x200 && offset != 0x400 && offset != 0x800 && offset != 0x1000 &&
153 offset < 0x2000) ||
154 !chunk_is_mmapped (p) || prev_inuse (p) ||
155 ((((unsigned long) p - prev_size (p)) & page_mask) != 0) ||
156 ((prev_size (p) + sz) & page_mask) != 0)
157 return NULL;
158
159 for (sz = CHUNK_HDR_SZ + memsize (p) - 1;
160 (c = *SAFE_CHAR_OFFSET (p, sz)) != magic;
161 sz -= c)
162 {
163 if (c == 0 || sz < (c + CHUNK_HDR_SZ))
164 return NULL;
165 }
166 }
167
168 unsigned char* safe_p = SAFE_CHAR_OFFSET (p, sz);
169 *safe_p ^= 0xFF;
170 if (magic_p)
171 *magic_p = safe_p;
172 return p;
173}
174
175/* Check for corruption of the top chunk. */
176static void
177top_check (void)
178{
179 mchunkptr t = top (&main_arena);
180
181 if (t == initial_top (&main_arena) ||
182 (!chunk_is_mmapped (t) &&
183 chunksize (t) >= MINSIZE &&
184 prev_inuse (t) &&
185 (!contiguous (&main_arena) ||
186 (char *) t + chunksize (t) == mp_.sbrk_base + main_arena.system_mem)))
187 return;
188
189 malloc_printerr ("malloc: top chunk is corrupt");
190}
191
192static void *
193malloc_check (size_t sz)
194{
195 void *victim;
196 size_t nb;
197
198 if (__builtin_add_overflow (sz, 1, &nb))
199 {
200 __set_errno (ENOMEM);
201 return NULL;
202 }
203
204 __libc_lock_lock (main_arena.mutex);
205 top_check ();
206 victim = _int_malloc (&main_arena, nb);
207 __libc_lock_unlock (main_arena.mutex);
208 return mem2mem_check (tag_new_usable (victim), sz);
209}
210
211static void
212free_check (void *mem)
213{
214 mchunkptr p;
215
216 if (!mem)
217 return;
218
219 int err = errno;
220
221 /* Quickly check that the freed pointer matches the tag for the memory.
222 This gives a useful double-free detection. */
223 if (__glibc_unlikely (mtag_enabled))
224 *(volatile char *)mem;
225
226 __libc_lock_lock (main_arena.mutex);
227 p = mem2chunk_check (mem, NULL);
228 if (!p)
229 malloc_printerr ("free(): invalid pointer");
230 if (chunk_is_mmapped (p))
231 {
232 __libc_lock_unlock (main_arena.mutex);
233 munmap_chunk (p);
234 }
235 else
236 {
237 /* Mark the chunk as belonging to the library again. */
238 (void)tag_region (chunk2mem (p), memsize (p));
239 _int_free (&main_arena, p, 1);
240 __libc_lock_unlock (main_arena.mutex);
241 }
242 __set_errno (err);
243}
244
245static void *
246realloc_check (void *oldmem, size_t bytes)
247{
248 INTERNAL_SIZE_T chnb;
249 void *newmem = 0;
250 unsigned char *magic_p;
251 size_t rb;
252
253 if (__builtin_add_overflow (bytes, 1, &rb))
254 {
255 __set_errno (ENOMEM);
256 return NULL;
257 }
258 if (oldmem == 0)
259 return malloc_check (bytes);
260
261 if (bytes == 0)
262 {
263 free_check (oldmem);
264 return NULL;
265 }
266
267 /* Quickly check that the freed pointer matches the tag for the memory.
268 This gives a useful double-free detection. */
269 if (__glibc_unlikely (mtag_enabled))
270 *(volatile char *)oldmem;
271
272 __libc_lock_lock (main_arena.mutex);
273 const mchunkptr oldp = mem2chunk_check (oldmem, &magic_p);
274 __libc_lock_unlock (main_arena.mutex);
275 if (!oldp)
276 malloc_printerr ("realloc(): invalid pointer");
277 const INTERNAL_SIZE_T oldsize = chunksize (oldp);
278
279 if (!checked_request2size (rb, &chnb))
280 {
281 __set_errno (ENOMEM);
282 goto invert;
283 }
284
285 __libc_lock_lock (main_arena.mutex);
286
287 if (chunk_is_mmapped (oldp))
288 {
289#if HAVE_MREMAP
290 mchunkptr newp = mremap_chunk (oldp, chnb);
291 if (newp)
292 newmem = chunk2mem_tag (newp);
293 else
294#endif
295 {
296 /* Note the extra SIZE_SZ overhead. */
297 if (oldsize - SIZE_SZ >= chnb)
298 newmem = oldmem; /* do nothing */
299 else
300 {
301 /* Must alloc, copy, free. */
302 top_check ();
303 newmem = _int_malloc (&main_arena, rb);
304 if (newmem)
305 {
306 memcpy (newmem, oldmem, oldsize - CHUNK_HDR_SZ);
307 munmap_chunk (oldp);
308 }
309 }
310 }
311 }
312 else
313 {
314 top_check ();
315 newmem = _int_realloc (&main_arena, oldp, oldsize, chnb);
316 }
317
318 DIAG_PUSH_NEEDS_COMMENT;
319#if __GNUC_PREREQ (7, 0)
320 /* GCC 7 warns about magic_p may be used uninitialized. But we never
321 reach here if magic_p is uninitialized. */
322 DIAG_IGNORE_NEEDS_COMMENT (7, "-Wmaybe-uninitialized");
323#endif
324 /* mem2chunk_check changed the magic byte in the old chunk.
325 If newmem is NULL, then the old chunk will still be used though,
326 so we need to invert that change here. */
327invert:
328 if (newmem == NULL)
329 *magic_p ^= 0xFF;
330 DIAG_POP_NEEDS_COMMENT;
331
332 __libc_lock_unlock (main_arena.mutex);
333
334 return mem2mem_check (tag_new_usable (newmem), bytes);
335}
336
337static void *
338memalign_check (size_t alignment, size_t bytes)
339{
340 void *mem;
341
342 if (alignment <= MALLOC_ALIGNMENT)
343 return malloc_check (bytes);
344
345 if (alignment < MINSIZE)
346 alignment = MINSIZE;
347
348 /* If the alignment is greater than SIZE_MAX / 2 + 1 it cannot be a
349 power of 2 and will cause overflow in the check below. */
350 if (alignment > SIZE_MAX / 2 + 1)
351 {
352 __set_errno (EINVAL);
353 return NULL;
354 }
355
356 /* Check for overflow. */
357 if (bytes > SIZE_MAX - alignment - MINSIZE)
358 {
359 __set_errno (ENOMEM);
360 return NULL;
361 }
362
363 /* Make sure alignment is power of 2. */
364 if (!powerof2 (alignment))
365 {
366 size_t a = MALLOC_ALIGNMENT * 2;
367 while (a < alignment)
368 a <<= 1;
369 alignment = a;
370 }
371
372 __libc_lock_lock (main_arena.mutex);
373 top_check ();
374 mem = _int_memalign (&main_arena, alignment, bytes + 1);
375 __libc_lock_unlock (main_arena.mutex);
376 return mem2mem_check (tag_new_usable (mem), bytes);
377}
378
379#if HAVE_TUNABLES
380static void
381TUNABLE_CALLBACK (set_mallopt_check) (tunable_val_t *valp)
382{
383 int32_t value = (int32_t) valp->numval;
384 if (value != 0)
385 __malloc_debug_enable (MALLOC_CHECK_HOOK);
386}
387#endif
388
389static bool
390initialize_malloc_check (void)
391{
392 /* This is the copy of the malloc initializer that we pulled in along with
393 malloc-check. This does not affect any of the libc malloc structures. */
394 ptmalloc_init ();
395#if HAVE_TUNABLES
396 TUNABLE_GET (check, int32_t, TUNABLE_CALLBACK (set_mallopt_check));
397#else
398 const char *s = secure_getenv ("MALLOC_CHECK_");
399 if (s && s[0] != '\0' && s[0] != '0')
400 __malloc_debug_enable (MALLOC_CHECK_HOOK);
401#endif
402 return __is_malloc_debug_enabled (MALLOC_CHECK_HOOK);
403}
404