#define MAX_TALLOC_SIZE 0x10000000
-#define TALLOC_MAGIC 0xe814ec70
+#define TALLOC_MAGIC_V1 0xe814ec70
+#define TALLOC_MAGIC_V2 0xe814ec80
+#define TALLOC_MAGIC TALLOC_MAGIC_V2
#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 *);
talloc_abort_fn(reason);
}
+static void talloc_abort_magic_v1(void)
+{
+ talloc_abort("Bad talloc magic value - old magic v1 used");
+}
+
static void talloc_abort_double_free(void)
{
talloc_abort("Bad talloc magic value - double free");
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 & (~0xF)) == TALLOC_MAGIC_V1) {
+ talloc_abort_magic_v1();
+ return NULL;
+ }
+
if (tc->flags & TALLOC_FLAG_FREE) {
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)
{
struct talloc_chunk *tc;
* pointer.
*/
is_child = talloc_is_parent(tc->refs, ptr);
- _talloc_free(tc->refs);
+ _talloc_free_internal(tc->refs);
if (is_child) {
- return _talloc_free(ptr);
+ return _talloc_free_internal(ptr);
}
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) == -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);
}
}
if (*pool_object_count == 0) {
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;
+#if DEVELOPER
+ fprintf(stderr, "WARNING: talloc_steal with references at %s\n", location);
+#endif
+ for (h=tc->refs; h; h=h->next) {
+#if DEVELOPER
+ fprintf(stderr, "\treference at %s\n", h->location);
+#endif
+ }
+ }
+
+ 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 (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);
}
/*
tc_p = talloc_chunk_from_ptr(ptr);
if (tc_p->refs == NULL) {
- return _talloc_free(ptr);
+ return _talloc_free_internal(ptr);
}
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);
return NULL;
}
va_end(ap);
if (unlikely(name == NULL)) {
- _talloc_free(ptr);
+ _talloc_free_internal(ptr);
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;
+#if DEVELOPER
+ fprintf(stderr, "ERROR: talloc_free with references at %s\n", location);
+#endif
+ for (h=tc->refs; h; h=h->next) {
+#if DEVELOPER
+ fprintf(stderr, "\treference at %s\n", h->location);
+#endif
+ }
+ return -1;
+ }
+
+ return _talloc_free_internal(ptr);
}
/* size zero is equivalent to free() */
if (unlikely(size == 0)) {
- _talloc_free(ptr);
+ talloc_unlink(context, ptr);
return NULL;
}
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, (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_disable_null_tracking(void)
{
- _talloc_free(null_context);
+ 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);
}
return 0;
}
+
+
+
+
+/* ABI compat functions (do NOT append anything beyond thess functions,
+ * keep them as the last ones in the file) */
+
+static const char *talloc_ABI_compat_location = "Called from compatibility function";
+
+/* ABI compat function (don't use) */
+void *_talloc_reference(const void *context, const void *ptr) {
+ return _talloc_reference_loc(context, ptr, talloc_ABI_compat_location);
+}
+
+/* ABI compat function (don't use) */
+void *_talloc_steal(const void *new_ctx, const void *ptr)
+{
+ return _talloc_steal_internal(new_ctx, ptr);
+}
+
+#undef talloc_free
+int talloc_free(void *ptr);
+int talloc_free(void *ptr)
+{
+ return _talloc_free_internal(ptr);
+}
+
+/* DO NOT APPEND ANYTHING BEYOND THIS POINT */