Coverage Report

Created: 2025-11-16 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/nanopb/tests/build/common/malloc_wrappers.c
Line
Count
Source
1
/* The wrapper functions in this file work like regular malloc() and free(),
2
 * but store check values before and after the allocation. This helps to catch
3
 * any buffer overrun errors in the test cases.
4
 *
5
 * This is done to help testing on embedded targets, where regular valgrind
6
 * and other systems are not necessarily available.
7
 */
8
9
#include "malloc_wrappers.h"
10
#include <stdint.h>
11
#include <stdio.h>
12
#include <assert.h>
13
#include <string.h>
14
15
110M
#define GUARD_SIZE (sizeof(size_t)*3)
16
229M
#define PREFIX_SIZE (sizeof(size_t)*2)
17
56.3M
#define CHECK1 ((size_t)0xDEADBEEF)
18
2.45M
#define CHECK2 ((size_t)0x600DCAFE)
19
20
#ifndef MAX_ALLOC_BYTES
21
#define MAX_ALLOC_BYTES 16*1024*1024
22
#endif
23
24
#ifndef DEBUG_MALLOC
25
58.7M
#define DEBUG_MALLOC 0
26
#endif
27
28
static size_t g_alloc_count = 0;
29
static size_t g_alloc_bytes = 0;
30
static size_t g_max_alloc_bytes = MAX_ALLOC_BYTES;
31
32
#ifdef LLVMFUZZER
33
/* LLVM libsanitizer has a realloc() implementation that always copies
34
 * the whole memory block, even if there would be space to expand it in
35
 * place. This gets pretty slow when fuzzing, so this wrapper limits the
36
 * realloc() calls by rounding allocation size upwards. Real world
37
 * realloc() implementations are hopefully smarter. */
38
static size_t round_blocksize(size_t size)
39
110M
{
40
110M
    if (size < 256)
41
9.34M
    {
42
9.34M
        return size;
43
9.34M
    }
44
100M
    else
45
100M
    {
46
100M
        return (size + 1023) / 1024 * 1024;
47
100M
    }
48
110M
}
49
#else
50
static size_t round_blocksize(size_t size)
51
{
52
    return size;
53
}
54
#endif
55
56
/* Allocate memory and place check values before and after. */
57
void* malloc_with_check(size_t size)
58
2.45M
{
59
2.45M
    const size_t check2 = CHECK2;
60
2.45M
    char *buf = NULL;
61
62
2.45M
    if (size <= g_max_alloc_bytes - g_alloc_bytes)
63
2.45M
    {
64
2.45M
        buf = malloc(round_blocksize(size + GUARD_SIZE));
65
2.45M
    }
66
67
2.45M
    if (buf)
68
2.45M
    {
69
2.45M
        ((size_t*)buf)[0] = size;
70
2.45M
        ((size_t*)buf)[1] = CHECK1;
71
2.45M
        memcpy(buf + PREFIX_SIZE + size, &check2, sizeof(check2));
72
73
2.45M
        g_alloc_count++;
74
2.45M
        g_alloc_bytes += size;
75
2.45M
        if (DEBUG_MALLOC) fprintf(stderr, "Alloc 0x%04x/%u\n", (unsigned)(uintptr_t)(buf + PREFIX_SIZE), (unsigned)size);
76
2.45M
        return buf + PREFIX_SIZE;
77
2.45M
    }
78
189
    else
79
189
    {
80
189
        if (DEBUG_MALLOC) fprintf(stderr, "malloc(%u) failed\n", (unsigned)size);
81
189
        return NULL;
82
189
    }
83
2.45M
}
84
85
/* Free memory allocated with malloc_with_check() and do the checks. */
86
void free_with_check(void *mem)
87
9.29M
{
88
9.29M
    if (mem)
89
2.45M
    {
90
2.45M
        char *buf = (char*)mem - PREFIX_SIZE;
91
2.45M
        size_t check2 = 0;
92
2.45M
        size_t size = ((size_t*)buf)[0];
93
2.45M
        if (DEBUG_MALLOC) fprintf(stderr, "Release 0x%04x/%u\n", (unsigned)(uintptr_t)mem, (unsigned)size);
94
2.45M
        assert(((size_t*)buf)[1] == CHECK1);
95
2.45M
        memcpy(&check2, buf + PREFIX_SIZE + size, sizeof(check2));
96
2.45M
        assert(check2 == CHECK2);
97
2.45M
        assert(g_alloc_count > 0);
98
2.45M
        assert(g_alloc_bytes >= size);
99
2.45M
        ((size_t*)buf)[1] = 0;
100
2.45M
        memset(buf + PREFIX_SIZE + size, 0, sizeof(check2));
101
2.45M
        g_alloc_count--;
102
2.45M
        g_alloc_bytes -= size;
103
2.45M
        free(buf);
104
2.45M
    }
105
9.29M
}
106
107
/* Reallocate block and check / write guard values */
108
void* realloc_with_check(void *ptr, size_t size)
109
56.2M
{
110
56.2M
    if (!ptr && size)
111
2.38M
    {
112
        /* Allocate new block and write guard values */
113
2.38M
        return malloc_with_check(size);
114
2.38M
    }
115
53.8M
    else if (ptr && size)
116
53.8M
    {
117
        /* Change block size */
118
53.8M
        char *buf = (char*)ptr - PREFIX_SIZE;
119
53.8M
        size_t check2 = 0;
120
53.8M
        size_t oldsize = ((size_t*)buf)[0];
121
53.8M
        assert(((size_t*)buf)[1] == CHECK1);
122
53.8M
        memcpy(&check2, buf + PREFIX_SIZE + oldsize, sizeof(check2));
123
53.8M
        assert(check2 == CHECK2);
124
53.8M
        assert(g_alloc_count > 0);
125
53.8M
        assert(g_alloc_bytes >= oldsize);
126
127
53.8M
        if (size <= g_max_alloc_bytes - (g_alloc_bytes - oldsize))
128
53.8M
        {
129
53.8M
            size_t new_rounded = round_blocksize(size + GUARD_SIZE);
130
53.8M
            size_t old_rounded = round_blocksize(oldsize + GUARD_SIZE);
131
132
53.8M
            if (new_rounded != old_rounded)
133
610k
            {
134
610k
                buf = realloc(buf, new_rounded);
135
610k
            }
136
53.8M
        }
137
63
        else
138
63
        {
139
63
            buf = NULL;
140
63
        }
141
142
53.8M
        if (!buf)
143
63
        {
144
63
            if (DEBUG_MALLOC) fprintf(stderr, "Realloc 0x%04x/%u to %u failed\n", (unsigned)(uintptr_t)ptr, (unsigned)oldsize, (unsigned)size);
145
63
            return NULL;
146
63
        }
147
148
53.8M
        ((size_t*)buf)[0] = size;
149
53.8M
        ((size_t*)buf)[1] = CHECK1;
150
53.8M
        memcpy(buf + PREFIX_SIZE + size, &check2, sizeof(check2));
151
53.8M
        g_alloc_bytes -= oldsize;
152
53.8M
        g_alloc_bytes += size;
153
154
53.8M
        if (DEBUG_MALLOC) fprintf(stderr, "Realloc 0x%04x/%u to 0x%04x/%u\n", (unsigned)(uintptr_t)ptr, (unsigned)oldsize, (unsigned)(uintptr_t)(buf + PREFIX_SIZE), (unsigned)size);
155
53.8M
        return buf + PREFIX_SIZE;
156
53.8M
    }
157
0
    else if (ptr && !size)
158
0
    {
159
        /* Deallocate */
160
0
        free_with_check(ptr);
161
0
        return NULL;
162
0
    }
163
0
    else
164
0
    {
165
        /* No action */
166
0
        return NULL;
167
0
    }
168
56.2M
}
169
170
/* Return total number of allocations not yet released */
171
size_t get_alloc_count()
172
122k
{
173
122k
    return g_alloc_count;
174
122k
}
175
176
/* Return allocated size for a pointer returned from malloc(). */
177
size_t get_allocation_size(const void *mem)
178
1.91M
{
179
1.91M
    char *buf = (char*)mem - PREFIX_SIZE;
180
1.91M
    return ((size_t*)buf)[0];
181
1.91M
}
182
183
/* Get total number of allocated bytes */
184
size_t get_alloc_bytes()
185
4.29k
{
186
4.29k
    return g_alloc_bytes;
187
4.29k
}
188
189
/* Set limit for allocation size */
190
void set_max_alloc_bytes(size_t max_bytes)
191
8.58k
{
192
8.58k
    g_max_alloc_bytes = max_bytes;
193
8.58k
}
194
195
size_t get_max_alloc_bytes()
196
4.29k
{
197
4.29k
    return g_max_alloc_bytes;
198
4.29k
}