mem.c (5978B)
1 /*
2 * Copyright (c) 2013-2022 Joris Vink <joris@coders.se>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 /*
18 * The memory facitilies such as kore_malloc / kore_calloc are all
19 * based on the kore pool system as long as the allocations are
20 * below 8192 bytes.
21 *
22 * Anything over 8192 bytes will get an mmap() allocation instead
23 * that does not benefit from the protections offered by the kore_pool API.
24 */
25
26 #include <sys/types.h>
27 #include <sys/mman.h>
28
29 #include <stdlib.h>
30 #include <stdint.h>
31
32 #include "kore.h"
33
34 #define KORE_MEM_POOLS 11
35 #define KORE_MEM_POOLS_PREALLOC 32
36 #define KORE_MEM_POOLS_SIZE_MAX 8192
37
38 #define KORE_MEM_TAGGED 0x0001
39
40 struct meminfo {
41 size_t len;
42 u_int16_t flags;
43 };
44
45 struct tag {
46 void *ptr;
47 u_int32_t id;
48 TAILQ_ENTRY(tag) list;
49 };
50
51 static inline struct meminfo *meminfo(void *);
52 static void *mem_alloc(size_t);
53 static size_t mem_index(size_t);
54
55 static TAILQ_HEAD(, tag) tags;
56 static struct kore_pool tag_pool;
57 static struct kore_pool mempools[KORE_MEM_POOLS];
58
59 void
60 kore_mem_init(void)
61 {
62 const char *opt;
63 int i, len;
64 char name[32];
65 size_t size, elm, mlen;
66
67 if ((opt = getenv("KORE_MEM_GUARD")) != NULL && !strcmp(opt, "1"))
68 kore_mem_guard = 1;
69
70 size = 8;
71 TAILQ_INIT(&tags);
72 kore_pool_init(&tag_pool, "tag_pool", sizeof(struct tag), 4);
73
74 for (i = 0; i < KORE_MEM_POOLS; i++) {
75 len = snprintf(name, sizeof(name), "block-%zu", size);
76 if (len == -1 || (size_t)len >= sizeof(name))
77 fatal("kore_mem_init: snprintf");
78
79 elm = (KORE_MEM_POOLS_PREALLOC * 1024) / size;
80 mlen = sizeof(struct meminfo) + size;
81
82 kore_pool_init(&mempools[i], name, mlen, elm);
83
84 size = size << 1;
85 }
86 }
87
88 void
89 kore_mem_cleanup(void)
90 {
91 int i;
92
93 for (i = 0; i < KORE_MEM_POOLS; i++) {
94 kore_pool_cleanup(&mempools[i]);
95 }
96 }
97
98 void *
99 kore_mmap_region(size_t len)
100 {
101 void *ptr;
102
103 if ((ptr = mmap(NULL, len, PROT_READ | PROT_WRITE,
104 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)) == MAP_FAILED)
105 fatal("%s: mmap: %s", __func__, errno_s);
106
107 return (ptr);
108 }
109
110 void *
111 kore_malloc(size_t len)
112 {
113 return (mem_alloc(len));
114 }
115
116 void *
117 kore_realloc(void *ptr, size_t len)
118 {
119 struct meminfo *mem;
120 void *nptr;
121
122 if (ptr == NULL) {
123 nptr = mem_alloc(len);
124 } else {
125 mem = meminfo(ptr);
126 if (len == mem->len)
127 return (ptr);
128 nptr = mem_alloc(len);
129 memcpy(nptr, ptr, MIN(len, mem->len));
130 kore_free_zero(ptr);
131 }
132
133 return (nptr);
134 }
135
136 void *
137 kore_calloc(size_t memb, size_t len)
138 {
139 void *ptr;
140 size_t total;
141
142 if (SIZE_MAX / memb < len)
143 fatal("kore_calloc(): memb * len > SIZE_MAX");
144
145 total = memb * len;
146 ptr = mem_alloc(total);
147 memset(ptr, 0, total);
148
149 return (ptr);
150 }
151
152 void
153 kore_free_zero(void *ptr)
154 {
155 struct meminfo *mem;
156
157 if (ptr == NULL)
158 return;
159
160 mem = meminfo(ptr);
161 kore_mem_zero(ptr, mem->len);
162
163 kore_free(ptr);
164 }
165
166 void
167 kore_free(void *ptr)
168 {
169 size_t idx;
170 struct meminfo *mem;
171 u_int8_t *addr;
172
173 if (ptr == NULL)
174 return;
175
176 mem = meminfo(ptr);
177 if (mem->flags & KORE_MEM_TAGGED) {
178 kore_mem_untag(ptr);
179 mem->flags &= ~KORE_MEM_TAGGED;
180 }
181
182 addr = (u_int8_t *)ptr - sizeof(struct meminfo);
183
184 if (mem->len <= KORE_MEM_POOLS_SIZE_MAX) {
185 idx = mem_index(mem->len);
186 kore_pool_put(&mempools[idx], addr);
187 } else {
188 if (munmap(addr, sizeof(*mem) + mem->len) == -1)
189 fatal("%s: munmap: %s", __func__, errno_s);
190 }
191 }
192
193 char *
194 kore_strdup(const char *str)
195 {
196 size_t len;
197 char *nstr;
198
199 len = strlen(str) + 1;
200 nstr = mem_alloc(len);
201 (void)kore_strlcpy(nstr, str, len);
202
203 return (nstr);
204 }
205
206 void *
207 kore_malloc_tagged(size_t len, u_int32_t tag)
208 {
209 void *ptr;
210
211 ptr = mem_alloc(len);
212 kore_mem_tag(ptr, tag);
213
214 return (ptr);
215 }
216
217 void
218 kore_mem_tag(void *ptr, u_int32_t id)
219 {
220 struct tag *tag;
221 struct meminfo *mem;
222
223 if (kore_mem_lookup(id) != NULL)
224 fatal("kore_mem_tag: tag %u taken", id);
225
226 mem = meminfo(ptr);
227 mem->flags |= KORE_MEM_TAGGED;
228
229 tag = kore_pool_get(&tag_pool);
230 tag->id = id;
231 tag->ptr = ptr;
232
233 TAILQ_INSERT_TAIL(&tags, tag, list);
234 }
235
236 void
237 kore_mem_untag(void *ptr)
238 {
239 struct tag *tag;
240
241 TAILQ_FOREACH(tag, &tags, list) {
242 if (tag->ptr == ptr) {
243 TAILQ_REMOVE(&tags, tag, list);
244 kore_pool_put(&tag_pool, tag);
245 break;
246 }
247 }
248 }
249
250 void *
251 kore_mem_lookup(u_int32_t id)
252 {
253 struct tag *tag;
254
255 TAILQ_FOREACH(tag, &tags, list) {
256 if (tag->id == id)
257 return (tag->ptr);
258 }
259
260 return (NULL);
261 }
262
263 /* Best effort to try and let the compiler not optimize this call away. */
264 void
265 kore_mem_zero(void *ptr, size_t len)
266 {
267 volatile char *p;
268
269 p = (volatile char *)ptr;
270
271 if (p != NULL) {
272 while (len-- > 0)
273 *(p)++ = 0x00;
274 }
275 }
276
277 static void *
278 mem_alloc(size_t len)
279 {
280 void *ptr;
281 struct meminfo *mem;
282 size_t mlen, idx;
283
284 if (len == 0)
285 len = 8;
286
287 if (len <= KORE_MEM_POOLS_SIZE_MAX) {
288 idx = mem_index(len);
289 ptr = kore_pool_get(&mempools[idx]);
290 } else {
291 mlen = sizeof(struct meminfo) + len;
292 ptr = kore_mmap_region(mlen);
293 }
294
295 mem = (struct meminfo *)ptr;
296 mem->len = len;
297 mem->flags = 0;
298
299 return ((u_int8_t *)ptr + sizeof(struct meminfo));
300 }
301
302 static size_t
303 mem_index(size_t len)
304 {
305 size_t mlen, idx;
306
307 idx = 0;
308 mlen = 8;
309 while (mlen < len) {
310 idx++;
311 mlen = mlen << 1;
312 }
313
314 if (idx > (KORE_MEM_POOLS - 1))
315 fatal("mem_index: idx too high");
316
317 return (idx);
318 }
319
320 static inline struct meminfo *
321 meminfo(void *ptr)
322 {
323 return ((struct meminfo *)((u_int8_t *)ptr - sizeof(struct meminfo)));
324 }