diff options
Diffstat (limited to 'src/lib/util/common_allocation.c')
-rw-r--r-- | src/lib/util/common_allocation.c | 504 |
1 files changed, 504 insertions, 0 deletions
diff --git a/src/lib/util/common_allocation.c b/src/lib/util/common_allocation.c new file mode 100644 index 000000000..36d49eddb --- /dev/null +++ b/src/lib/util/common_allocation.c | |||
@@ -0,0 +1,504 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2001, 2002, 2003, 2005, 2006 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file util/common_allocation.c | ||
23 | * @brief wrapper around malloc/free | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | |||
27 | #include "platform.h" | ||
28 | #include "gnunet_util_lib.h" | ||
29 | #if HAVE_MALLOC_H | ||
30 | #include <malloc.h> | ||
31 | #endif | ||
32 | #if HAVE_MALLOC_MALLOC_H | ||
33 | #include <malloc/malloc.h> | ||
34 | #endif | ||
35 | |||
36 | #define LOG(kind, ...) \ | ||
37 | GNUNET_log_from (kind, "util-common-allocation", __VA_ARGS__) | ||
38 | |||
39 | #define LOG_STRERROR(kind, syscall) \ | ||
40 | GNUNET_log_from_strerror (kind, "util-common-allocation", syscall) | ||
41 | |||
42 | #ifndef INT_MAX | ||
43 | #define INT_MAX 0x7FFFFFFF | ||
44 | #endif | ||
45 | |||
46 | |||
47 | /** | ||
48 | * Allocate memory. Checks the return value, aborts if no more | ||
49 | * memory is available. | ||
50 | * | ||
51 | * @param size how many bytes of memory to allocate, do NOT use | ||
52 | * this function (or GNUNET_malloc()) to allocate more than several MB | ||
53 | * of memory, if you are possibly needing a very large chunk use | ||
54 | * #GNUNET_xmalloc_unchecked_() instead. | ||
55 | * @param filename where in the code was the call to GNUNET_malloc() | ||
56 | * @param linenumber where in the code was the call to GNUNET_malloc() | ||
57 | * @return pointer to size bytes of memory | ||
58 | */ | ||
59 | void * | ||
60 | GNUNET_xmalloc_ (size_t size, const char *filename, int linenumber) | ||
61 | { | ||
62 | void *ret; | ||
63 | |||
64 | /* As a security precaution, we generally do not allow very large | ||
65 | * allocations using the default 'GNUNET_malloc()' macro */ | ||
66 | GNUNET_assert_at (size <= GNUNET_MAX_MALLOC_CHECKED, filename, linenumber); | ||
67 | ret = GNUNET_xmalloc_unchecked_ (size, filename, linenumber); | ||
68 | if (NULL == ret) | ||
69 | { | ||
70 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "malloc"); | ||
71 | GNUNET_assert (0); | ||
72 | } | ||
73 | return ret; | ||
74 | } | ||
75 | |||
76 | |||
77 | /** | ||
78 | * Allocate memory for a two dimensional array in one block | ||
79 | * and set up pointers. Aborts if no more memory is available. | ||
80 | * Don't use GNUNET_xnew_array_2d_ directly. Use the | ||
81 | * #GNUNET_new_array_2d macro. | ||
82 | * The memory of the elements will be zero'ed out. | ||
83 | * | ||
84 | * @param n size of the first dimension | ||
85 | * @param m size of the second dimension | ||
86 | * @param elementSize size of a single element in bytes | ||
87 | * @param filename where is this call being made (for debugging) | ||
88 | * @param linenumber line where this call is being made (for debugging) | ||
89 | * @return allocated memory, never NULL | ||
90 | */ | ||
91 | void ** | ||
92 | GNUNET_xnew_array_2d_ (size_t n, | ||
93 | size_t m, | ||
94 | size_t elementSize, | ||
95 | const char *filename, | ||
96 | int linenumber) | ||
97 | { | ||
98 | /* use char pointer internally to avoid void pointer arithmetic warnings */ | ||
99 | char **ret = GNUNET_xmalloc_ (n * sizeof(void *) /* 1. dim header */ | ||
100 | + n * m * elementSize, /* element data */ | ||
101 | filename, | ||
102 | linenumber); | ||
103 | |||
104 | for (size_t i = 0; i < n; i++) | ||
105 | ret[i] = (char *) ret /* base address */ | ||
106 | + n * sizeof(void *) /* skip 1. dim header */ | ||
107 | + i * m * elementSize; /* skip to 2. dim row header */ | ||
108 | return (void **) ret; | ||
109 | } | ||
110 | |||
111 | |||
112 | /** | ||
113 | * Allocate memory for a three dimensional array in one block | ||
114 | * and set up pointers. Aborts if no more memory is available. | ||
115 | * Don't use GNUNET_xnew_array_3d_ directly. Use the | ||
116 | * #GNUNET_new_array_3d macro. | ||
117 | * The memory of the elements will be zero'ed out. | ||
118 | * | ||
119 | * @param n size of the first dimension | ||
120 | * @param m size of the second dimension | ||
121 | * @param o size of the third dimension | ||
122 | * @param elementSize size of a single element in bytes | ||
123 | * @param filename where is this call being made (for debugging) | ||
124 | * @param linenumber line where this call is being made (for debugging) | ||
125 | * @return allocated memory, never NULL | ||
126 | */ | ||
127 | void *** | ||
128 | GNUNET_xnew_array_3d_ (size_t n, | ||
129 | size_t m, | ||
130 | size_t o, | ||
131 | size_t elementSize, | ||
132 | const char *filename, | ||
133 | int linenumber) | ||
134 | { | ||
135 | /* use char pointer internally to avoid void pointer arithmetic warnings */ | ||
136 | char ***ret = GNUNET_xmalloc_ (n * sizeof(void **) /* 1. dim header */ | ||
137 | + n * m * sizeof(void *) /* 2. dim header */ | ||
138 | + n * m * o * elementSize, /* element data */ | ||
139 | filename, | ||
140 | linenumber); | ||
141 | |||
142 | for (size_t i = 0; i < n; i++) | ||
143 | { | ||
144 | /* need to cast to (char *) temporarily for byte level accuracy */ | ||
145 | ret[i] = (char **) ((char *) ret /* base address */ | ||
146 | + n * sizeof(void **) /* skip 1. dim header */ | ||
147 | + i * m * sizeof(void *)); /* skip to 2. dim header */ | ||
148 | for (size_t j = 0; j < m; j++) | ||
149 | ret[i][j] = (char *) ret /* base address */ | ||
150 | + n * sizeof(void **) /* skip 1. dim header */ | ||
151 | + n * m * sizeof(void *) /* skip 2. dim header */ | ||
152 | + i * m * o * elementSize /* skip to 2. dim part */ | ||
153 | + j * o * elementSize; /* skip to 3. dim row data */ | ||
154 | } | ||
155 | return (void ***) ret; | ||
156 | } | ||
157 | |||
158 | |||
159 | void * | ||
160 | GNUNET_xmemdup_ (const void *buf, | ||
161 | size_t size, | ||
162 | const char *filename, | ||
163 | int linenumber) | ||
164 | { | ||
165 | void *ret; | ||
166 | |||
167 | /* As a security precaution, we generally do not allow very large | ||
168 | * allocations here */ | ||
169 | GNUNET_assert_at (size <= GNUNET_MAX_MALLOC_CHECKED, filename, linenumber); | ||
170 | GNUNET_assert_at (size < INT_MAX, filename, linenumber); | ||
171 | ret = malloc (size); | ||
172 | if (ret == NULL) | ||
173 | { | ||
174 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "malloc"); | ||
175 | GNUNET_assert (0); | ||
176 | } | ||
177 | GNUNET_memcpy (ret, buf, size); | ||
178 | return ret; | ||
179 | } | ||
180 | |||
181 | |||
182 | /** | ||
183 | * Wrapper around malloc(). Allocates size bytes of memory. | ||
184 | * The memory will be zero'ed out. | ||
185 | * | ||
186 | * @param size the number of bytes to allocate | ||
187 | * @param filename where in the code was the call to GNUNET_malloc_unchecked() | ||
188 | * @param linenumber where in the code was the call to GNUNET_malloc_unchecked() | ||
189 | * @return pointer to size bytes of memory, NULL if we do not have enough memory | ||
190 | */ | ||
191 | void * | ||
192 | GNUNET_xmalloc_unchecked_ (size_t size, const char *filename, int linenumber) | ||
193 | { | ||
194 | void *result; | ||
195 | |||
196 | (void) filename; | ||
197 | (void) linenumber; | ||
198 | |||
199 | result = malloc (size); | ||
200 | if (NULL == result) | ||
201 | return NULL; | ||
202 | memset (result, 0, size); | ||
203 | |||
204 | return result; | ||
205 | } | ||
206 | |||
207 | |||
208 | /** | ||
209 | * Reallocate memory. Checks the return value, aborts if no more | ||
210 | * memory is available. | ||
211 | * The content of the intersection of the new and old size will be unchanged. | ||
212 | * | ||
213 | * @param ptr the pointer to reallocate | ||
214 | * @param n how many bytes of memory to allocate | ||
215 | * @param filename where in the code was the call to GNUNET_realloc() | ||
216 | * @param linenumber where in the code was the call to GNUNET_realloc() | ||
217 | * @return pointer to size bytes of memory | ||
218 | */ | ||
219 | void * | ||
220 | GNUNET_xrealloc_ (void *ptr, size_t n, const char *filename, int linenumber) | ||
221 | { | ||
222 | (void) filename; | ||
223 | (void) linenumber; | ||
224 | |||
225 | #if defined(M_SIZE) | ||
226 | #if ENABLE_POISONING | ||
227 | { | ||
228 | uint64_t *base = ptr; | ||
229 | size_t s = M_SIZE (ptr); | ||
230 | |||
231 | if (s > n) | ||
232 | { | ||
233 | const uint64_t baadfood = GNUNET_ntohll (0xBAADF00DBAADF00DLL); | ||
234 | char *cbase = ptr; | ||
235 | |||
236 | GNUNET_memcpy (&cbase[n], | ||
237 | &baadfood, | ||
238 | GNUNET_MIN (8 - (n % 8), | ||
239 | s - n)); | ||
240 | for (size_t i = 1 + (n + 7) / 8; i < s / 8; i++) | ||
241 | base[i] = baadfood; | ||
242 | GNUNET_memcpy (&base[s / 8], | ||
243 | &baadfood, | ||
244 | s % 8); | ||
245 | } | ||
246 | } | ||
247 | #endif | ||
248 | #endif | ||
249 | ptr = realloc (ptr, n); | ||
250 | if ((NULL == ptr) && (n > 0)) | ||
251 | { | ||
252 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "realloc"); | ||
253 | GNUNET_assert (0); | ||
254 | } | ||
255 | return ptr; | ||
256 | } | ||
257 | |||
258 | |||
259 | #if __BYTE_ORDER == __LITTLE_ENDIAN | ||
260 | #define BAADFOOD_STR "\x0D\xF0\xAD\xBA" | ||
261 | #endif | ||
262 | #if __BYTE_ORDER == __BIG_ENDIAN | ||
263 | #define BAADFOOD_STR "\xBA\xAD\xF0\x0D" | ||
264 | #endif | ||
265 | |||
266 | #if HAVE_MALLOC_NP_H | ||
267 | #include <malloc_np.h> | ||
268 | #endif | ||
269 | #if HAVE_MALLOC_USABLE_SIZE | ||
270 | #define M_SIZE(p) malloc_usable_size (p) | ||
271 | #elif HAVE_MALLOC_SIZE | ||
272 | #define M_SIZE(p) malloc_size (p) | ||
273 | #endif | ||
274 | |||
275 | /** | ||
276 | * Free memory. Merely a wrapper for the case that we | ||
277 | * want to keep track of allocations. | ||
278 | * | ||
279 | * @param ptr the pointer to free | ||
280 | * @param filename where in the code was the call to GNUNET_free() | ||
281 | * @param linenumber where in the code was the call to GNUNET_free() | ||
282 | */ | ||
283 | void | ||
284 | GNUNET_xfree_ (void *ptr, | ||
285 | const char *filename, | ||
286 | int linenumber) | ||
287 | { | ||
288 | if (NULL == ptr) | ||
289 | return; | ||
290 | #if defined(M_SIZE) | ||
291 | #if ENABLE_POISONING | ||
292 | { | ||
293 | const uint64_t baadfood = GNUNET_ntohll (0xBAADF00DBAADF00DLL); | ||
294 | uint64_t *base = ptr; | ||
295 | size_t s = M_SIZE (ptr); | ||
296 | |||
297 | for (size_t i = 0; i < s / 8; i++) | ||
298 | base[i] = baadfood; | ||
299 | GNUNET_memcpy (&base[s / 8], &baadfood, s % 8); | ||
300 | } | ||
301 | #endif | ||
302 | #endif | ||
303 | free (ptr); | ||
304 | } | ||
305 | |||
306 | |||
307 | /** | ||
308 | * Dup a string (same semantics as strdup). | ||
309 | * | ||
310 | * @param str the string to dup | ||
311 | * @param filename where in the code was the call to GNUNET_strdup() | ||
312 | * @param linenumber where in the code was the call to GNUNET_strdup() | ||
313 | * @return `strdup(@a str)` | ||
314 | */ | ||
315 | char * | ||
316 | GNUNET_xstrdup_ (const char *str, const char *filename, int linenumber) | ||
317 | { | ||
318 | char *res; | ||
319 | size_t slen; | ||
320 | |||
321 | GNUNET_assert_at (str != NULL, filename, linenumber); | ||
322 | slen = strlen (str) + 1; | ||
323 | res = GNUNET_xmalloc_ (slen, filename, linenumber); | ||
324 | GNUNET_memcpy (res, str, slen); | ||
325 | return res; | ||
326 | } | ||
327 | |||
328 | |||
329 | #if ! HAVE_STRNLEN | ||
330 | static size_t | ||
331 | strnlen (const char *s, size_t n) | ||
332 | { | ||
333 | const char *e; | ||
334 | |||
335 | e = memchr (s, '\0', n); | ||
336 | if (NULL == e) | ||
337 | return n; | ||
338 | return e - s; | ||
339 | } | ||
340 | |||
341 | |||
342 | #endif | ||
343 | |||
344 | |||
345 | /** | ||
346 | * Dup partially a string (same semantics as strndup). | ||
347 | * | ||
348 | * @param str the string to dup | ||
349 | * @param len the length of the string to dup | ||
350 | * @param filename where in the code was the call to GNUNET_strndup() | ||
351 | * @param linenumber where in the code was the call to GNUNET_strndup() | ||
352 | * @return `strndup(@a str,@a len)` | ||
353 | */ | ||
354 | char * | ||
355 | GNUNET_xstrndup_ (const char *str, | ||
356 | size_t len, | ||
357 | const char *filename, | ||
358 | int linenumber) | ||
359 | { | ||
360 | char *res; | ||
361 | |||
362 | if (0 == len) | ||
363 | return GNUNET_strdup (""); | ||
364 | GNUNET_assert_at (NULL != str, filename, linenumber); | ||
365 | len = strnlen (str, len); | ||
366 | res = GNUNET_xmalloc_ (len + 1, filename, linenumber); | ||
367 | GNUNET_memcpy (res, str, len); | ||
368 | /* res[len] = '\0'; 'malloc' zeros out anyway */ | ||
369 | return res; | ||
370 | } | ||
371 | |||
372 | |||
373 | /** | ||
374 | * Grow an array. Grows old by (*oldCount-newCount)*elementSize bytes | ||
375 | * and sets *oldCount to newCount. | ||
376 | * | ||
377 | * @param old address of the pointer to the array | ||
378 | * *old may be NULL | ||
379 | * @param elementSize the size of the elements of the array | ||
380 | * @param oldCount address of the number of elements in the *old array | ||
381 | * @param newCount number of elements in the new array, may be 0 | ||
382 | * @param filename where in the code was the call to GNUNET_array_grow() | ||
383 | * @param linenumber where in the code was the call to GNUNET_array_grow() | ||
384 | */ | ||
385 | void | ||
386 | GNUNET_xgrow_ (void **old, | ||
387 | size_t elementSize, | ||
388 | unsigned int *oldCount, | ||
389 | unsigned int newCount, | ||
390 | const char *filename, | ||
391 | int linenumber) | ||
392 | { | ||
393 | void *tmp; | ||
394 | size_t size; | ||
395 | |||
396 | GNUNET_assert_at (INT_MAX / elementSize > newCount, filename, linenumber); | ||
397 | size = newCount * elementSize; | ||
398 | if (0 == size) | ||
399 | { | ||
400 | tmp = NULL; | ||
401 | } | ||
402 | else | ||
403 | { | ||
404 | tmp = GNUNET_xmalloc_ (size, filename, linenumber); | ||
405 | if (NULL != *old) | ||
406 | { | ||
407 | GNUNET_memcpy (tmp, *old, elementSize * GNUNET_MIN (*oldCount, newCount)); | ||
408 | } | ||
409 | } | ||
410 | |||
411 | if (NULL != *old) | ||
412 | { | ||
413 | GNUNET_xfree_ (*old, filename, linenumber); | ||
414 | } | ||
415 | *old = tmp; | ||
416 | *oldCount = newCount; | ||
417 | } | ||
418 | |||
419 | |||
420 | /** | ||
421 | * Like asprintf(), just portable. | ||
422 | * | ||
423 | * @param buf set to a buffer of sufficient size (allocated, caller must free) | ||
424 | * @param format format string (see printf(), fprintf(), etc.) | ||
425 | * @param ... data for format string | ||
426 | * @return number of bytes in `*@a buf`, excluding 0-termination | ||
427 | */ | ||
428 | int | ||
429 | GNUNET_asprintf (char **buf, const char *format, ...) | ||
430 | { | ||
431 | int ret; | ||
432 | va_list args; | ||
433 | |||
434 | va_start (args, format); | ||
435 | ret = vsnprintf (NULL, 0, format, args); | ||
436 | va_end (args); | ||
437 | GNUNET_assert (ret >= 0); | ||
438 | *buf = GNUNET_malloc (ret + 1); | ||
439 | va_start (args, format); | ||
440 | ret = vsprintf (*buf, format, args); | ||
441 | va_end (args); | ||
442 | return ret; | ||
443 | } | ||
444 | |||
445 | |||
446 | /** | ||
447 | * Like snprintf(), just aborts if the buffer is of insufficient size. | ||
448 | * | ||
449 | * @param buf pointer to buffer that is written to | ||
450 | * @param size number of bytes in buf | ||
451 | * @param format format strings | ||
452 | * @param ... data for format string | ||
453 | * @return number of bytes written to buf or negative value on error | ||
454 | */ | ||
455 | int | ||
456 | GNUNET_snprintf (char *buf, size_t size, const char *format, ...) | ||
457 | { | ||
458 | int ret; | ||
459 | va_list args; | ||
460 | |||
461 | va_start (args, format); | ||
462 | ret = vsnprintf (buf, size, format, args); | ||
463 | va_end (args); | ||
464 | GNUNET_assert ((ret >= 0) && (((size_t) ret) < size)); | ||
465 | return ret; | ||
466 | } | ||
467 | |||
468 | |||
469 | struct GNUNET_MessageHeader * | ||
470 | GNUNET_copy_message (const struct GNUNET_MessageHeader *msg) | ||
471 | { | ||
472 | struct GNUNET_MessageHeader *ret; | ||
473 | uint16_t msize; | ||
474 | |||
475 | msize = ntohs (msg->size); | ||
476 | GNUNET_assert (msize >= sizeof(struct GNUNET_MessageHeader)); | ||
477 | ret = GNUNET_malloc (msize); | ||
478 | GNUNET_memcpy (ret, msg, msize); | ||
479 | return ret; | ||
480 | } | ||
481 | |||
482 | |||
483 | /** | ||
484 | * Check that memory in @a a is all zeros. @a a must be a pointer. | ||
485 | * | ||
486 | * @param a pointer to @a n bytes which should be tested for the | ||
487 | * entire memory being zero'ed out. | ||
488 | * @param n number of bytes in @a to be tested | ||
489 | * @return GNUNET_YES if a is zero, GNUNET_NO otherwise | ||
490 | */ | ||
491 | enum GNUNET_GenericReturnValue | ||
492 | GNUNET_is_zero_ (const void *a, | ||
493 | size_t n) | ||
494 | { | ||
495 | const char *b = a; | ||
496 | |||
497 | for (size_t i = 0; i < n; i++) | ||
498 | if (b[i]) | ||
499 | return GNUNET_NO; | ||
500 | return GNUNET_YES; | ||
501 | } | ||
502 | |||
503 | |||
504 | /* end of common_allocation.c */ | ||