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