SingingCat 0
application
umm_poison.c
1/* poisoning (UMM_POISON_CHECK) {{{ */
2#if defined(UMM_POISON_CHECK)
3#define POISON_BYTE (0xa5)
4
5#include <stdint.h>
6#include <stddef.h>
7#include <stdbool.h>
8
9/*
10 * Yields a size of the poison for the block of size `s`.
11 * If `s` is 0, returns 0.
12 */
13static size_t poison_size(size_t s) {
14 return s ? (UMM_POISON_SIZE_BEFORE +
15 sizeof(UMM_POISONED_BLOCK_LEN_TYPE) +
16 UMM_POISON_SIZE_AFTER)
17 : 0;
18}
19
20/*
21 * Print memory contents starting from given `ptr`
22 */
23static void dump_mem(const void *ptr, size_t len) {
24 while (len--) {
25 DBGLOG_ERROR(" 0x%.2x", (*(uint8_t *)ptr++));
26 }
27}
28
29/*
30 * Put poison data at given `ptr` and `poison_size`
31 */
32static void put_poison(void *ptr, size_t poison_size) {
33 memset(ptr, POISON_BYTE, poison_size);
34}
35
36/*
37 * Check poison data at given `ptr` and `poison_size`. `where` is a pointer to
38 * a string, either "before" or "after", meaning, before or after the block.
39 *
40 * If poison is there, returns 1.
41 * Otherwise, prints the appropriate message, and returns 0.
42 */
43static bool check_poison(const void *ptr, size_t poison_size,
44 const void *where) {
45 size_t i;
46 bool ok = true;
47
48 for (i = 0; i < poison_size; i++) {
49 if (((uint8_t *)ptr)[i] != POISON_BYTE) {
50 ok = false;
51 break;
52 }
53 }
54
55 if (!ok) {
56 DBGLOG_ERROR("No poison %s block at: 0x%08x, actual data:", (char *)where, DBGLOG_32_BIT_PTR(ptr));
57 dump_mem(ptr, poison_size);
58 DBGLOG_ERROR("\n");
59 }
60
61 return ok;
62}
63
64/*
65 * Check if a block is properly poisoned. Must be called only for non-free
66 * blocks.
67 */
68static bool check_poison_block(umm_block *pblock) {
69 bool ok = true;
70
71 if (pblock->header.used.next & UMM_FREELIST_MASK) {
72 DBGLOG_ERROR("check_poison_block is called for free block 0x%08x\n", DBGLOG_32_BIT_PTR(pblock));
73 } else {
74 /* the block is used; let's check poison */
75 void *pc = (void *)pblock->body.data;
76 void *pc_cur;
77
78 pc_cur = pc + sizeof(UMM_POISONED_BLOCK_LEN_TYPE);
79 if (!check_poison(pc_cur, UMM_POISON_SIZE_BEFORE, "before")) {
80 ok = false;
81 goto clean;
82 }
83
84 pc_cur = pc + *((UMM_POISONED_BLOCK_LEN_TYPE *)pc) - UMM_POISON_SIZE_AFTER;
85 if (!check_poison(pc_cur, UMM_POISON_SIZE_AFTER, "after")) {
86 ok = false;
87 goto clean;
88 }
89 }
90
91clean:
92 return ok;
93}
94
95/*
96 * Takes a pointer returned by actual allocator function (`umm_malloc` or
97 * `umm_realloc`), puts appropriate poison, and returns adjusted pointer that
98 * should be returned to the user.
99 *
100 * `size_w_poison` is a size of the whole block, including a poison.
101 */
102static void *get_poisoned(void *ptr, size_t size_w_poison) {
103 if (size_w_poison != 0 && ptr != NULL) {
104 /* Poison beginning and the end of the allocated chunk */
105 put_poison(ptr + sizeof(UMM_POISONED_BLOCK_LEN_TYPE),
106 UMM_POISON_SIZE_BEFORE);
107 put_poison(ptr + size_w_poison - UMM_POISON_SIZE_AFTER,
108 UMM_POISON_SIZE_AFTER);
109
110 /* Put exact length of the user's chunk of memory */
111 *(UMM_POISONED_BLOCK_LEN_TYPE *)ptr = (UMM_POISONED_BLOCK_LEN_TYPE)size_w_poison;
112
113 /* Return pointer at the first non-poisoned byte */
114 return ptr + sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE;
115 } else {
116 return ptr;
117 }
118}
119
120/*
121 * Takes "poisoned" pointer (i.e. pointer returned from `get_poisoned()`),
122 * and checks that the poison of this particular block is still there.
123 *
124 * Returns unpoisoned pointer, i.e. actual pointer to the allocated memory.
125 */
126static void *get_unpoisoned(void *ptr) {
127 if (ptr != NULL) {
128 uint16_t c;
129
130 ptr -= (sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE);
131
132 /* Figure out which block we're in. Note the use of truncated division... */
133 c = (((void *)ptr) - (void *)(&(UMM_HEAP[0]))) / UMM_BLOCKSIZE;
134
135 check_poison_block(&UMM_BLOCK(c));
136 }
137
138 return ptr;
139}
140
141/* }}} */
142
143/* ------------------------------------------------------------------------ */
144
145void *umm_poison_malloc(size_t size) {
146 void *ret;
147
148 size += poison_size(size);
149
150 ret = umm_malloc(size);
151
152 ret = get_poisoned(ret, size);
153
154 return ret;
155}
156
157/* ------------------------------------------------------------------------ */
158
159void *umm_poison_calloc(size_t num, size_t item_size) {
160 void *ret;
161 size_t size = item_size * num;
162
163 size += poison_size(size);
164
165 ret = umm_malloc(size);
166
167 if (NULL != ret) {
168 memset(ret, 0x00, size);
169 }
170
171 ret = get_poisoned(ret, size);
172
173 return ret;
174}
175
176/* ------------------------------------------------------------------------ */
177
178void *umm_poison_realloc(void *ptr, size_t size) {
179 void *ret;
180
181 ptr = get_unpoisoned(ptr);
182
183 size += poison_size(size);
184 ret = umm_realloc(ptr, size);
185
186 ret = get_poisoned(ret, size);
187
188 return ret;
189}
190
191/* ------------------------------------------------------------------------ */
192
193void umm_poison_free(void *ptr) {
194 ptr = get_unpoisoned(ptr);
195
196 umm_free(ptr);
197}
198
199/*
200 * Iterates through all blocks in the heap, and checks poison for all used
201 * blocks.
202 */
203bool umm_poison_check(void) {
204 UMM_CRITICAL_DECL(id_poison);
205
206 bool ok = true;
207 unsigned short int cur;
208
209 UMM_CHECK_INITIALIZED();
210
211 UMM_CRITICAL_ENTRY(id_poison);
212
213 /* Now iterate through the blocks list */
214 cur = UMM_NBLOCK(0) & UMM_BLOCKNO_MASK;
215
216 while (UMM_NBLOCK(cur) & UMM_BLOCKNO_MASK) {
217 if (!(UMM_NBLOCK(cur) & UMM_FREELIST_MASK)) {
218 /* This is a used block (not free), so, check its poison */
219 ok = check_poison_block(&UMM_BLOCK(cur));
220 if (!ok) {
221 break;
222 }
223 }
224
225 cur = UMM_NBLOCK(cur) & UMM_BLOCKNO_MASK;
226 }
227 UMM_CRITICAL_EXIT(id_poison);
228
229 return ok;
230}
231
232/* ------------------------------------------------------------------------ */
233
234#endif