Skip to content

Commit 7b9cc4e

Browse files
committed
mem: HG_MALLOC unit tests: exit drain and double free
A process leaving through dynamic_process_final_exit() must give its cells back (the arena's real_used returns to its baseline); a cell freed twice in a row and a large fragment freed twice are refused, counted and never handed out twice.
1 parent 29d029f commit 7b9cc4e

2 files changed

Lines changed: 103 additions & 1 deletion

File tree

‎mem/test/test_hg_malloc.c‎

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,104 @@ static void test_hg_grow(void)
331331
shm_free(p[i]);
332332
}
333333

334+
/*
335+
* A process leaving for good (auto-scaling down) must give back what it holds
336+
* in the shared arena: its cached cells and the unused rest of the chunks it
337+
* carved. The arena's real_used counts whole carved blocks, so it returns to
338+
* its baseline only if every block the child touched drained. The size is one
339+
* the idle processes of a test run do not use.
340+
*/
341+
#define HGT_EXIT_SIZE 20000
342+
#define HGT_EXIT_CELLS 100
343+
344+
static int hgt_child_exit(void *arg)
345+
{
346+
void *p[HGT_EXIT_CELLS];
347+
int i;
348+
349+
for (i = 0; i < HGT_EXIT_CELLS; i++) {
350+
p[i] = shm_malloc(HGT_EXIT_SIZE);
351+
if (!p[i]) {
352+
LM_ERR("shm_malloc(%d) failed\n", HGT_EXIT_SIZE);
353+
return -1;
354+
}
355+
}
356+
for (i = 0; i < HGT_EXIT_CELLS; i++)
357+
shm_free(p[i]);
358+
359+
/* report first: the exit below does not return */
360+
*hgt_verdict = HGT_PASS;
361+
dynamic_process_final_exit();
362+
return 0;
363+
}
364+
365+
static void test_hg_exit_returns_cells(void)
366+
{
367+
struct hg_block *hb = shm_block;
368+
unsigned long base = hb->real_used;
369+
int waited;
370+
371+
if (!hgt_in_child("hg exit", hgt_child_exit, NULL)) {
372+
ok(0, "a process leaving for good gives back its cells");
373+
return;
374+
}
375+
376+
/* the child gives its cells back right before it exits */
377+
for (waited = 0; hb->real_used > base && waited < HGT_WAIT_US;
378+
waited += HGT_STEP_US)
379+
usleep(HGT_STEP_US);
380+
381+
ok(hb->real_used <= base, "a process leaving for good gives back its "
382+
"cached cells and unused chunk tails (real_used %lu -> %lu KB)",
383+
base >> 10, hb->real_used >> 10);
384+
}
385+
386+
/*
387+
* A double free must be refused and counted, never hand the same memory to
388+
* two callers: the same cell freed twice in a row (it stays on the freeing
389+
* process's private list, which the shared pool's free map cannot see), and
390+
* a large-tier fragment freed twice.
391+
*/
392+
static int hgt_child_double_free(void *arg)
393+
{
394+
unsigned long size = (unsigned long)arg;
395+
void *p, *a, *b;
396+
397+
p = shm_malloc(size);
398+
if (!p)
399+
return -1;
400+
shm_free(p);
401+
shm_free(p);
402+
403+
a = shm_malloc(size);
404+
b = shm_malloc(size);
405+
if (!a || !b || a == b) {
406+
LM_ERR("after a double free of %p: two allocations returned %p "
407+
"and %p\n", p, a, b);
408+
return -1;
409+
}
410+
shm_free(a);
411+
shm_free(b);
412+
return 0;
413+
}
414+
415+
static void test_hg_double_free(void)
416+
{
417+
struct hg_block *hb = shm_block;
418+
unsigned long c0;
419+
420+
c0 = hb->corrupt[HG_C_DOUBLE_FREE];
421+
ok(hgt_in_child("hg double free", hgt_child_double_free,
422+
(void *)200UL) && hb->corrupt[HG_C_DOUBLE_FREE] == c0 + 1,
423+
"a cell freed twice in a row is refused and counted, not handed "
424+
"out twice");
425+
426+
c0 = hb->corrupt[HG_C_DOUBLE_FREE];
427+
ok(hgt_in_child("hg large double free", hgt_child_double_free,
428+
(void *)(200UL * 1024)) && hb->corrupt[HG_C_DOUBLE_FREE] == c0 + 1,
429+
"a large fragment freed twice is refused and counted");
430+
}
431+
334432
static unsigned long hgt_corruption(void)
335433
{
336434
unsigned long t = hg_corrupt_total((struct hg_block *)shm_block) +
@@ -359,10 +457,14 @@ void test_hg_malloc(void)
359457
test_hg_cross_process_shm();
360458
test_hg_large();
361459
test_hg_grow();
460+
test_hg_exit_returns_cells();
362461

363462
ok(hgt_corruption() == 0, "no corruption detected in the parent's view "
364463
"of the shm and pkg arenas");
365464

465+
/* last: these count a detection on purpose */
466+
test_hg_double_free();
467+
366468
shm_free((void *)hgt_verdict);
367469
}
368470

‎mem/test/test_hg_malloc.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
#define __TEST_HG_MALLOC_H__
2525

2626
/* children forked by test_hg_malloc(), one at a time (pt.c reserves them) */
27-
#define TEST_HG_MALLOC_PROCS 2
27+
#define TEST_HG_MALLOC_PROCS 5
2828

2929
/* non-zero when HG_MALLOC (or HG_MALLOC_DBG) serves both pkg and shm */
3030
int hg_ut_selected(void);

0 commit comments

Comments
 (0)