#define TALLOC_FLAG_POOL 0x04 /* This is a talloc pool */
#define TALLOC_FLAG_POOLMEM 0x08 /* This is allocated in a pool */
#define TALLOC_MAGIC_REFERENCE ((const char *)1)
+#define TALLOC_MAGIC_NO_OWNER ((void *)1)
/* by default we abort when given a bad pointer (such as when talloc_free() is called
on a pointer that came from malloc() */
*/
static void *null_context;
static void *autofree_context;
+static void *no_owner_context = TALLOC_MAGIC_NO_OWNER;
+static inline int _talloc_free(void *ptr);
struct talloc_reference_handle {
struct talloc_reference_handle *next, *prev;
talloc_abort("Bad talloc magic value - double free");
}
+static void talloc_abort_no_owner_free(void)
+{
+ talloc_abort("Bad talloc parent - no_owner free");
+}
+
static void talloc_abort_unknown_value(void)
{
talloc_abort("Bad talloc magic value - unknown value");
if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) {
if (tc->flags & TALLOC_FLAG_FREE) {
talloc_abort_double_free();
+ return NULL;
} else {
talloc_abort_unknown_value();
+ return NULL;
}
}
return tc;
if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \
} while (0)
+static inline talloc_parent_is_no_owner(const void *ptr)
+{
+ struct talloc_chunk *tc;
+ struct talloc_chunk *no_owner_tc = TALLOC_MAGIC_NO_OWNER;
+
+ tc = talloc_chunk_from_ptr(ptr);
+
+ if (no_owner_context != TALLOC_MAGIC_NO_OWNER) {
+ no_owner_tc = talloc_chunk_from_ptr(no_owner_context);
+ }
+
+ if (tc->parent == no_owner_tc) {
+ return true;
+ }
+
+ return false;
+}
/*
return the parent chunk of a pointer
tc = talloc_chunk_from_ptr(ptr);
while (tc->prev) tc=tc->prev;
+ if (unlikely(tc->parent == TALLOC_MAGIC_NO_OWNER)) {
+ return NULL;
+ }
+
return tc->parent;
}
{
struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr);
_TLIST_REMOVE(ptr_tc->refs, handle);
+ /*
+ * If ptr_tc has no refs left and is owner by
+ * no_owner_context then we free it
+ */
+ if (ptr_tc->refs == NULL &&
+ talloc_parent_is_no_owner(handle->ptr)) {
+ int ret = _talloc_free(handle->ptr);
+ if (ret != 0) {
+ _TLIST_ADD(ptr_tc->refs, handle);
+ return ret;
+ }
+ }
return 0;
}
return handle->ptr;
}
+static inline void _talloc_free_children(struct talloc_chunk *tc)
+{
+ void *ptr = TC_PTR_FROM_CHUNK(tc);
+
+ tc->flags |= TALLOC_FLAG_LOOP;
+
+ while (tc->child) {
+ void *child = TC_PTR_FROM_CHUNK(tc->child);
+ if (unlikely(_talloc_free(child) == -1)) {
+ const void *new_parent = no_owner_context;
+ struct talloc_chunk *p = talloc_parent_chunk(ptr);
+
+ /*
+ * we need to work out who will own an abandoned child
+ * if it cannot be freed. In priority order, the first
+ * choice is our parent, and the final choice is the
+ * no owner context.
+ */
+ if (p) {
+ new_parent = TC_PTR_FROM_CHUNK(p);
+ }
+
+ /*
+ * if talloc_free fails because of refs and
+ * not due to the destructor, it will already
+ * have a new owner no_owner_context
+ */
+ if (!talloc_parent_is_no_owner(child)) {
+ talloc_steal(new_parent, child);
+ }
+ }
+ }
+
+ tc->flags &= ~TALLOC_FLAG_LOOP;
+}
/*
internal talloc_free call
tc = talloc_chunk_from_ptr(ptr);
if (unlikely(tc->refs)) {
+ /*TODO: Fix this loop detection. The code here breaks top reference if
+ it is also one of our children. It is insufficient and can lead
+ to dangling references */
+
int is_child;
/* check this is a reference from a child or grantchild
* back to it's parent or grantparent
* pointer.
*/
is_child = talloc_is_parent(tc->refs, ptr);
- _talloc_free(tc->refs);
if (is_child) {
+ _talloc_free(tc->refs);
return _talloc_free(ptr);
+ } else {
+ /*
+ * we can't free if we have refs,
+ * so no_owner_context becomes the owner
+ */
+ _talloc_steal(no_owner_context, ptr);
}
return -1;
}
if (tc->next) tc->next->prev = tc->prev;
}
- tc->flags |= TALLOC_FLAG_LOOP;
-
- while (tc->child) {
- /* we need to work out who will own an abandoned child
- if it cannot be freed. In priority order, the first
- choice is owner of any remaining reference to this
- pointer, the second choice is our parent, and the
- final choice is the null context. */
- void *child = TC_PTR_FROM_CHUNK(tc->child);
- const void *new_parent = null_context;
- if (unlikely(tc->child->refs)) {
- 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 (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_free_children(tc);
tc->flags |= TALLOC_FLAG_FREE;
if (*pool_object_count == 0) {
talloc_abort("Pool object count zero!");
+ return 0;
}
*pool_object_count -= 1;
return NULL;
}
+ if (unlikely(talloc_parent_is_no_owner(ptr) &&
+ new_ctx != no_owner_context)) {
+ talloc_abort_no_owner_free();
+ return NULL;
+ }
+
+ if (unlikely(new_ctx == TALLOC_MAGIC_NO_OWNER)) {
+ new_ctx = NULL;
+ }
+
if (unlikely(new_ctx == NULL)) {
new_ctx = null_context;
}
return _talloc_free(ptr);
}
- new_p = talloc_parent_chunk(tc_p->refs);
- if (new_p) {
- new_parent = TC_PTR_FROM_CHUNK(new_p);
- } else {
- new_parent = NULL;
- }
-
- if (talloc_unreference(new_parent, ptr) != 0) {
- return -1;
- }
-
- talloc_steal(new_parent, ptr);
-
+ talloc_steal(no_owner_context, ptr);
return 0;
}
tc = talloc_chunk_from_ptr(ptr);
- while (tc->child) {
- /* we need to work out who will own an abandoned child
- if it cannot be freed. In priority order, the first
- choice is owner of any remaining reference to this
- pointer, the second choice is our parent, and the
- final choice is the null context. */
- void *child = TC_PTR_FROM_CHUNK(tc->child);
- const void *new_parent = null_context;
- if (unlikely(tc->child->refs)) {
- 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 (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_free_children(tc);
if ((tc->flags & TALLOC_FLAG_POOL)
&& (*talloc_pool_objectcount(tc) == 1)) {
*/
int talloc_free(void *ptr)
{
+ struct talloc_chunk *tc;
+
+ if (unlikely(ptr == NULL)) {
+ return -1;
+ }
+
+ tc = talloc_chunk_from_ptr(ptr);
+
+ if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) {
+ /* we have a free loop - stop looping */
+ return 0;
+ }
+
+ if (unlikely(talloc_parent_is_no_owner(ptr))) {
+ talloc_abort_no_owner_free();
+ return -1;
+ }
+
return _talloc_free(ptr);
}
FILE *f = (FILE *)_f;
if (is_ref) {
- fprintf(f, "%*sreference to: %s\n", depth*4, "", name);
+ if (talloc_parent_is_no_owner(ptr)) {
+ fprintf(f, "%*sreference to: %s [no owner]\n", depth*4, "", name);
+ } else {
+ fprintf(f, "%*sreference to: %s\n", depth*4, "", name);
+ }
return;
}
{
if (null_context == NULL) {
null_context = _talloc_named_const(NULL, 0, "null_context");
+ no_owner_context = _talloc_named_const(null_context, 0,
+ "no_owner_context");
}
}
{
_talloc_free(null_context);
null_context = NULL;
+ no_owner_context = TALLOC_MAGIC_NO_OWNER;
}
/*