summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2005-07-01 01:25:55 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 13:19:00 -0500
commit9cfe2d83f1103f83ace2ead46e31d7368a29f3c0 (patch)
tree2d6f7dc7cfc0813a4de7365737daa22947bc47f7
parent7134b64292a9e124d1280e61ef29810ac7e8c0b0 (diff)
downloadsamba-9cfe2d83f1103f83ace2ead46e31d7368a29f3c0.tar.gz
samba-9cfe2d83f1103f83ace2ead46e31d7368a29f3c0.tar.bz2
samba-9cfe2d83f1103f83ace2ead46e31d7368a29f3c0.zip
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)
-rw-r--r--source4/lib/talloc/talloc.c62
-rw-r--r--source4/lib/talloc/testsuite.c21
2 files changed, 67 insertions, 16 deletions
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();
}