From 9cfe2d83f1103f83ace2ead46e31d7368a29f3c0 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Fri, 1 Jul 2005 01:25:55 +0000 Subject: r8032: added loop detection into talloc. Robert Collins found a way to make a memory loop with talloc_unlink(), so now we detect it and handle it (This used to be commit 563058e78b8c74e821fabf6a43fa861c1ad09944) --- source4/lib/talloc/talloc.c | 62 +++++++++++++++++++++++++++++++----------- source4/lib/talloc/testsuite.c | 21 ++++++++++++++ 2 files changed, 67 insertions(+), 16 deletions(-) (limited to 'source4/lib') diff --git a/source4/lib/talloc/talloc.c b/source4/lib/talloc/talloc.c index 3adf741870..3a068f4eaa 100644 --- a/source4/lib/talloc/talloc.c +++ b/source4/lib/talloc/talloc.c @@ -59,8 +59,9 @@ #define MAX_TALLOC_SIZE 0x10000000 -#define TALLOC_MAGIC 0xe814ec4f -#define TALLOC_MAGIC_FREE 0x7faebef3 +#define TALLOC_MAGIC 0xe814ec70 +#define TALLOC_FLAG_FREE 0x01 +#define TALLOC_FLAG_LOOP 0x02 #define TALLOC_MAGIC_REFERENCE ((const char *)1) /* by default we abort when given a bad pointer (such as when talloc_free() is called @@ -100,7 +101,7 @@ struct talloc_chunk { talloc_destructor_t destructor; const char *name; union { - unsigned magic; + unsigned flags; double align_dummy; } u; }; @@ -109,14 +110,12 @@ struct talloc_chunk { static struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr) { struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, ptr)-1; - if (tc->u.magic != TALLOC_MAGIC) { - if (tc->u.magic == TALLOC_MAGIC_FREE) { - TALLOC_ABORT("Bad talloc magic value - double free"); - } else { - TALLOC_ABORT("Bad talloc magic value - unknown value"); - } + if ((tc->u.flags & ~0xF) != TALLOC_MAGIC) { + TALLOC_ABORT("Bad talloc magic value - unknown value"); + } + if (tc->u.flags & TALLOC_FLAG_FREE) { + TALLOC_ABORT("Bad talloc magic value - double free"); } - return tc; } @@ -183,7 +182,7 @@ void *_talloc(const void *context, size_t size) if (tc == NULL) return NULL; tc->size = size; - tc->u.magic = TALLOC_MAGIC; + tc->u.flags = TALLOC_MAGIC; tc->destructor = NULL; tc->child = NULL; tc->name = NULL; @@ -537,6 +536,11 @@ int talloc_free(void *ptr) return -1; } + if (tc->u.flags & TALLOC_FLAG_LOOP) { + /* we have a free loop - stop looping */ + return 0; + } + if (tc->destructor) { talloc_destructor_t d = tc->destructor; if (d == (talloc_destructor_t)-1) { @@ -550,6 +554,8 @@ int talloc_free(void *ptr) tc->destructor = NULL; } + tc->u.flags |= TALLOC_FLAG_LOOP; + talloc_free_children(ptr); if (tc->parent) { @@ -562,7 +568,7 @@ int talloc_free(void *ptr) if (tc->next) tc->next->prev = tc->prev; } - tc->u.magic = TALLOC_MAGIC_FREE; + tc->u.flags |= TALLOC_FLAG_FREE; free(tc); return 0; @@ -602,7 +608,7 @@ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *n } /* by resetting magic we catch users of the old memory */ - tc->u.magic = TALLOC_MAGIC_FREE; + tc->u.flags |= TALLOC_FLAG_FREE; #if ALWAYS_REALLOC new_ptr = malloc(size + sizeof(*tc)); @@ -614,12 +620,12 @@ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *n new_ptr = realloc(tc, size + sizeof(*tc)); #endif if (!new_ptr) { - tc->u.magic = TALLOC_MAGIC; + tc->u.flags &= ~TALLOC_FLAG_FREE; return NULL; } tc = new_ptr; - tc->u.magic = TALLOC_MAGIC; + tc->u.flags &= ~TALLOC_FLAG_FREE; if (tc->parent) { tc->parent->child = new_ptr; } @@ -714,10 +720,19 @@ off_t talloc_total_size(const void *ptr) tc = talloc_chunk_from_ptr(ptr); + if (tc->u.flags & TALLOC_FLAG_LOOP) { + return 0; + } + + tc->u.flags |= TALLOC_FLAG_LOOP; + total = tc->size; for (c=tc->child;c;c=c->next) { total += talloc_total_size(c+1); } + + tc->u.flags &= ~TALLOC_FLAG_LOOP; + return total; } @@ -729,10 +744,19 @@ off_t talloc_total_blocks(const void *ptr) off_t total = 0; struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr); + if (tc->u.flags & TALLOC_FLAG_LOOP) { + return 0; + } + + tc->u.flags |= TALLOC_FLAG_LOOP; + total++; for (c=tc->child;c;c=c->next) { total += talloc_total_blocks(c+1); } + + tc->u.flags &= ~TALLOC_FLAG_LOOP; + return total; } @@ -758,6 +782,12 @@ void talloc_report_depth(const void *ptr, FILE *f, int depth) { struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr); + if (tc->u.flags & TALLOC_FLAG_LOOP) { + return; + } + + tc->u.flags |= TALLOC_FLAG_LOOP; + for (c=tc->child;c;c=c->next) { if (c->name == TALLOC_MAGIC_REFERENCE) { struct talloc_reference_handle *handle = (void *)(c+1); @@ -774,7 +804,7 @@ void talloc_report_depth(const void *ptr, FILE *f, int depth) talloc_report_depth(c+1, f, depth+1); } } - + tc->u.flags &= ~TALLOC_FLAG_LOOP; } /* diff --git a/source4/lib/talloc/testsuite.c b/source4/lib/talloc/testsuite.c index 60da1c3f87..422cb8fee5 100644 --- a/source4/lib/talloc/testsuite.c +++ b/source4/lib/talloc/testsuite.c @@ -818,6 +818,26 @@ static BOOL test_speed(void) } +BOOL test_lifeless(void) +{ + char *top = talloc_new(NULL); + char *parent, *child; + char *child_owner = talloc_new(NULL); + + printf("TESTING TALLOC_UNLINK LOOP\n"); + + parent = talloc_strdup(top, "parent"); + child = talloc_strdup(parent, "child"); + talloc_reference(child, parent); + talloc_reference(child_owner, child); + talloc_unlink(top, parent); + talloc_free(child); + talloc_report_full(top, stdout); + talloc_free(top); + return True; +} + + BOOL torture_local_talloc(void) { BOOL ret = True; @@ -834,6 +854,7 @@ BOOL torture_local_talloc(void) ret &= test_unref_reparent(); ret &= test_realloc_fn(); ret &= test_type(); + ret &= test_lifeless(); if (ret) { ret &= test_speed(); } -- cgit