1 /*@ Implementation of mem.h: allocation functions.
2 *@ TODO - flux memory: pool specific (like _auto_ and _lofi_), but normal
3 *@ TODO heap beside, which can be free()d in random order etc.
4 *@ TODO - dump,trace,etc. should take a log::domain object; if NIL: builtin.
5 *@ TODO (we need a logdom_detach() or so which ensures all resources are
6 *@ TODO initialized [better than asserting it is device-based dom]).
7 *@ TODO - port C++ memcache
8 *
9 * Copyright (c) 2012 - 2020 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
10 * SPDX-License-Identifier: ISC
11 *
12 * Permission to use, copy, modify, and/or distribute this software for any
13 * purpose with or without fee is hereby granted, provided that the above
14 * copyright notice and this permission notice appear in all copies.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
17 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
19 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
21 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
22 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 */
24 #undef su_FILE
25 #define su_FILE su_mem_alloc
26 #define su_SOURCE
27 #define su_SOURCE_MEM_ALLOC
28
29 #include "su/code.h"
30
31 #include <stdlib.h> /* TODO -> port C++ cache */
32
33 #include "su/mem.h"
34 #include "su/code-in.h"
35
36 #ifndef su_MEM_ALLOC_DEBUG
37 # define a_MEMA_DBG(X)
38 # define a_MEMA_HOPE_SIZE_ADD 0
39 #else
40 # define a_MEMA_DBG(X) X
41 # define a_MEMA_HOPE_SIZE (2 * 8 * sizeof(u8))
42 # define a_MEMA_HOPE_INC(P) (P) += 8 * sizeof(u8)
43 # define a_MEMA_HOPE_DEC(P) (P) -= 8 * sizeof(u8)
44 # define a_MEMA_HOPE_SIZE_ADD \
45 (a_MEMA_HOPE_SIZE + sizeof(struct a_mema_heap_chunk))
46
47 /* We use address-induced canary values, inspiration (but he did not invent)
48 * and primes from maxv@netbsd.org, src/sys/kern/subr_kmem.c */
49 # define a_MEMA_HOPE_LOWER(M,P) \
50 do{\
51 u64 __h__ = R(up,P);\
52 __h__ *= (S(u64,0x9E37FFFFu) << 32) | 0xFFFC0000u;\
53 __h__ >>= 56;\
54 (M) = S(u8,__h__);\
55 }while(0)
56
57 # define a_MEMA_HOPE_UPPER(M,P) \
58 do{\
59 u32 __i__;\
60 u64 __x__, __h__ = R(up,P);\
61 __h__ *= (S(u64,0x9E37FFFFu) << 32) | 0xFFFC0000u;\
62 for(__i__ = 56; __i__ != 0; __i__ -= 8)\
63 if((__x__ = (__h__ >> __i__)) != 0){\
64 (M) = S(u8,__x__);\
65 break;\
66 }\
67 if(__i__ == 0)\
68 (M) = 0xAAu;\
69 }while(0)
70
71 # define a_MEMA_HOPE_SET(T,C) \
72 do{\
73 union a_mema_ptr __xp;\
74 struct a_mema_chunk *__xc;\
75 __xp.map_vp = (C).map_vp;\
76 __xc = R(struct a_mema_chunk*,__xp.T - 1);\
77 a_MEMA_HOPE_INC((C).map_cp);\
78 a_MEMA_HOPE_LOWER(__xp.map_u8p[0], &__xp.map_u8p[0]);\
79 a_MEMA_HOPE_LOWER(__xp.map_u8p[1], &__xp.map_u8p[1]);\
80 a_MEMA_HOPE_LOWER(__xp.map_u8p[2], &__xp.map_u8p[2]);\
81 a_MEMA_HOPE_LOWER(__xp.map_u8p[3], &__xp.map_u8p[3]);\
82 a_MEMA_HOPE_LOWER(__xp.map_u8p[4], &__xp.map_u8p[4]);\
83 a_MEMA_HOPE_LOWER(__xp.map_u8p[5], &__xp.map_u8p[5]);\
84 a_MEMA_HOPE_LOWER(__xp.map_u8p[6], &__xp.map_u8p[6]);\
85 a_MEMA_HOPE_LOWER(__xp.map_u8p[7], &__xp.map_u8p[7]);\
86 a_MEMA_HOPE_INC(__xp.map_u8p) + __xc->mac_size - __xc->mac_user_off;\
87 a_MEMA_HOPE_UPPER(__xp.map_u8p[0], &__xp.map_u8p[0]);\
88 a_MEMA_HOPE_UPPER(__xp.map_u8p[1], &__xp.map_u8p[1]);\
89 a_MEMA_HOPE_UPPER(__xp.map_u8p[2], &__xp.map_u8p[2]);\
90 a_MEMA_HOPE_UPPER(__xp.map_u8p[3], &__xp.map_u8p[3]);\
91 a_MEMA_HOPE_UPPER(__xp.map_u8p[4], &__xp.map_u8p[4]);\
92 a_MEMA_HOPE_UPPER(__xp.map_u8p[5], &__xp.map_u8p[5]);\
93 a_MEMA_HOPE_UPPER(__xp.map_u8p[6], &__xp.map_u8p[6]);\
94 a_MEMA_HOPE_UPPER(__xp.map_u8p[7], &__xp.map_u8p[7]);\
95 }while(0)
96
97 # define a_MEMA_HOPE_GET_TRACE(T,C,BAD) \
98 do{\
99 a_MEMA_HOPE_INC((C).map_cp);\
100 a_MEMA_HOPE_GET(T, C, BAD);\
101 a_MEMA_HOPE_INC((C).map_cp);\
102 }while(0)
103
104 # define a_MEMA_HOPE_GET(T,C,BAD) \
105 do{\
106 union a_mema_ptr __xp;\
107 struct a_mema_chunk *__xc;\
108 u32 __i;\
109 u8 __m;\
110 __xp.map_vp = (C).map_vp;\
111 a_MEMA_HOPE_DEC(__xp.map_cp);\
112 (C).map_cp = __xp.map_cp;\
113 __xc = R(struct a_mema_chunk*,__xp.T - 1);\
114 (BAD) = FAL0;\
115 __i = 0;\
116 a_MEMA_HOPE_LOWER(__m, &__xp.map_u8p[0]);\
117 if(__xp.map_u8p[0] != __m) __i |= 1<<7;\
118 a_MEMA_HOPE_LOWER(__m, &__xp.map_u8p[1]);\
119 if(__xp.map_u8p[1] != __m) __i |= 1<<6;\
120 a_MEMA_HOPE_LOWER(__m, &__xp.map_u8p[2]);\
121 if(__xp.map_u8p[2] != __m) __i |= 1<<5;\
122 a_MEMA_HOPE_LOWER(__m, &__xp.map_u8p[3]);\
123 if(__xp.map_u8p[3] != __m) __i |= 1<<4;\
124 a_MEMA_HOPE_LOWER(__m, &__xp.map_u8p[4]);\
125 if(__xp.map_u8p[4] != __m) __i |= 1<<3;\
126 a_MEMA_HOPE_LOWER(__m, &__xp.map_u8p[5]);\
127 if(__xp.map_u8p[5] != __m) __i |= 1<<2;\
128 a_MEMA_HOPE_LOWER(__m, &__xp.map_u8p[6]);\
129 if(__xp.map_u8p[6] != __m) __i |= 1<<1;\
130 a_MEMA_HOPE_LOWER(__m, &__xp.map_u8p[7]);\
131 if(__xp.map_u8p[7] != __m) __i |= 1<<0;\
132 if(__i != 0){\
133 (BAD) = (__i >= (1<<3)) ? TRUM1 : TRU1;\
134 a_MEMA_HOPE_INC((C).map_cp);\
135 su_log_write(su_LOG_ALERT | su_LOG_F_CORE,\
136 "! SU memory: %p: corrupt lower canary: " \
137 "0x%02X: %s, line %" PRIu32 "\n",\
138 (C).map_cp, __i, su_DBG_LOC_ARGS_FILE, su_DBG_LOC_ARGS_LINE);\
139 a_MEMA_HOPE_DEC((C).map_cp);\
140 }\
141 a_MEMA_HOPE_INC(__xp.map_u8p) + __xc->mac_size - __xc->mac_user_off;\
142 __i = 0;\
143 a_MEMA_HOPE_UPPER(__m, &__xp.map_u8p[0]);\
144 if(__xp.map_u8p[0] != __m) __i |= 1<<0;\
145 a_MEMA_HOPE_UPPER(__m, &__xp.map_u8p[1]);\
146 if(__xp.map_u8p[1] != __m) __i |= 1<<1;\
147 a_MEMA_HOPE_UPPER(__m, &__xp.map_u8p[2]);\
148 if(__xp.map_u8p[2] != __m) __i |= 1<<2;\
149 a_MEMA_HOPE_UPPER(__m, &__xp.map_u8p[3]);\
150 if(__xp.map_u8p[3] != __m) __i |= 1<<3;\
151 a_MEMA_HOPE_UPPER(__m, &__xp.map_u8p[4]);\
152 if(__xp.map_u8p[4] != __m) __i |= 1<<4;\
153 a_MEMA_HOPE_UPPER(__m, &__xp.map_u8p[5]);\
154 if(__xp.map_u8p[5] != __m) __i |= 1<<5;\
155 a_MEMA_HOPE_UPPER(__m, &__xp.map_u8p[6]);\
156 if(__xp.map_u8p[6] != __m) __i |= 1<<6;\
157 a_MEMA_HOPE_UPPER(__m, &__xp.map_u8p[7]);\
158 if(__xp.map_u8p[7] != __m) __i |= 1<<7;\
159 if(__i != 0){\
160 (BAD) |= (__i >= (1<<3)) ? TRUM1 : TRU1;\
161 a_MEMA_HOPE_INC((C).map_cp);\
162 su_log_write(su_LOG_ALERT | su_LOG_F_CORE,\
163 "! SU memory: %p: corrupt upper canary: " \
164 "0x%02X: %s, line %" PRIu32 "\n",\
165 (C).map_cp, __i, su_DBG_LOC_ARGS_FILE, su_DBG_LOC_ARGS_LINE);\
166 a_MEMA_HOPE_DEC((C).map_cp);\
167 }\
168 if(BAD)\
169 su_log_write(su_LOG_ALERT | su_LOG_F_CORE,\
170 "! SU memory: ..canary last seen: %s, line %" PRIu32 "\n",\
171 __xc->mac_file, __xc->mac_line);\
172 }while(0)
173 #endif /* su_MEM_ALLOC_DEBUG */
174
175 #ifdef su_MEM_ALLOC_DEBUG
176 struct a_mema_chunk{
177 char const *mac_file;
178 u32 mac_line : 29;
179 u32 mac_isfree : 1;
180 u32 mac_mark : 2;
181 u32 mac_user_off; /* .mac_size-.mac_user_off: user size */
182 uz mac_size;
183 };
184 # define a_MEMA_MARK_TO_STORE(X) \
185 ((S(u32,X) >> su__MEM_ALLOC_MARK_SHIFT) & su__MEM_ALLOC_MARK_MASK)
186 # define a_MEMA_STORE_TO_MARK(MACP) \
187 ((MACP)->mac_mark << su__MEM_ALLOC_MARK_SHIFT)
188
189 /* The heap memory mem_free() may become delayed to detect double frees */
190 struct a_mema_heap_chunk{
191 struct a_mema_chunk mahc_super;
192 struct a_mema_heap_chunk *mahc_prev;
193 struct a_mema_heap_chunk *mahc_next;
194 };
195
196 struct a_mema_stats{
197 u64 mas_cnt_all;
198 u64 mas_cnt_curr;
199 u64 mas_cnt_max;
200 u64 mas_mem_all;
201 u64 mas_mem_curr;
202 u64 mas_mem_max;
203 };
204 #endif /* su_MEM_ALLOC_DEBUG */
205
206 union a_mema_ptr{
207 void *map_vp;
208 char *map_cp;
209 u8 *map_u8p;
210 #ifdef su_MEM_ALLOC_DEBUG
211 struct a_mema_chunk *map_c;
212 struct a_mema_heap_chunk *map_hc;
213 #endif
214 };
215
216 #ifdef su_MEM_ALLOC_DEBUG
217 static char const * const a_mema_mark_names[] = {
218 # ifdef su_USECASE_MX
219 "heap", "auto", "auto-huge", "lofi"
220 # else
221 "zero/0", "one/1", "two/2", "three/3"
222 # endif
223 };
224 CTAV(a_MEMA_MARK_TO_STORE(su_MEM_ALLOC_MARK_0) == 0);
225 CTAV(a_MEMA_MARK_TO_STORE(su_MEM_ALLOC_MARK_1) == 1);
226 CTAV(a_MEMA_MARK_TO_STORE(su_MEM_ALLOC_MARK_2) == 2);
227 CTAV(a_MEMA_MARK_TO_STORE(su_MEM_ALLOC_MARK_3) == 3);
228 #endif
229
230 static uz a_mema_conf /*= su_MEM_CONF_NONE*/;
231 CTAV(su_MEM_CONF_NONE == 0);
232
233 #ifdef su_MEM_ALLOC_DEBUG
234 static struct a_mema_heap_chunk *a_mema_heap_list;
235 static struct a_mema_heap_chunk *a_mema_free_list;
236
237 static struct a_mema_stats a_mema_stats[su__MEM_ALLOC_MARK_MAX + 1];
238 #endif
239
240 #ifdef su_MEM_ALLOC_DEBUG
241 /* */
242 static void a_mema_release_free(void);
243 #endif
244
245 #ifdef su_MEM_ALLOC_DEBUG
246 static void
a_mema_release_free(void)247 a_mema_release_free(void){
248 uz c, s;
249 union a_mema_ptr p;
250 NYD2_IN;
251
252 if((p.map_hc = a_mema_free_list) != NIL){
253 a_mema_free_list = NIL;
254 c = s = 0;
255
256 for(; p.map_hc != NIL;){
257 void *vp;
258
259 vp = p.map_hc;
260 ++c;
261 s += p.map_c->mac_size;
262 p.map_hc = p.map_hc->mahc_next;
263 free(vp);
264 }
265
266 su_log_write(su_LOG_INFO | su_LOG_F_CORE,
267 "su_mem_set_conf(LINGER_FREE_RELEASE): freed %" PRIuZ
268 " chunks / %" PRIuZ " bytes\n",
269 c, s);
270 }
271 NYD2_OU;
272 }
273 #endif /* su_MEM_ALLOC_DEBUG */
274
275 #ifdef su_MEM_ALLOC_DEBUG
276 boole
su__mem_get_can_book(uz size,uz no)277 su__mem_get_can_book(uz size, uz no){
278 boole rv;
279 NYD2_IN;
280
281 rv = FAL0;
282
283 if(UZ_MAX - a_MEMA_HOPE_SIZE_ADD < size)
284 goto jleave;
285 size += a_MEMA_HOPE_SIZE_ADD;
286
287 if(UZ_MAX / no <= size)
288 goto jleave;
289
290 rv = TRU1;
291 jleave:
292 NYD2_OU;
293 return rv;
294 }
295
296 boole
su__mem_check(su_DBG_LOC_ARGS_DECL_SOLE)297 su__mem_check(su_DBG_LOC_ARGS_DECL_SOLE){
298 union a_mema_ptr p, xp;
299 boole anybad, isbad;
300 NYD2_IN;
301
302 anybad = FAL0;
303
304 for(p.map_hc = a_mema_heap_list; p.map_hc != NIL;
305 p.map_hc = p.map_hc->mahc_next){
306 xp = p;
307 ++xp.map_hc;
308 a_MEMA_HOPE_GET_TRACE(map_hc, xp, isbad);
309 if(isbad){
310 anybad |= isbad;
311 su_log_write(su_LOG_ALERT | su_LOG_F_CORE,
312 "! SU memory: CANARY ERROR (heap): %p (%" PRIuZ
313 " bytes): %s, line %" PRIu32 "\n",
314 xp.map_vp, (p.map_c->mac_size - p.map_c->mac_user_off),
315 p.map_c->mac_file, p.map_c->mac_line);
316 }
317 }
318
319 for(p.map_hc = a_mema_free_list; p.map_hc != NIL;
320 p.map_hc = p.map_hc->mahc_next){
321 xp = p;
322 ++xp.map_hc;
323 a_MEMA_HOPE_GET_TRACE(map_hc, xp, isbad);
324 if(isbad){
325 anybad |= isbad;
326 su_log_write(su_LOG_ALERT | su_LOG_F_CORE,
327 "! SU memory: CANARY ERROR (free list): %p (%" PRIuZ
328 " bytes): %s, line %" PRIu32 "\n",
329 xp.map_vp, (p.map_c->mac_size - p.map_c->mac_user_off),
330 p.map_c->mac_file, p.map_c->mac_line);
331 }
332 }
333
334 if(anybad)
335 su_log_write(((a_mema_conf & su_MEM_CONF_ON_ERROR_EMERG)
336 ? su_LOG_EMERG : su_LOG_CRIT) | su_LOG_F_CORE,
337 "SU memory check: errors encountered\n");
338
339 NYD2_OU;
340 return anybad;
341 }
342
343 boole
su__mem_trace(su_DBG_LOC_ARGS_DECL_SOLE)344 su__mem_trace(su_DBG_LOC_ARGS_DECL_SOLE){
345 union a_mema_ptr p, xp;
346 u32 mark;
347 boole anybad, isbad;
348 NYD2_IN;
349
350 anybad = FAL0;
351
352 for(mark = su__MEM_ALLOC_MARK_MAX;; --mark){
353 struct a_mema_stats const *masp;
354
355 masp = &a_mema_stats[mark];
356
357 su_log_write(su_LOG_INFO | su_LOG_F_CORE,
358 "MARK \"%s\" MEMORY:\n"
359 " Count cur/peek/all: %7" PRIu64 "/%7" PRIu64 "/%10" PRIu64 "\n"
360 " Memory cur/peek/all: %7" PRIu64 "/%7" PRIu64 "/%10" PRIu64 "\n\n",
361 a_mema_mark_names[mark],
362 masp->mas_cnt_curr, masp->mas_cnt_max, masp->mas_cnt_all,
363 masp->mas_mem_curr, masp->mas_mem_max, masp->mas_mem_all);
364
365 for(p.map_hc = a_mema_heap_list; p.map_hc != NIL;
366 p.map_hc = p.map_hc->mahc_next){
367 if(p.map_c->mac_mark != mark)
368 continue;
369 xp = p;
370 ++xp.map_hc;
371 a_MEMA_HOPE_GET_TRACE(map_hc, xp, isbad);
372 anybad |= isbad;
373 su_log_write((isbad ? su_LOG_ALERT : su_LOG_INFO) | su_LOG_F_CORE,
374 " %s%p (%" PRIuZ " bytes): %s, line %" PRIu32 "\n",
375 (isbad ? "! SU memory: CANARY ERROR: " : ""), xp.map_vp,
376 p.map_c->mac_size - p.map_c->mac_user_off,
377 p.map_c->mac_file, p.map_c->mac_line);
378 }
379
380 if(mark == su_MEM_ALLOC_MARK_0)
381 break;
382 }
383
384 if(a_mema_free_list != NIL){
385 su_log_write(su_LOG_INFO | su_LOG_F_CORE,
386 "Freed memory lingering for release:\n");
387
388 for(p.map_hc = a_mema_free_list; p.map_hc != NIL;
389 p.map_hc = p.map_hc->mahc_next){
390 xp = p;
391 ++xp.map_hc;
392 a_MEMA_HOPE_GET_TRACE(map_hc, xp, isbad);
393 anybad |= isbad;
394 su_log_write((isbad ? su_LOG_ALERT : su_LOG_INFO) | su_LOG_F_CORE,
395 " %s%p (%" PRIuZ " bytes): %s, line %" PRIu32 "\n",
396 (isbad ? "! SU memory: CANARY ERROR: " : ""), xp.map_vp,
397 p.map_c->mac_size - p.map_c->mac_user_off,
398 p.map_c->mac_file, p.map_c->mac_line);
399 }
400 }
401
402 NYD2_OU;
403 return anybad;
404 }
405 #endif /* su_MEM_ALLOC_DEBUG */
406
407 void *
su_mem_allocate(uz size,uz no,u32 maf su_DBG_LOC_ARGS_DECL)408 su_mem_allocate(uz size, uz no, u32 maf su_DBG_LOC_ARGS_DECL){
409 #ifdef su_MEM_ALLOC_DEBUG
410 u32 mark;
411 union a_mema_ptr p;
412 uz user_sz, user_no;
413 #endif
414 void *rv;
415 NYD_IN;
416 su_DBG_LOC_ARGS_UNUSED();
417
418 a_MEMA_DBG( user_sz = size su_COMMA user_no = no; )
419 if(UNLIKELY(size == 0))
420 size = 1;
421 if(UNLIKELY(no == 0))
422 no = 1;
423 maf &= su__MEM_ALLOC_USER_MASK;
424
425 rv = NIL;
426
427 if(a_MEMA_DBG( UZ_MAX - a_MEMA_HOPE_SIZE_ADD > size && )
428 LIKELY(((maf & su_MEM_ALLOC_32BIT_OVERFLOW) ? U32_MAX :
429 ((maf & su_MEM_ALLOC_31BIT_OVERFLOW) ? U32_MAX >> 1 : UZ_MAX))
430 / no > size + a_MEMA_HOPE_SIZE_ADD)){
431 size *= no;
432 #if !defined su_MEM_ALLOC_DEBUG && !defined su_HAVE_MEM_CANARIES_DISABLE
433 if(size < su_MEM_ALLOC_MIN)
434 size = su_MEM_ALLOC_MIN;
435 #endif
436 #ifdef su_MEM_ALLOC_DEBUG
437 size += a_MEMA_HOPE_SIZE_ADD;
438 user_sz *= user_no;
439 #endif
440
441 if(LIKELY((rv = malloc(size)) != NIL)){
442 /* XXX Of course this may run on odd ranges, but once upon a time
443 * XXX i will port my C++ cache and then we're fine again (it will not
444 * XXX even be handled in here) */
445 if(maf & su_MEM_ALLOC_CLEAR)
446 su_mem_set(rv, 0, size);
447 #ifdef su_MEM_ALLOC_DEBUG
448 else
449 su_mem_set(rv, 0xAA, size);
450 p.map_vp = rv;
451
452 p.map_hc->mahc_prev = NIL;
453 if((p.map_hc->mahc_next = a_mema_heap_list) != NIL)
454 a_mema_heap_list->mahc_prev = p.map_hc;
455 p.map_c->mac_file = su_DBG_LOC_ARGS_FILE;
456 p.map_c->mac_line = su_DBG_LOC_ARGS_LINE;
457 p.map_c->mac_isfree = FAL0;
458 p.map_c->mac_mark = mark = a_MEMA_MARK_TO_STORE(maf);
459 ASSERT(size - user_sz <= S32_MAX);
460 p.map_c->mac_user_off = S(u32,size - user_sz);
461 p.map_c->mac_size = size;
462 a_mema_heap_list = p.map_hc++;
463
464 a_MEMA_HOPE_SET(map_hc, p);
465 rv = p.map_vp;
466
467 ++a_mema_stats[mark].mas_cnt_all;
468 ++a_mema_stats[mark].mas_cnt_curr;
469 a_mema_stats[mark].mas_cnt_max = MAX(
470 a_mema_stats[mark].mas_cnt_max,
471 a_mema_stats[mark].mas_cnt_curr);
472 a_mema_stats[mark].mas_mem_all += user_sz;
473 a_mema_stats[mark].mas_mem_curr += user_sz;
474 a_mema_stats[mark].mas_mem_max = MAX(
475 a_mema_stats[mark].mas_mem_max,
476 a_mema_stats[mark].mas_mem_curr);
477 #endif /* su_MEM_ALLOC_DEBUG */
478 }else
479 su_state_err(su_STATE_ERR_NOMEM, maf,
480 _("SU memory: allocation request"));
481 }else
482 su_state_err(su_STATE_ERR_OVERFLOW, maf,
483 _("SU memory: allocation request"));
484 NYD_OU;
485 return rv;
486 }
487
488 void *
su_mem_reallocate(void * ovp,uz size,uz no,u32 maf su_DBG_LOC_ARGS_DECL)489 su_mem_reallocate(void *ovp, uz size, uz no, u32 maf su_DBG_LOC_ARGS_DECL){
490 #ifdef su_MEM_ALLOC_DEBUG
491 u32 mark;
492 union a_mema_ptr p;
493 void *origovp;
494 uz user_sz, user_no, orig_sz;
495 #endif
496 void *rv;
497 NYD_IN;
498 su_DBG_LOC_ARGS_UNUSED();
499
500 a_MEMA_DBG( user_sz = size su_COMMA user_no = no su_COMMA orig_sz = 0; )
501 if(UNLIKELY(size == 0))
502 size = 1;
503 if(UNLIKELY(no == 0))
504 no = 1;
505 maf &= su__MEM_ALLOC_USER_MASK;
506
507 rv = NIL;
508
509 /* In the debug case we always allocate a new buffer */
510 #ifdef su_MEM_ALLOC_DEBUG
511 if((p.map_vp = origovp = ovp) != NIL){
512 boole isbad;
513
514 ovp = NIL;
515 a_MEMA_HOPE_GET(map_hc, p, isbad);
516 --p.map_hc;
517
518 if(!p.map_c->mac_isfree)
519 orig_sz = p.map_c->mac_size - p.map_c->mac_user_off;
520 else if(isbad == TRUM1){
521 su_log_write(su_LOG_ALERT | su_LOG_F_CORE,
522 "SU memory: reallocation: pointer corrupted! At %s, line %" PRIu32
523 "\n\tLast seen: %s, line %" PRIu32 "\n"
524 su_DBG_LOC_ARGS_USE, p.map_c->mac_file, p.map_c->mac_line);
525 su_state_err(su_STATE_ERR_NOMEM, maf,
526 _("SU memory: reallocation of corrupted pointer"));
527 goto su_NYD_OU_LABEL;
528 }else{
529 su_log_write(su_LOG_ALERT | su_LOG_F_CORE,
530 "SU memory: reallocation: pointer freed! At %s, line %" PRIu32
531 "\n\tLast seen: %s, line %" PRIu32 "\n"
532 su_DBG_LOC_ARGS_USE, p.map_c->mac_file, p.map_c->mac_line);
533 su_state_err(su_STATE_ERR_NOMEM, maf,
534 _("SU memory: reallocation of a freed pointer"));
535 goto su_NYD_OU_LABEL;
536 }
537 }
538 #endif /* su_MEM_ALLOC_DEBUG */
539
540 if(a_MEMA_DBG( UZ_MAX - a_MEMA_HOPE_SIZE_ADD > size && )
541 LIKELY(((maf & su_MEM_ALLOC_32BIT_OVERFLOW) ? U32_MAX :
542 ((maf & su_MEM_ALLOC_31BIT_OVERFLOW) ? U32_MAX >> 1 : UZ_MAX))
543 / no > size + a_MEMA_HOPE_SIZE_ADD)){
544 size *= no;
545 #if !defined su_MEM_ALLOC_DEBUG && !defined su_HAVE_MEM_CANARIES_DISABLE
546 if(size < su_MEM_ALLOC_MIN)
547 size = su_MEM_ALLOC_MIN;
548 #endif
549 size *= no;
550 #ifdef su_MEM_ALLOC_DEBUG
551 size += a_MEMA_HOPE_SIZE_ADD;
552 user_sz *= user_no;
553 #endif
554
555 if(UNLIKELY((rv = realloc(ovp, size)) == NIL))
556 su_state_err(su_STATE_ERR_NOMEM, maf,
557 _("SU memory: reallocation request"));
558 #ifdef su_MEM_ALLOC_DEBUG
559 else{
560 p.map_vp = rv;
561
562 p.map_hc->mahc_prev = NIL;
563 if((p.map_hc->mahc_next = a_mema_heap_list) != NIL)
564 a_mema_heap_list->mahc_prev = p.map_hc;
565 p.map_c->mac_file = su_DBG_LOC_ARGS_FILE;
566 p.map_c->mac_line = su_DBG_LOC_ARGS_LINE;
567 p.map_c->mac_isfree = FAL0;
568 p.map_c->mac_mark = mark = a_MEMA_MARK_TO_STORE(maf);
569 ASSERT(size - user_sz <= S32_MAX);
570 p.map_c->mac_user_off = S(u32,size - user_sz);
571 p.map_c->mac_size = size;
572 size -= p.map_c->mac_user_off; /* Real user size for potential copy */
573 a_mema_heap_list = p.map_hc++;
574
575 a_MEMA_HOPE_SET(map_hc, p);
576 rv = p.map_vp;
577
578 ++a_mema_stats[mark].mas_cnt_all;
579 ++a_mema_stats[mark].mas_cnt_curr;
580 a_mema_stats[mark].mas_cnt_max = MAX(
581 a_mema_stats[mark].mas_cnt_max,
582 a_mema_stats[mark].mas_cnt_curr);
583 a_mema_stats[mark].mas_mem_all += user_sz;
584 a_mema_stats[mark].mas_mem_curr += user_sz;
585 a_mema_stats[mark].mas_mem_max = MAX(
586 a_mema_stats[mark].mas_mem_max,
587 a_mema_stats[mark].mas_mem_curr);
588
589 if(origovp != NIL){
590 su_mem_copy(rv, origovp, MIN(orig_sz, size));
591 su_mem_free(origovp su_DBG_LOC_ARGS_USE);
592 }
593 }
594 #endif /* su_MEM_ALLOC_DEBUG */
595 }else
596 su_state_err(su_STATE_ERR_OVERFLOW, maf,
597 _("SU memory: reallocation request"));
598 NYD_OU;
599 return rv;
600 }
601
602 void
su_mem_free(void * ovp su_DBG_LOC_ARGS_DECL)603 su_mem_free(void *ovp su_DBG_LOC_ARGS_DECL){
604 NYD_IN;
605 su_DBG_LOC_ARGS_UNUSED();
606
607 if(LIKELY(ovp != NIL)){
608 #ifdef su_MEM_ALLOC_DEBUG
609 u32 mark;
610 uz orig_sz;
611 union a_mema_ptr p;
612 boole isbad;
613
614 p.map_vp = ovp;
615 a_MEMA_HOPE_GET(map_hc, p, isbad);
616 --p.map_hc;
617
618 if(isbad == TRUM1){
619 su_log_write(su_LOG_ALERT | su_LOG_F_CORE,
620 "SU memory: free of corrupted pointer at %s, line %" PRIu32 "\n"
621 "\tLast seen: %s, line %" PRIu32 "\n"
622 su_DBG_LOC_ARGS_USE, p.map_c->mac_file, p.map_c->mac_line);
623 goto su_NYD_OU_LABEL;
624 }else if(p.map_c->mac_isfree){
625 su_log_write(su_LOG_ALERT | su_LOG_F_CORE,
626 "SU memory: double-free avoided at %s, line %" PRIu32 "\n"
627 "\tLast seen: %s, line %" PRIu32 "\n"
628 su_DBG_LOC_ARGS_USE, p.map_c->mac_file, p.map_c->mac_line);
629 goto su_NYD_OU_LABEL;
630 }
631
632 orig_sz = p.map_c->mac_size - p.map_c->mac_user_off;
633 su_mem_set(ovp, 0xBA, orig_sz);
634 ovp = p.map_vp;
635
636 p.map_c->mac_file = su_DBG_LOC_ARGS_FILE;
637 p.map_c->mac_line = su_DBG_LOC_ARGS_LINE;
638 p.map_c->mac_isfree = TRU1;
639 if(p.map_hc == a_mema_heap_list){
640 if((a_mema_heap_list = p.map_hc->mahc_next) != NIL)
641 a_mema_heap_list->mahc_prev = NIL;
642 }else
643 p.map_hc->mahc_prev->mahc_next = p.map_hc->mahc_next;
644 if(p.map_hc->mahc_next != NIL)
645 p.map_hc->mahc_next->mahc_prev = p.map_hc->mahc_prev;
646
647 mark = p.map_c->mac_mark;
648 --a_mema_stats[mark].mas_cnt_curr;
649 a_mema_stats[mark].mas_mem_curr -= orig_sz;
650
651 if(a_mema_conf & su_MEM_CONF_LINGER_FREE){
652 p.map_hc->mahc_next = a_mema_free_list;
653 a_mema_free_list = p.map_hc;
654 }else
655 #endif /* su_MEM_ALLOC_DEBUG */
656 free(ovp);
657 }
658 #ifdef su_MEM_ALLOC_DEBUG
659 else
660 su_log_write(su_LOG_DEBUG,
661 "SU memory: free(NIL) from %s, line %" PRIu32 "\n"
662 su_DBG_LOC_ARGS_USE);
663 #endif
664 NYD_OU;
665 }
666
667 void
su_mem_set_conf(u32 mco,uz val)668 su_mem_set_conf(u32 mco, uz val){
669 uz rmco;
670 NYD_IN;
671
672 rmco = S(uz,mco);
673 ASSERT_NYD(rmco <= su__MEM_CONF_MAX);
674
675 if((rmco & su_MEM_CONF_LINGER_FREE_RELEASE) ||
676 (!val && (rmco & su_MEM_CONF_LINGER_FREE))){
677 rmco &= ~su_MEM_CONF_LINGER_FREE_RELEASE;
678 #ifdef su_MEM_ALLOC_DEBUG
679 su_mem_check();
680 a_mema_release_free();
681 #endif
682 }
683
684 /* xxx !MEM_DEBUG does not test whether mem_conf_option is available */
685 if(rmco != 0){
686 if(val != FAL0)
687 a_mema_conf |= rmco;
688 else
689 a_mema_conf &= ~rmco;
690 }
691 NYD_OU;
692 }
693
694 #include "su/code-ou.h"
695 /* s-it-mode */
696