1
0
mirror of https://github.com/samba-team/samba.git synced 2025-03-02 08:58:33 +03:00

changes to remove the ambiguity in talloc_free() and talloc_steal()

These changes follow from the discussions on samba-technical. The
changes are in several parts, and stem from the inherent ambiguity
that was in talloc_free() and talloc_steal() when the pointer that is
being changes has more than one parent, via references.

The changes are:

 1) when you call talloc_free() on a pointer with more than one parent
 the free will fail, and talloc will log an error to stderr like this:

    ERROR: talloc_free with references at some/foo.c:123
	   reference at other/bar.c:201
	   reference at other/foobar.c:641
 
 2) Similarly, when you call talloc_steal() on a pointer with more
 than one parent, the steal will fail and talloc will log an error to
 stderr like this:

    ERROR: talloc_steal with references at some/foo.c:123
	   reference at other/bar.c:201

 3) A new function talloc_reparent() has been added to change a parent
 in a controlled fashion. You need to supply both the old parent and
 the new parent. It handles the case whether either the old parent was
 a normal parent or a reference

The use of stderr in the logging is ugly (and potentially dangerous),
and will be removed in a future patch. We'll need to add a debug
registration function to talloc.
This commit is contained in:
Andrew Tridgell 2009-07-01 14:53:01 +10:00
parent 6a192020a2
commit 5fe1d8dc12
2 changed files with 109 additions and 26 deletions

View File

@ -107,6 +107,7 @@ static void *autofree_context;
struct talloc_reference_handle {
struct talloc_reference_handle *next, *prev;
void *ptr;
const char *location;
};
typedef int (*talloc_destructor_t)(void *);
@ -465,7 +466,7 @@ static inline void *_talloc_named_const(const void *context, size_t size, const
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(const void *context, const void *ptr, const char *location)
{
struct talloc_chunk *tc;
struct talloc_reference_handle *handle;
@ -482,6 +483,7 @@ void *_talloc_reference(const void *context, const void *ptr)
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;
}
@ -490,7 +492,7 @@ void *_talloc_reference(const void *context, 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;
@ -510,9 +512,9 @@ static inline int _talloc_free(void *ptr)
* 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;
}
@ -559,12 +561,12 @@ static inline int _talloc_free(void *ptr)
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);
}
}
@ -600,7 +602,7 @@ static inline int _talloc_free(void *ptr)
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)
void *_talloc_steal_internal(const void *new_ctx, const void *ptr)
{
struct talloc_chunk *tc, *new_tc;
@ -653,6 +655,66 @@ void *_talloc_steal(const void *new_ctx, const 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(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;
fprintf(stderr, "ERROR: talloc_steal with references at %s\n", location);
for (h=tc->refs; h; h=h->next) {
fprintf(stderr, "\treference at %s\n", h->location);
}
return NULL;
}
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 ptr;
}
}
/* it wasn't a parent */
return NULL;
}
/*
remove a secondary reference to a pointer. This undo's what
@ -680,7 +742,7 @@ static inline int talloc_unreference(const void *context, const void *ptr)
return -1;
}
return _talloc_free(h);
return _talloc_free_internal(h);
}
/*
@ -717,7 +779,7 @@ int talloc_unlink(const void *context, void *ptr)
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);
@ -731,7 +793,7 @@ int talloc_unlink(const void *context, void *ptr)
return -1;
}
talloc_steal(new_parent, ptr);
_talloc_steal_internal(new_parent, ptr);
return 0;
}
@ -784,7 +846,7 @@ void *talloc_named(const void *context, size_t size, const char *fmt, ...)
va_end(ap);
if (unlikely(name == NULL)) {
_talloc_free(ptr);
_talloc_free_internal(ptr);
return NULL;
}
@ -882,7 +944,7 @@ void *talloc_init(const char *fmt, ...)
va_end(ap);
if (unlikely(name == NULL)) {
_talloc_free(ptr);
_talloc_free_internal(ptr);
return NULL;
}
@ -916,12 +978,12 @@ void talloc_free_children(void *ptr)
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);
}
}
@ -969,9 +1031,26 @@ void *talloc_named_const(const void *context, size_t size, const char *name)
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;
fprintf(stderr, "ERROR: talloc_free with references at %s\n", location);
for (h=tc->refs; h; h=h->next) {
fprintf(stderr, "\treference at %s\n", h->location);
}
return -1;
}
return _talloc_free_internal(ptr);
}
@ -988,7 +1067,7 @@ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *n
/* size zero is equivalent to free() */
if (unlikely(size == 0)) {
_talloc_free(ptr);
talloc_free(ptr);
return NULL;
}
@ -1085,7 +1164,7 @@ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *n
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, *pptr);
(*pptr) = NULL;
return ret;
}
@ -1306,7 +1385,7 @@ void talloc_enable_null_tracking(void)
*/
void talloc_disable_null_tracking(void)
{
_talloc_free(null_context);
talloc_free(null_context);
null_context = NULL;
}
@ -1688,7 +1767,7 @@ static int talloc_autofree_destructor(void *ptr)
static void talloc_autofree(void)
{
_talloc_free(autofree_context);
talloc_free(autofree_context);
}
/*

View File

@ -69,15 +69,15 @@ typedef void TALLOC_CTX;
} while(0)
/* this extremely strange macro is to avoid some braindamaged warning
stupidity in gcc 4.1.x */
#define talloc_steal(ctx, ptr) ({ _TALLOC_TYPEOF(ptr) __talloc_steal_ret = (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)); __talloc_steal_ret; })
#define talloc_steal(ctx, ptr) ({ _TALLOC_TYPEOF(ptr) __talloc_steal_ret = (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr), __location__); __talloc_steal_ret; })
#else
#define talloc_set_destructor(ptr, function) \
_talloc_set_destructor((ptr), (int (*)(void *))(function))
#define _TALLOC_TYPEOF(ptr) void *
#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr))
#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr), __location__)
#endif
#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference((ctx),(ptr))
#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference((ctx),(ptr), __location__)
#define talloc_move(ctx, ptr) (_TALLOC_TYPEOF(*(ptr)))_talloc_move((ctx),(void *)(ptr))
/* useful macros for creating type checked pointers */
@ -106,6 +106,8 @@ typedef void TALLOC_CTX;
#define talloc_get_type_abort(ptr, type) (type *)_talloc_get_type_abort(ptr, #type, __location__)
#define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type)
#define talloc_free(ctx) _talloc_free(ctx, __location__)
#if TALLOC_DEPRECATED
#define talloc_zero_p(ctx, type) talloc_zero(ctx, type)
@ -124,7 +126,7 @@ void *talloc_pool(const void *context, size_t size);
void _talloc_set_destructor(const void *ptr, int (*_destructor)(void *));
int talloc_increase_ref_count(const void *ptr);
size_t talloc_reference_count(const void *ptr);
void *_talloc_reference(const void *context, const void *ptr);
void *_talloc_reference(const void *context, const void *ptr, const char *location);
int talloc_unlink(const void *context, void *ptr);
const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
void talloc_set_name_const(const void *ptr, const char *name);
@ -137,10 +139,12 @@ void *_talloc_get_type_abort(const void *ptr, const char *name, const char *loca
void *talloc_parent(const void *ptr);
const char *talloc_parent_name(const void *ptr);
void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2);
int talloc_free(void *ptr);
int _talloc_free(void *ptr, const char *location);
void talloc_free_children(void *ptr);
void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name);
void *_talloc_steal(const void *new_ctx, const void *ptr);
void *_talloc_steal(const void *new_ctx, const void *ptr, const char *location);
void *_talloc_steal_internal(const void *new_ctx, const void *ptr);
void *talloc_reparent(const void *old_parent, const void *new_parent, const void *ptr);
void *_talloc_move(const void *new_ctx, const void *pptr);
size_t talloc_total_size(const void *ptr);
size_t talloc_total_blocks(const void *ptr);