Coverage Report

Created: 2025-08-28 06:16

/src/opensips/mem/q_malloc_dyn.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2019 OpenSIPS Solutions
3
 *
4
 * This file is part of opensips, a free SIP server.
5
 *
6
 * opensips is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 2 of the License, or
9
 * (at your option) any later version
10
 *
11
 * opensips 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
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
 */
20
21
/* for any questions on the complex ifdef logic, see mem/f_malloc_dyn.h */
22
23
/* returns 0 on success, -1 on error;
24
 * new_size < size & rounded-up already!*/
25
static inline
26
#if !defined INLINE_ALLOC && defined DBG_MALLOC
27
int qm_split_frag_dbg(struct qm_block *qm, struct qm_frag *f,
28
                      unsigned long new_size, const char *file,
29
                      const char *func, unsigned int line)
30
#elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC
31
int qm_split_frag(struct qm_block *qm, struct qm_frag *f,
32
                  unsigned long new_size)
33
#else
34
int qm_split_frag(struct qm_block *qm, struct qm_frag *f,
35
                  unsigned long new_size, const char *file, const char *func,
36
                  unsigned int line)
37
#endif
38
0
{
39
0
  unsigned long rest;
40
0
  struct qm_frag *n;
41
0
  struct qm_frag_end *end;
42
43
0
  rest=f->size-new_size;
44
#ifdef MEM_FRAG_AVOIDANCE
45
  if ((rest> (FRAG_OVERHEAD+Q_MALLOC_OPTIMIZE))||
46
    (rest>=(FRAG_OVERHEAD+new_size))){/* the residue fragm. is big enough*/
47
#else
48
0
  if (rest>(FRAG_OVERHEAD+MIN_FRAG_SIZE)){
49
0
#endif
50
0
    f->size=new_size;
51
    /*split the fragment*/
52
0
    end=FRAG_END(f);
53
0
    end->size=new_size;
54
0
    n=(struct qm_frag*)(end+1);
55
0
    n->size=rest-FRAG_OVERHEAD;
56
0
    FRAG_END(n)->size=n->size;
57
0
    FRAG_CLEAR_USED(n); /* never used */
58
0
#if defined(DBG_MALLOC) || defined(STATISTICS)
59
0
    qm->used-=FRAG_OVERHEAD;
60
0
#endif
61
#ifdef DBG_MALLOC
62
    end->check1=END_CHECK_PATTERN1;
63
    end->check2=END_CHECK_PATTERN2;
64
    /* frag created by malloc, mark it*/
65
    n->check=ST_CHECK_PATTERN;
66
    qm_dbg_clear(n);
67
    qm_dbg_fill(n, file, func, line);
68
#endif
69
    /* reinsert n in free list*/
70
0
    qm_insert_free(qm, n);
71
0
    return 0;
72
0
  }else{
73
      /* we cannot split this fragment any more */
74
0
    return -1;
75
0
  }
76
0
}
77
78
79
80
#if !defined INLINE_ALLOC && defined DBG_MALLOC
81
void *qm_malloc_dbg(struct qm_block *qm, unsigned long size,
82
          const char *file, const char *func, unsigned int line)
83
#elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC
84
void *qm_malloc(struct qm_block *qm, unsigned long size)
85
#else
86
void *qm_malloc(struct qm_block *qm, unsigned long size,
87
          const char *file, const char *func, unsigned int line)
88
#endif
89
0
{
90
0
  struct qm_frag *f;
91
0
  int hash;
92
93
#if defined DBG_MALLOC || defined Q_MALLOC_DYN
94
  unsigned int list_cntr;
95
  list_cntr = 0;
96
#endif
97
98
#if defined DBG_MALLOC
99
  LM_GEN1(memlog, "%s_malloc (%lu), called from %s: %s(%d)\n",
100
    qm->name, size, file, func, line);
101
#endif
102
103
  /*size must be a multiple of 8*/
104
0
  size=ROUNDUP(size);
105
0
  if (size>(qm->size-qm->real_used)) {
106
0
    LM_ERR(oom_errorf, qm->name, qm->size - qm->real_used, size,
107
0
        qm->name[0] == 'p' ? "M" : "m");
108
0
    pkg_threshold_check();
109
0
    return 0;
110
0
  }
111
112
  /*search for a suitable free frag*/
113
#if !defined INLINE_ALLOC && defined DBG_MALLOC
114
  if ((f=qm_find_free(qm, size, &hash, &list_cntr))!=0){
115
#elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC
116
0
  if ((f=qm_find_free(qm, size, &hash))!=0){
117
#else
118
  if ((f=qm_find_free(qm, size, &hash, &list_cntr))!=0){
119
#endif
120
    /* we found it!*/
121
    /*detach it from the free list*/
122
#ifdef DBG_MALLOC
123
    qm_debug_frag(qm, f);
124
#endif
125
0
    qm_detach_free(qm, f);
126
    /*mark it as "busy"*/
127
0
    f->u.is_free=0;
128
0
    qm->free_hash[hash].no--;
129
    /* we ignore split return */
130
131
    #if !defined INLINE_ALLOC && defined DBG_MALLOC
132
    qm_split_frag_dbg(qm, f, size, file, "qm_malloc frag", line);
133
    #elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC
134
    qm_split_frag(qm, f, size);
135
    #else
136
    qm_split_frag(qm, f, size, file, "qm_malloc frag", line);
137
    #endif
138
139
0
    if (qm->max_real_used<qm->real_used)
140
0
      qm->max_real_used=qm->real_used;
141
142
#ifdef DBG_MALLOC
143
    qm_dbg_fill(f, file, func, line);
144
    f->check=ST_CHECK_PATTERN;
145
    /*  FRAG_END(f)->check1=END_CHECK_PATTERN1;
146
      FRAG_END(f)->check2=END_CHECK_PATTERN2;*/
147
    LM_GEN1(memlog, "%s_malloc(%lu), returns address %p frag. %p "
148
      "(size=%lu) on %d -th hit\n",
149
       qm->name, size, (char*)f+sizeof(struct qm_frag), f, f->size, list_cntr );
150
#endif
151
152
0
    pkg_threshold_check();
153
0
    qm->fragments += 1;
154
0
    return (char*)f+sizeof(struct qm_frag);
155
0
  }
156
157
0
  LM_ERR(oom_errorf, qm->name, qm->size - qm->real_used, size,
158
0
      qm->name[0] == 'p' ? "M" : "m");
159
0
  pkg_threshold_check();
160
0
  return 0;
161
0
}
162
163
164
165
#if !defined INLINE_ALLOC && defined DBG_MALLOC
166
void qm_free_dbg(struct qm_block *qm, void *p,
167
                 const char *file, const char *func, unsigned int line)
168
#elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC
169
void qm_free(struct qm_block *qm, void *p)
170
#else
171
void qm_free(struct qm_block *qm, void *p,
172
             const char *file, const char *func, unsigned int line)
173
#endif
174
0
{
175
0
  struct qm_frag *f;
176
0
  struct qm_frag *prev;
177
0
  struct qm_frag *next;
178
0
  unsigned long size;
179
180
#ifdef DBG_MALLOC
181
  LM_GEN1(memlog, "%s_free(%p), called from %s: %s(%d)\n",
182
          qm->name, p, file, func, line);
183
#endif
184
0
  if (!p) {
185
0
    LM_GEN1(memlog, "free(NULL) called\n");
186
0
    return;
187
0
  }
188
#ifdef DBG_MALLOC
189
  if (p>(void*)qm->last_frag_end || p<(void*)qm->first_frag){
190
    LM_CRIT("bad pointer %p (out of memory block!) - aborting\n", p);
191
    abort();
192
  }
193
#endif
194
0
  f=QM_FRAG(p);
195
#ifdef DBG_MALLOC
196
  qm_debug_frag(qm, f);
197
  if (f->u.is_free){
198
    LM_CRIT("freeing already freed pointer,"
199
        " first free: %s: %s(%ld) - aborting\n",
200
        qm_dbg_coords(f));
201
    abort();
202
  }
203
  LM_GEN1( memlog, "freeing frag. %p alloc'ed from %s: %s(%ld)\n",
204
      f, qm_dbg_coords(f));
205
#endif
206
207
0
  size=f->size;
208
  /* join packets if possible*/
209
0
  prev=next=0;
210
0
  next=FRAG_NEXT(f);
211
0
  if (((char*)next < (char*)qm->last_frag_end) &&( next->u.is_free)){
212
    /* join */
213
#ifdef DBG_MALLOC
214
    qm_debug_frag(qm, next);
215
#endif
216
0
    qm_detach_free(qm, next);
217
0
    size+=next->size+FRAG_OVERHEAD;
218
0
#if defined(DBG_MALLOC) || defined(STATISTICS)
219
0
    qm->used+=FRAG_OVERHEAD;
220
0
#endif
221
0
    qm->free_hash[GET_HASH(next->size)].no--; /* FIXME slow */
222
0
  }
223
224
0
  if (f > qm->first_frag){
225
0
    prev=FRAG_PREV(f);
226
    /*  (struct qm_frag*)((char*)f - (struct qm_frag_end*)((char*)f-
227
                sizeof(struct qm_frag_end))->size);*/
228
#ifdef DBG_MALLOC
229
    qm_debug_frag(qm, prev);
230
#endif
231
0
    if (prev->u.is_free){
232
      /*join*/
233
0
      qm_detach_free(qm, prev);
234
0
      size+=prev->size+FRAG_OVERHEAD;
235
0
#if defined(DBG_MALLOC) || defined(STATISTICS)
236
0
      qm->used+=FRAG_OVERHEAD;
237
0
#endif
238
0
      qm->free_hash[GET_HASH(prev->size)].no--; /* FIXME slow */
239
0
      f=prev;
240
0
    }
241
0
  }
242
0
  f->size=size;
243
0
  FRAG_END(f)->size=f->size;
244
#ifdef DBG_MALLOC
245
  qm_dbg_fill(f, file, func, line);
246
#endif
247
0
  qm_insert_free(qm, f);
248
0
  qm->fragments -= 1;
249
0
  pkg_threshold_check();
250
0
}
251
252
253
254
#if !defined INLINE_ALLOC && defined DBG_MALLOC
255
void *qm_realloc_dbg(struct qm_block *qm, void *p, unsigned long size,
256
                     const char *file, const char *func, unsigned int line)
257
#elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC
258
void *qm_realloc(struct qm_block *qm, void *p, unsigned long size)
259
#else
260
void *qm_realloc(struct qm_block *qm, void *p, unsigned long size,
261
                 const char *file, const char *func, unsigned int line)
262
#endif
263
0
{
264
0
  struct qm_frag *f;
265
0
  unsigned long diff;
266
0
  unsigned long orig_size;
267
0
  struct qm_frag *n;
268
0
  void *ptr;
269
270
  #ifdef DBG_MALLOC
271
  LM_GEN1(memlog, "%s_realloc(%p, %lu->%lu), called from %s: %s(%d)\n",
272
      qm->name, p, p ? QM_FRAG(p)->size:0, size, file, func, line);
273
  if (p && (p > (void *)qm->last_frag_end || p < (void *)qm->first_frag)) {
274
    LM_CRIT("bad pointer %p (out of memory block!) - aborting\n", p);
275
    abort();
276
  }
277
  #endif
278
279
0
  if (size == 0) {
280
0
    if (p)
281
      #if !defined INLINE_ALLOC && defined DBG_MALLOC
282
      qm_free_dbg(qm, p, file, func, line);
283
      #elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC
284
0
      qm_free(qm, p);
285
      #else
286
      qm_free(qm, p, file, func, line);
287
      #endif
288
0
    pkg_threshold_check();
289
0
    return 0;
290
0
  }
291
292
0
  if (!p)
293
    #if !defined INLINE_ALLOC && defined DBG_MALLOC
294
    return qm_malloc_dbg(qm, size, file, func, line);
295
    #elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC
296
0
    return qm_malloc(qm, size);
297
    #else
298
    return qm_malloc(qm, size, file, func, line);
299
    #endif
300
301
0
  f = QM_FRAG(p);
302
303
  #ifdef DBG_MALLOC
304
  qm_debug_frag(qm, f);
305
  LM_GEN1( memlog, "realloc'ing frag %p alloc'ed from %s: %s(%ld)\n",
306
      f, qm_dbg_coords(f));
307
  if (f->u.is_free) {
308
    LM_CRIT("trying to realloc an already freed "
309
        "pointer %p , fragment %p -- aborting\n", p, f);
310
    abort();
311
  }
312
  #endif
313
314
  /* find first acceptable size */
315
0
  size=ROUNDUP(size);
316
0
  if (f->size > size) {
317
0
    orig_size=f->size;
318
    /* shrink */
319
    #ifdef DBG_MALLOC
320
    LM_GEN1(memlog,"shrinking from %lu to %lu\n", f->size, size);
321
    #endif
322
323
    #if !defined INLINE_ALLOC && defined DBG_MALLOC
324
    qm_split_frag_dbg(qm, f, size, file, "qm_realloc frag", line);
325
    #elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC
326
    qm_split_frag(qm, f, size);
327
    #else
328
    qm_split_frag(qm, f, size, file, "qm_realloc frag", line);
329
    #endif
330
331
0
  } else if (f->size < size) {
332
    /* grow */
333
    #ifdef DBG_MALLOC
334
    LM_GEN1( memlog, "growing from %lu to %lu\n", f->size, size);
335
    #endif
336
337
0
    orig_size=f->size;
338
0
    diff=size-f->size;
339
0
    n=FRAG_NEXT(f);
340
0
    if (((char*)n < (char*)qm->last_frag_end) &&
341
0
        (n->u.is_free)&&((n->size+FRAG_OVERHEAD)>=diff)){
342
      /* join  */
343
0
      qm_detach_free(qm, n);
344
0
      qm->free_hash[GET_HASH(n->size)].no--; /*FIXME: slow*/
345
0
      f->size+=n->size+FRAG_OVERHEAD;
346
0
      #if defined(DBG_MALLOC) || defined(STATISTICS)
347
0
      qm->used+=FRAG_OVERHEAD;
348
0
      #endif
349
0
      FRAG_END(f)->size=f->size;
350
      /* end checks should be ok */
351
      /* split it if necessary */
352
0
      if (f->size > size ) {
353
        #if !defined INLINE_ALLOC && defined DBG_MALLOC
354
        qm_split_frag_dbg(qm, f, size, file, "qm_realloc frag", line);
355
        #elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC
356
        qm_split_frag(qm, f, size);
357
        #else
358
        qm_split_frag(qm, f, size, file, "qm_realloc frag", line);
359
        #endif
360
0
      }
361
0
    } else {
362
      /* could not join => realloc */
363
      #if !defined INLINE_ALLOC && defined DBG_MALLOC
364
      ptr = qm_malloc_dbg(qm, size, file, func, line);
365
      #elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC
366
      ptr = qm_malloc(qm, size);
367
      #else
368
      ptr = qm_malloc(qm, size, file, func, line);
369
      #endif
370
371
0
      if (ptr) {
372
        /* copy, need by libssl */
373
0
        memcpy(ptr, p, orig_size);
374
375
        #if !defined INLINE_ALLOC && defined DBG_MALLOC
376
        qm_free_dbg(qm, p, file, func, line);
377
        #elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC
378
        qm_free(qm, p);
379
        #else
380
        qm_free(qm, p, file, func, line);
381
        #endif
382
0
      }
383
384
0
      p = ptr;
385
0
    }
386
0
  } else {
387
    /* do nothing */
388
    #ifdef DBG_MALLOC
389
    LM_GEN1(memlog,"doing nothing, same size: %lu - %lu\n", f->size, size);
390
    #endif
391
0
  }
392
393
  #ifdef DBG_MALLOC
394
  LM_GEN1(memlog,"returning %p\n", p);
395
  #endif
396
397
0
  pkg_threshold_check();
398
0
  return p;
399
0
}
400
401
#if !defined INLINE_ALLOC && defined DBG_MALLOC
402
void qm_status_dbg(struct qm_block *qm)
403
#else
404
void qm_status(struct qm_block *qm)
405
#endif
406
0
{
407
0
  struct qm_frag *f;
408
0
  int i,j;
409
0
  int h;
410
0
  int unused;
411
412
#ifdef DBG_MALLOC
413
  mem_dbg_htable_t allocd;
414
  struct mem_dbg_entry *it;
415
#endif
416
417
0
  LM_GEN1(memdump, "qm_status (%p):\n", qm);
418
0
  if (!qm) return;
419
420
0
#if defined(DBG_MALLOC) || defined(STATISTICS)
421
0
  LM_GEN1(memdump, " heap size= %lu\n", qm->size);
422
0
  LM_GEN1(memdump, " used= %lu, used+overhead=%lu, free=%lu\n",
423
0
      qm->used, qm->real_used, qm->size-qm->real_used);
424
0
  LM_GEN1(memdump, " max used (+overhead)= %lu\n", qm->max_real_used);
425
0
#endif
426
427
#ifdef DBG_MALLOC
428
  dbg_ht_init(allocd);
429
430
  for (f = qm->first_frag; f >= qm->first_frag &&
431
          (void *)f < (void *)qm->last_frag_end; f = FRAG_NEXT(f)) {
432
    if (!f->u.is_free && f->dbg[0].file)
433
      if (dbg_ht_update(allocd, qm_dbg_coords(f), f->size) < 0) {
434
        LM_ERR("Unable to update alloc'ed. memory summary\n");
435
        dbg_ht_free(allocd);
436
        return;
437
      }
438
  }
439
440
  if ((void *)f != (void *)qm->last_frag_end)
441
    LM_GEN1(memdump, "failed to walk through all fragments (%p %p %p)\n",
442
            f, qm->first_frag, qm->last_frag_end);
443
444
  LM_GEN1(memdump, " dumping summary of all alloc'ed. fragments:\n");
445
  LM_GEN1(memdump, "----------------------------------------------------\n");
446
  LM_GEN1(memdump, "total_bytes | num_allocations x [file: func, line]\n");
447
  LM_GEN1(memdump, "----------------------------------------------------\n");
448
  for(i=0; i < DBG_HASH_SIZE; i++) {
449
    it = allocd[i];
450
    while (it) {
451
      LM_GEN1(memdump, " %10lu : %lu x [%s: %s, line %lu]\n",
452
        it->size, it->no_fragments, it->file, it->func, it->line);
453
      it = it->next;
454
    }
455
  }
456
  LM_GEN1(memdump, "----------------------------------------------------\n");
457
458
  dbg_ht_free(allocd);
459
#endif
460
461
0
  LM_GEN1(memdump, " dumping free list stats :\n");
462
0
  for(h=0,i=0;h<QM_HASH_SIZE;h++){
463
0
    unused=0;
464
0
    for (f=qm->free_hash[h].head.u.nxt_free,j=0;
465
0
        f!=&(qm->free_hash[h].head); f=f->u.nxt_free, i++, j++){
466
0
        if (!FRAG_WAS_USED(f)){
467
0
          unused++;
468
#ifdef DBG_MALLOC
469
          LM_GEN1(memdump, "unused fragm.: hash = %3d, fragment %p,"
470
            " address %p size %lu, created from %s: %s(%lu)\n",
471
              h, f, (char*)f+sizeof(struct qm_frag), f->size,
472
            qm_dbg_coords(f));
473
#endif
474
0
        }
475
0
    }
476
477
0
    if (j) LM_GEN1(memdump, "hash= %3d. fragments no.: %5d, unused: %5d\n"
478
0
          "\t\t bucket size: %9lu - %9ld (first %9lu)\n",
479
0
          h, j, unused, UN_HASH(h),
480
0
          ((h<=Q_MALLOC_OPTIMIZE/QM_ROUNDTO)?1:2)*UN_HASH(h),
481
0
          qm->free_hash[h].head.u.nxt_free->size
482
0
        );
483
0
    if (j!=qm->free_hash[h].no){
484
0
      LM_CRIT("different free frag. count: %d!=%lu"
485
0
        " for hash %3d\n", j, qm->free_hash[h].no, h);
486
0
    }
487
488
0
  }
489
0
  LM_GEN1(memdump, "-----------------------------\n");
490
0
}
491
492
#define Q_MALLOC_DYN