prevent git ref/unref mismatches. prevent reference promotion to owner
[metze/samba/wip.git] / lib / talloc / talloc.c
index c472e9fda9c685e7bab11f1ff83f562662c0569e..7296b93edd6db8cbf4c7c0073f6104cb97aa3db7 100644 (file)
 */
 static void *null_context;
 static void *autofree_context;
+static void *no_owner_context;
+void *talloc_no_owner_context(void);
+static inline int _talloc_free(void *ptr);
 
 struct talloc_reference_handle {
        struct talloc_reference_handle *next, *prev;
@@ -138,14 +141,30 @@ struct talloc_chunk {
 #define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15)
 #define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc))
 
+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)
+{
+       if (!talloc_abort_fn) {
+               TALLOC_ABORT(reason);
+       }
+
+       talloc_abort_fn(reason);
+}
+
 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 */
@@ -410,6 +429,11 @@ static int talloc_reference_destructor(struct talloc_reference_handle *handle)
 {
        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 &&
+               no_owner_context && talloc_parent(handle->ptr) == no_owner_context) {
+                       _talloc_free(handle->ptr);
+       }
        return 0;
 }
 
@@ -485,6 +509,10 @@ static inline int _talloc_free(void *ptr)
        tc = talloc_chunk_from_ptr(ptr);
 
        if (unlikely(tc->refs)) {
+       /*TODO: INSTEAD:
+       Rather than delete child references,
+               If a node descends from no_owner_context and so do all his references then it can be deleted
+       */
                int is_child;
                /* check this is a reference from a child or grantchild
                 * back to it's parent or grantparent
@@ -494,9 +522,12 @@ static inline int _talloc_free(void *ptr)
                 * 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(talloc_no_owner_context(), ptr);
                }
                return -1;
        }
@@ -538,16 +569,21 @@ static inline int _talloc_free(void *ptr)
                   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);
-               }
+               const void *new_parent = talloc_no_owner_context();
+/* we are moving this problem of who should own the can't-free to the top of the function */
+//HERE
+//             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 talloc_free fails because of refs and not due to the destructor,
+                  it will already have a new owner no_owner_context */
                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);
-                       }
+// making dead mans parent the owner could be a memory leak. no_owner_context will destroy with refs going
+//                     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);
                }
        }
@@ -564,7 +600,7 @@ static inline int _talloc_free(void *ptr)
                pool_object_count = talloc_pool_objectcount(pool);
 
                if (*pool_object_count == 0) {
-                       TALLOC_ABORT("Pool object count zero!");
+                       talloc_abort("Pool object count zero!");
                }
 
                *pool_object_count -= 1;
@@ -612,7 +648,6 @@ void *_talloc_steal(const void *new_ctx, const void *ptr)
                tc->parent = tc->next = tc->prev = NULL;
                return discard_const_p(void, ptr);
        }
-
        new_tc = talloc_chunk_from_ptr(new_ctx);
 
        if (unlikely(tc == new_tc || tc->parent == new_tc)) {
@@ -704,19 +739,20 @@ int talloc_unlink(const void *context, void *ptr)
                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);
-
+//HERE
+//     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(talloc_no_owner_context(), ptr);
        return 0;
 }
 
@@ -810,7 +846,18 @@ static void talloc_abort_type_missmatch(const char *location,
                                        const char *name,
                                        const char *expected)
 {
-       TALLOC_ABORT("Type missmatch");
+       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)
@@ -884,10 +931,12 @@ void talloc_free_children(void *ptr)
                   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;
+               const void *new_parent = talloc_no_owner_context();
+//HERE
                if (unlikely(tc->child->refs)) {
-                       struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
-                       if (p) new_parent = TC_PTR_FROM_CHUNK(p);
+//                     struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
+//                     if (p) new_parent = TC_PTR_FROM_CHUNK(p);
+                       new_parent = talloc_no_owner_context();
                }
                if (unlikely(_talloc_free(child) == -1)) {
                        if (new_parent == null_context) {
@@ -981,6 +1030,11 @@ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *n
                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;
@@ -1177,7 +1231,11 @@ static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_
        FILE *f = (FILE *)_f;
 
        if (is_ref) {
-               fprintf(f, "%*sreference to: %s\n", depth*4, "", name);
+               if (talloc_parent(ptr) == no_owner_context && no_owner_context) {
+                       fprintf(f, "%*sreference to: %s [no owner]\n", depth*4, "", name);
+               } else {
+                       fprintf(f, "%*sreference to: %s\n", depth*4, "", name);
+               }
                return;
        }
 
@@ -1673,6 +1731,29 @@ void *talloc_autofree_context(void)
        return autofree_context;
 }
 
+/* this is only used by the test suite so that failures from on test don't
+   confuse the results of the next test */
+void talloc_erase_no_owner_context(void)
+{
+       /* force free all children */
+       if (no_owner_context) {
+               struct talloc_chunk *tc;
+               tc = talloc_chunk_from_ptr(no_owner_context);
+               tc->child=NULL;
+               tc->refs=NULL;
+               _talloc_free(no_owner_context);
+               no_owner_context=NULL;
+       }
+}
+
+void *talloc_no_owner_context(void)
+{
+       if (no_owner_context == NULL) {
+               no_owner_context = _talloc_named_const(NULL, 0, "no_owner_context");
+       }
+       return no_owner_context;
+}
+
 size_t talloc_get_size(const void *context)
 {
        struct talloc_chunk *tc;