inspired by http://swapped.cc/halloc/
*/
-#ifdef _SAMBA_BUILD_
-#include "version.h"
-#if (SAMBA_VERSION_MAJOR<4)
-#include "includes.h"
-/* This is to circumvent SAMBA3's paranoid malloc checker. Here in this file
- * we trust ourselves... */
-#ifdef malloc
-#undef malloc
+#include "replace.h"
+#include "talloc.h"
+
+#ifdef TALLOC_BUILD_VERSION_MAJOR
+#if (TALLOC_VERSION_MAJOR != TALLOC_BUILD_VERSION_MAJOR)
+#error "TALLOC_VERSION_MAJOR != TALLOC_BUILD_VERSION_MAJOR"
#endif
-#ifdef realloc
-#undef realloc
#endif
-#define _TALLOC_SAMBA3
-#endif /* (SAMBA_VERSION_MAJOR<4) */
-#endif /* _SAMBA_BUILD_ */
-#ifndef _TALLOC_SAMBA3
-#include "replace.h"
-#include "talloc.h"
-#endif /* not _TALLOC_SAMBA3 */
+#ifdef TALLOC_BUILD_VERSION_MINOR
+#if (TALLOC_VERSION_MINOR != TALLOC_BUILD_VERSION_MINOR)
+#error "TALLOC_VERSION_MINOR != TALLOC_BUILD_VERSION_MINOR"
+#endif
+#endif
/* use this to force every realloc to change the pointer, to stress test
code that might not cope */
#define MAX_TALLOC_SIZE 0x10000000
-#define TALLOC_MAGIC 0xe814ec70
+#define TALLOC_MAGIC_BASE 0xe814ec70
+#define TALLOC_MAGIC ( \
+ TALLOC_MAGIC_BASE + \
+ (TALLOC_VERSION_MAJOR << 12) + \
+ (TALLOC_VERSION_MINOR << 4) \
+)
+
#define TALLOC_FLAG_FREE 0x01
#define TALLOC_FLAG_LOOP 0x02
#define TALLOC_FLAG_POOL 0x04 /* This is a talloc pool */
struct talloc_reference_handle {
struct talloc_reference_handle *next, *prev;
void *ptr;
+ const char *location;
};
typedef int (*talloc_destructor_t)(void *);
#define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15)
#define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc))
+int talloc_version_major(void)
+{
+ return TALLOC_VERSION_MAJOR;
+}
+
+int talloc_version_minor(void)
+{
+ return TALLOC_VERSION_MINOR;
+}
+
+static void (*talloc_log_fn)(const char *message);
+
+void talloc_set_log_fn(void (*log_fn)(const char *message))
+{
+ talloc_log_fn = log_fn;
+}
+
+static void talloc_log(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2);
+static void talloc_log(const char *fmt, ...)
+{
+ va_list ap;
+ char *message;
+
+ if (!talloc_log_fn) {
+ return;
+ }
+
+ va_start(ap, fmt);
+ message = talloc_vasprintf(NULL, fmt, ap);
+ va_end(ap);
+
+ talloc_log_fn(message);
+ talloc_free(message);
+}
+
+static void talloc_log_stderr(const char *message)
+{
+ fprintf(stderr, "%s", message);
+}
+
+void talloc_set_log_stderr(void)
+{
+ talloc_set_log_fn(talloc_log_stderr);
+}
+
+static void (*talloc_abort_fn)(const char *reason);
+
+void talloc_set_abort_fn(void (*abort_fn)(const char *reason))
+{
+ talloc_abort_fn = abort_fn;
+}
+
+static void talloc_abort(const char *reason)
+{
+ talloc_log("%s\n", reason);
+
+ if (!talloc_abort_fn) {
+ TALLOC_ABORT(reason);
+ }
+
+ talloc_abort_fn(reason);
+}
+
+static void talloc_abort_magic(unsigned magic)
+{
+ unsigned striped = magic - TALLOC_MAGIC_BASE;
+ unsigned major = (striped & 0xFFFFF000) >> 12;
+ unsigned minor = (striped & 0x00000FF0) >> 4;
+ talloc_log("Bad talloc magic[0x%08X/%u/%u] expected[0x%08X/%u/%u]\n",
+ magic, major, minor,
+ TALLOC_MAGIC, TALLOC_VERSION_MAJOR, TALLOC_VERSION_MINOR);
+ talloc_abort("Bad talloc magic value - wrong talloc version used/mixed");
+}
+
static void talloc_abort_double_free(void)
{
- TALLOC_ABORT("Bad talloc magic value - double free");
+ talloc_abort("Bad talloc magic value - double free");
}
static void talloc_abort_unknown_value(void)
{
- TALLOC_ABORT("Bad talloc magic value - unknown value");
+ talloc_abort("Bad talloc magic value - unknown value");
}
/* panic if we get a bad magic value */
const char *pp = (const char *)ptr;
struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE);
if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) {
+ if ((tc->flags & (~0xFFF)) == TALLOC_MAGIC_BASE) {
+ talloc_abort_magic(tc->flags & (~0xF));
+ return NULL;
+ }
+
if (tc->flags & TALLOC_FLAG_FREE) {
+ talloc_log("talloc: double free error - first free may be at %s\n", tc->name);
talloc_abort_double_free();
+ return NULL;
} else {
talloc_abort_unknown_value();
+ return NULL;
}
}
return tc;
same underlying data, and you want to be able to free the two instances separately,
and in either order
*/
-void *_talloc_reference(const void *context, const void *ptr)
+void *_talloc_reference_loc(const void *context, const void *ptr, const char *location)
{
struct talloc_chunk *tc;
struct talloc_reference_handle *handle;
own destructor on the context if they want to */
talloc_set_destructor(handle, talloc_reference_destructor);
handle->ptr = discard_const_p(void, ptr);
+ handle->location = location;
_TLIST_ADD(tc->refs, handle);
return handle->ptr;
}
+static void *_talloc_steal_internal(const void *new_ctx, const void *ptr);
/*
internal talloc_free call
*/
-static inline int _talloc_free(void *ptr)
+static inline int _talloc_free_internal(void *ptr, const char *location)
{
struct talloc_chunk *tc;
* pointer.
*/
is_child = talloc_is_parent(tc->refs, ptr);
- _talloc_free(tc->refs);
+ _talloc_free_internal(tc->refs, location);
if (is_child) {
- return _talloc_free(ptr);
+ return _talloc_free_internal(ptr, location);
}
return -1;
}
struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
if (p) new_parent = TC_PTR_FROM_CHUNK(p);
}
- if (unlikely(_talloc_free(child) == -1)) {
+ if (unlikely(_talloc_free_internal(child, location) == -1)) {
if (new_parent == null_context) {
struct talloc_chunk *p = talloc_parent_chunk(ptr);
if (p) new_parent = TC_PTR_FROM_CHUNK(p);
}
- talloc_steal(new_parent, child);
+ _talloc_steal_internal(new_parent, child);
}
}
tc->flags |= TALLOC_FLAG_FREE;
+ /* we mark the freed memory with where we called the free
+ * from. This means on a double free error we can report where
+ * the first free came from
+ */
+ tc->name = location;
+
if (tc->flags & (TALLOC_FLAG_POOL|TALLOC_FLAG_POOLMEM)) {
struct talloc_chunk *pool;
unsigned int *pool_object_count;
pool_object_count = talloc_pool_objectcount(pool);
if (*pool_object_count == 0) {
- TALLOC_ABORT("Pool object count zero!");
+ talloc_abort("Pool object count zero!");
+ return 0;
}
*pool_object_count -= 1;
ptr on success, or NULL if it could not be transferred.
passing NULL as ptr will always return NULL with no side effects.
*/
-void *_talloc_steal(const void *new_ctx, const void *ptr)
+static void *_talloc_steal_internal(const void *new_ctx, const void *ptr)
{
struct talloc_chunk *tc, *new_tc;
return discard_const_p(void, ptr);
}
+/*
+ move a lump of memory from one talloc context to another return the
+ ptr on success, or NULL if it could not be transferred.
+ passing NULL as ptr will always return NULL with no side effects.
+*/
+void *_talloc_steal_loc(const void *new_ctx, const void *ptr, const char *location)
+{
+ struct talloc_chunk *tc;
+ if (unlikely(ptr == NULL)) {
+ return NULL;
+ }
+
+ tc = talloc_chunk_from_ptr(ptr);
+
+ if (unlikely(tc->refs != NULL) && talloc_parent(ptr) != new_ctx) {
+ struct talloc_reference_handle *h;
+
+ talloc_log("WARNING: talloc_steal with references at %s\n",
+ location);
+
+ for (h=tc->refs; h; h=h->next) {
+ talloc_log("\treference at %s\n",
+ h->location);
+ }
+ }
+
+ return _talloc_steal_internal(new_ctx, ptr);
+}
+
+/*
+ this is like a talloc_steal(), but you must supply the old
+ parent. This resolves the ambiguity in a talloc_steal() which is
+ called on a context that has more than one parent (via references)
+
+ The old parent can be either a reference or a parent
+*/
+void *talloc_reparent(const void *old_parent, const void *new_parent, const void *ptr)
+{
+ struct talloc_chunk *tc;
+ struct talloc_reference_handle *h;
+
+ if (unlikely(ptr == NULL)) {
+ return NULL;
+ }
+
+ if (old_parent == talloc_parent(ptr)) {
+ return _talloc_steal_internal(new_parent, ptr);
+ }
+
+ tc = talloc_chunk_from_ptr(ptr);
+ for (h=tc->refs;h;h=h->next) {
+ if (talloc_parent(h) == old_parent) {
+ if (_talloc_steal_internal(new_parent, h) != h) {
+ return NULL;
+ }
+ return discard_const_p(void, ptr);
+ }
+ }
+
+ /* it wasn't a parent */
+ return NULL;
+}
/*
remove a secondary reference to a pointer. This undo's what
return -1;
}
- return _talloc_free(h);
+ return _talloc_free_internal(h, __location__);
}
/*
tc_p = talloc_chunk_from_ptr(ptr);
if (tc_p->refs == NULL) {
- return _talloc_free(ptr);
+ return _talloc_free_internal(ptr, __location__);
}
new_p = talloc_parent_chunk(tc_p->refs);
return -1;
}
- talloc_steal(new_parent, ptr);
+ _talloc_steal_internal(new_parent, ptr);
return 0;
}
va_end(ap);
if (unlikely(name == NULL)) {
- _talloc_free(ptr);
+ _talloc_free_internal(ptr, __location__);
return NULL;
}
return NULL;
}
+static void talloc_abort_type_missmatch(const char *location,
+ const char *name,
+ const char *expected)
+{
+ const char *reason;
+
+ reason = talloc_asprintf(NULL,
+ "%s: Type mismatch: name[%s] expected[%s]",
+ location,
+ name?name:"NULL",
+ expected);
+ if (!reason) {
+ reason = "Type mismatch";
+ }
+
+ talloc_abort(reason);
+}
+
+void *_talloc_get_type_abort(const void *ptr, const char *name, const char *location)
+{
+ const char *pname;
+
+ if (unlikely(ptr == NULL)) {
+ talloc_abort_type_missmatch(location, NULL, name);
+ return NULL;
+ }
+
+ pname = talloc_get_name(ptr);
+ if (likely(pname == name || strcmp(pname, name) == 0)) {
+ return discard_const_p(void, ptr);
+ }
+
+ talloc_abort_type_missmatch(location, pname, name);
+ return NULL;
+}
/*
this is for compatibility with older versions of talloc
va_end(ap);
if (unlikely(name == NULL)) {
- _talloc_free(ptr);
+ _talloc_free_internal(ptr, __location__);
return NULL;
}
struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
if (p) new_parent = TC_PTR_FROM_CHUNK(p);
}
- if (unlikely(_talloc_free(child) == -1)) {
+ if (unlikely(talloc_free(child) == -1)) {
if (new_parent == null_context) {
struct talloc_chunk *p = talloc_parent_chunk(ptr);
if (p) new_parent = TC_PTR_FROM_CHUNK(p);
}
- talloc_steal(new_parent, child);
+ _talloc_steal_internal(new_parent, child);
}
}
will not be freed if the ref_count is > 1 or the destructor (if
any) returns non-zero
*/
-int talloc_free(void *ptr)
+int _talloc_free(void *ptr, const char *location)
{
- return _talloc_free(ptr);
+ struct talloc_chunk *tc;
+
+ if (unlikely(ptr == NULL)) {
+ return -1;
+ }
+
+ tc = talloc_chunk_from_ptr(ptr);
+
+ if (unlikely(tc->refs != NULL)) {
+ struct talloc_reference_handle *h;
+
+ talloc_log("ERROR: talloc_free with references at %s\n",
+ location);
+
+ for (h=tc->refs; h; h=h->next) {
+ talloc_log("\treference at %s\n",
+ h->location);
+ }
+ return -1;
+ }
+
+ return _talloc_free_internal(ptr, location);
}
/* size zero is equivalent to free() */
if (unlikely(size == 0)) {
- _talloc_free(ptr);
+ talloc_unlink(context, ptr);
return NULL;
}
return NULL;
}
+ /* don't let anybody try to realloc a talloc_pool */
+ if (unlikely(tc->flags & TALLOC_FLAG_POOL)) {
+ return NULL;
+ }
+
/* don't shrink if we have less than 1k to gain */
if ((size < tc->size) && ((tc->size - size) < 1024)) {
tc->size = size;
void *_talloc_move(const void *new_ctx, const void *_pptr)
{
const void **pptr = discard_const_p(const void *,_pptr);
- void *ret = _talloc_steal(new_ctx, *pptr);
+ void *ret = talloc_steal(new_ctx, discard_const_p(void, *pptr));
(*pptr) = NULL;
return ret;
}
tc->flags |= TALLOC_FLAG_LOOP;
- total = tc->size;
+ if (likely(tc->name != TALLOC_MAGIC_REFERENCE)) {
+ total = tc->size;
+ }
for (c=tc->child;c;c=c->next) {
total += talloc_total_size(TC_PTR_FROM_CHUNK(c));
}
size_t talloc_total_blocks(const void *ptr)
{
size_t total = 0;
- struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr);
+ struct talloc_chunk *c, *tc;
+
+ if (ptr == NULL) {
+ ptr = null_context;
+ }
+ if (ptr == NULL) {
+ return 0;
+ }
+
+ tc = talloc_chunk_from_ptr(ptr);
if (tc->flags & TALLOC_FLAG_LOOP) {
return 0;
*/
void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f)
{
- talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f);
- fflush(f);
+ if (f) {
+ talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f);
+ fflush(f);
+ }
}
/*
enable tracking of the NULL context
*/
void talloc_enable_null_tracking(void)
+{
+ if (null_context == NULL) {
+ null_context = _talloc_named_const(NULL, 0, "null_context");
+ if (autofree_context != NULL) {
+ talloc_reparent(NULL, null_context, autofree_context);
+ }
+ }
+}
+
+/*
+ enable tracking of the NULL context, not moving the autofree context
+ into the NULL context. This is needed for the talloc testsuite
+*/
+void talloc_enable_null_tracking_no_autofree(void)
{
if (null_context == NULL) {
null_context = _talloc_named_const(NULL, 0, "null_context");
*/
void talloc_disable_null_tracking(void)
{
- _talloc_free(null_context);
+ if (null_context != NULL) {
+ /* we have to move any children onto the real NULL
+ context */
+ struct talloc_chunk *tc, *tc2;
+ tc = talloc_chunk_from_ptr(null_context);
+ for (tc2 = tc->child; tc2; tc2=tc2->next) {
+ if (tc2->parent == tc) tc2->parent = NULL;
+ if (tc2->prev == tc) tc2->prev = NULL;
+ }
+ for (tc2 = tc->next; tc2; tc2=tc2->next) {
+ if (tc2->parent == tc) tc2->parent = NULL;
+ if (tc2->prev == tc) tc2->prev = NULL;
+ }
+ tc->child = NULL;
+ tc->next = NULL;
+ }
+ talloc_free(null_context);
null_context = NULL;
}
static void talloc_autofree(void)
{
- _talloc_free(autofree_context);
+ talloc_free(autofree_context);
}
/*
{
struct talloc_chunk *tc;
- if (context == NULL)
+ if (context == NULL) {
+ context = null_context;
+ }
+ if (context == NULL) {
return 0;
+ }
tc = talloc_chunk_from_ptr(context);