blob: b038bb0539bfbd8ed8fc2a6738f1d496eeb24c69 [file] [log] [blame]
Eugene Urievf5f24c82024-03-31 23:03:21 +03001/* SPDX-License-Identifier: GPL-2.0+ */
2/*
3 * Copyright (C) 2024 Free Software Foundation, Inc.
4 * Written by Eugene Uriev, based on glibc 2.0 prototype of Mike Haertel.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 * This 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 * Library General Public License for more details.
15 * <https://www.gnu.org/licenses/>
16 */
17
18/*
19 * TL;DR: this is a porting of glibc mcheck into U-Boot
20 *
21 * This file contains no entities for external linkage.
22 * So mcheck protection may be used in parallel, e.g. for "malloc_simple(..)" and "malloc(..)".
23 * To do so, the file should be shared/include twice, - without linkage conflicts.
24 * I.e. "core"-part is shared as a source, but not as a binary.
25 * Maybe some optimization here make sense, to engage more binary sharing too.
26 * But, currently I strive to keep it as simple, as possible.
27 * And this, programmers'-only, mode don't pretend to be main.
28 *
29 * This library is aware of U-Boot specific. It's also aware of ARM alignment concerns.
30 * Unlike glibc-clients, U-Boot has limited malloc-usage, and only one thread.
31 * So it's better to make the protection heavier.
32 * Thus overflow canary here is greater, than glibc's one. Underflow canary is bigger too.
33 * Heavy canary allows to catch not only memset(..)-errors,
34 * but overflow/underflow of struct-array access:
35 * {
36 * struct mystruct* p = malloc(sizeof(struct mystruct) * N);
37 * p[-1].field1 = 0;
38 * p[N].field2 = 13;
39 * }
40 * TODO: In order to guarantee full coverage of that kind of errors, a user can add variable-size
41 * canaries here. So pre- and post-canary with size >= reqested_size, could be provided
42 * (with the price of 3x heap-usage). Therefore, it would catch 100% of changes beyond
43 * an array, for index(+1/-1) errors.
44 *
45 * U-Boot is a BL, not an OS with a lib. Activity of the library is set not in runtime,
46 * rather in compile-time, by MCHECK_HEAP_PROTECTION macro. That guarantees that
47 * we haven't missed first malloc.
48 */
49#ifndef _MCHECKCORE_INC_H
50#define _MCHECKCORE_INC_H 1
51#include "mcheck.h"
52
53#if defined(MCHECK_HEAP_PROTECTION)
54#define mcheck_flood memset
55
56// these are from /dev/random:
57#define MAGICWORD 0x99ccf430fa562a05ULL
58#define MAGICFREE 0x4875e63c0c6fc08eULL
59#define MAGICTAIL 0x918dbcd7df78dcd6ULL
60#define MALLOCFLOOD ((char)0xb6)
61#define FREEFLOOD ((char)0xf5)
62#define PADDINGFLOOD ((char)0x58)
63
64#define CANARY_DEPTH 2
65
66typedef unsigned long long mcheck_elem;
67typedef struct {
68 mcheck_elem elems[CANARY_DEPTH];
69} mcheck_canary;
70struct mcheck_hdr {
71 size_t size; /* Exact size requested by user. */
Eugene Urievc61e3132024-03-31 23:03:23 +030072 size_t aln_skip; /* Ignored bytes, before the mcheck_hdr, to fulfill alignment */
Eugene Urievf5f24c82024-03-31 23:03:21 +030073 mcheck_canary canary; /* Magic number to check header integrity. */
74};
75
76static void mcheck_default_abort(enum mcheck_status status)
77{
78 const char *msg;
79
80 switch (status) {
81 case MCHECK_OK:
82 msg = "memory is consistent, library is buggy\n";
83 break;
84 case MCHECK_HEAD:
85 msg = "memory clobbered before allocated block\n";
86 break;
87 case MCHECK_TAIL:
88 msg = "memory clobbered past end of allocated block\n";
89 break;
90 case MCHECK_FREE:
91 msg = "block freed twice\n";
92 break;
93 default:
94 msg = "bogus mcheck_status, library is buggy\n";
95 break;
96 }
97 printf("\n\nmcheck: %s!!!\n\n", msg);
98}
99
100static mcheck_abortfunc_t mcheck_abortfunc = &mcheck_default_abort;
101
102static inline size_t allign_size_up(size_t sz, size_t grain)
103{
104 return (sz + grain - 1) & ~(grain - 1);
105}
106
107#define mcheck_allign_customer_size(SZ) allign_size_up(SZ, sizeof(mcheck_elem))
Eugene Urievc61e3132024-03-31 23:03:23 +0300108#define mcheck_evaluate_memalign_prefix_size(ALIGN) allign_size_up(sizeof(struct mcheck_hdr), ALIGN)
Eugene Urievf5f24c82024-03-31 23:03:21 +0300109
110static enum mcheck_status mcheck_OnNok(enum mcheck_status status)
111{
112 (*mcheck_abortfunc)(status);
113 return status;
114}
115
116static enum mcheck_status mcheck_checkhdr(const struct mcheck_hdr *hdr)
117{
118 int i;
119
120 for (i = 0; i < CANARY_DEPTH; ++i)
121 if (hdr->canary.elems[i] == MAGICFREE)
122 return mcheck_OnNok(MCHECK_FREE);
123
124 for (i = 0; i < CANARY_DEPTH; ++i)
125 if (hdr->canary.elems[i] != MAGICWORD)
126 return mcheck_OnNok(MCHECK_HEAD);
127
128 const size_t payload_size = hdr->size;
129 const size_t payload_size_aligned = mcheck_allign_customer_size(payload_size);
130 const size_t padd_size = payload_size_aligned - hdr->size;
131
132 const char *payload = (const char *)&hdr[1];
133
134 for (i = 0; i < padd_size; ++i)
135 if (payload[payload_size + i] != PADDINGFLOOD)
136 return mcheck_OnNok(MCHECK_TAIL);
137
138 const mcheck_canary *tail = (const mcheck_canary *)&payload[payload_size_aligned];
139
140 for (i = 0; i < CANARY_DEPTH; ++i)
141 if (tail->elems[i] != MAGICTAIL)
142 return mcheck_OnNok(MCHECK_TAIL);
143 return MCHECK_OK;
144}
145
146enum { KEEP_CONTENT = 0, CLEAN_CONTENT, ANY_ALIGNMENT = 1 };
147static void *mcheck_free_helper(void *ptr, int clean_content)
148{
149 if (!ptr)
150 return ptr;
151
152 struct mcheck_hdr *hdr = &((struct mcheck_hdr *)ptr)[-1];
153 int i;
154
155 mcheck_checkhdr(hdr);
156 for (i = 0; i < CANARY_DEPTH; ++i)
157 hdr->canary.elems[i] = MAGICFREE;
158
159 if (clean_content)
160 mcheck_flood(ptr, FREEFLOOD, mcheck_allign_customer_size(hdr->size));
Eugene Urievc61e3132024-03-31 23:03:23 +0300161
162 return (char *)hdr - hdr->aln_skip;
Eugene Urievf5f24c82024-03-31 23:03:21 +0300163}
164
165static void *mcheck_free_prehook(void *ptr) { return mcheck_free_helper(ptr, CLEAN_CONTENT); }
166static void *mcheck_reallocfree_prehook(void *ptr) { return mcheck_free_helper(ptr, KEEP_CONTENT); }
167
168static size_t mcheck_alloc_prehook(size_t sz)
169{
170 sz = mcheck_allign_customer_size(sz);
171 return sizeof(struct mcheck_hdr) + sz + sizeof(mcheck_canary);
172}
173
174static void *mcheck_allocated_helper(void *altoghether_ptr, size_t customer_sz,
175 size_t alignment, int clean_content)
176{
Eugene Urievc61e3132024-03-31 23:03:23 +0300177 const size_t slop = alignment ?
178 mcheck_evaluate_memalign_prefix_size(alignment) - sizeof(struct mcheck_hdr) : 0;
179 struct mcheck_hdr *hdr = (struct mcheck_hdr *)((char *)altoghether_ptr + slop);
Eugene Urievf5f24c82024-03-31 23:03:21 +0300180 int i;
181
182 hdr->size = customer_sz;
Eugene Urievc61e3132024-03-31 23:03:23 +0300183 hdr->aln_skip = slop;
Eugene Urievf5f24c82024-03-31 23:03:21 +0300184 for (i = 0; i < CANARY_DEPTH; ++i)
185 hdr->canary.elems[i] = MAGICWORD;
186
187 char *payload = (char *)&hdr[1];
188
189 if (clean_content)
190 mcheck_flood(payload, MALLOCFLOOD, customer_sz);
191
192 const size_t customer_size_aligned = mcheck_allign_customer_size(customer_sz);
193
194 mcheck_flood(payload + customer_sz, PADDINGFLOOD, customer_size_aligned - customer_sz);
195
196 mcheck_canary *tail = (mcheck_canary *)&payload[customer_size_aligned];
197
198 for (i = 0; i < CANARY_DEPTH; ++i)
199 tail->elems[i] = MAGICTAIL;
200 return payload;
201}
202
203static void *mcheck_alloc_posthook(void *altoghether_ptr, size_t customer_sz)
204{
205 return mcheck_allocated_helper(altoghether_ptr, customer_sz, ANY_ALIGNMENT, CLEAN_CONTENT);
206}
207
208static void *mcheck_alloc_noclean_posthook(void *altoghether_ptr, size_t customer_sz)
209{
210 return mcheck_allocated_helper(altoghether_ptr, customer_sz, ANY_ALIGNMENT, KEEP_CONTENT);
211}
212
Eugene Urievc61e3132024-03-31 23:03:23 +0300213static size_t mcheck_memalign_prehook(size_t alig, size_t sz)
214{
215 return mcheck_evaluate_memalign_prefix_size(alig) + sz + sizeof(mcheck_canary);
216}
217
218static void *mcheck_memalign_posthook(size_t alignment, void *altoghether_ptr, size_t customer_sz)
219{
220 return mcheck_allocated_helper(altoghether_ptr, customer_sz, alignment, CLEAN_CONTENT);
221}
222
Eugene Urievf5f24c82024-03-31 23:03:21 +0300223static enum mcheck_status mcheck_mprobe(void *ptr)
224{
225 struct mcheck_hdr *hdr = &((struct mcheck_hdr *)ptr)[-1];
226
227 return mcheck_checkhdr(hdr);
228}
229
230static void mcheck_initialize(mcheck_abortfunc_t new_func, char pedantic_flag)
231{
232 mcheck_abortfunc = (new_func) ? new_func : &mcheck_default_abort;
233}
234
235#endif
236#endif