X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=libatalk%2Ftalloc%2Ftalloc.c;h=78dffa8e7363e9f414b02c6f997b94bf6d0e099e;hb=3a84db87064922ad10ac10cc1d6833380e575995;hp=f56ec431f33e6d748ee87af58fcc4d87d560a421;hpb=dd14ee0c14fe7a965ec8db07c0d3e8bb0aa49613;p=netatalk.git diff --git a/libatalk/talloc/talloc.c b/libatalk/talloc/talloc.c index f56ec431..78dffa8e 100644 --- a/libatalk/talloc/talloc.c +++ b/libatalk/talloc/talloc.c @@ -30,13 +30,25 @@ inspired by http://swapped.cc/halloc/ */ -#include +#include "config.h" + #include +#include +#include +#include #include #include #include +#define _PUBLIC_ extern + +/** + * pointer difference macro + */ +#define PTR_DIFF(p1,p2) ((ptrdiff_t)(((const char *)(p1)) - (const char *)(p2))) + + #ifdef TALLOC_BUILD_VERSION_MAJOR #if (TALLOC_VERSION_MAJOR != TALLOC_BUILD_VERSION_MAJOR) #error "TALLOC_VERSION_MAJOR != TALLOC_BUILD_VERSION_MAJOR" @@ -49,6 +61,15 @@ #endif #endif +/* Special macros that are no-ops except when run under Valgrind on + * x86. They've moved a little bit from valgrind 1.0.4 to 1.9.4 */ +#ifdef HAVE_VALGRIND_MEMCHECK_H + /* memcheck.h includes valgrind.h */ +#include +#elif defined(HAVE_VALGRIND_H) +#include +#endif + /* use this to force every realloc to change the pointer, to stress test code that might not cope */ #define ALWAYS_REALLOC 0 @@ -108,6 +129,114 @@ static void *null_context; static void *autofree_context; +/* used to enable fill of memory on free, which can be useful for + * catching use after free errors when valgrind is too slow + */ +static struct { + bool initialised; + bool enabled; + uint8_t fill_value; +} talloc_fill; + +#define TALLOC_FILL_ENV "TALLOC_FREE_FILL" + +/* + * do not wipe the header, to allow the + * double-free logic to still work + */ +#define TC_INVALIDATE_FULL_FILL_CHUNK(_tc) do { \ + if (unlikely(talloc_fill.enabled)) { \ + size_t _flen = (_tc)->size; \ + char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \ + memset(_fptr, talloc_fill.fill_value, _flen); \ + } \ +} while (0) + +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS) +/* Mark the whole chunk as not accessable */ +#define TC_INVALIDATE_FULL_VALGRIND_CHUNK(_tc) do { \ + size_t _flen = TC_HDR_SIZE + (_tc)->size; \ + char *_fptr = (char *)(_tc); \ + VALGRIND_MAKE_MEM_NOACCESS(_fptr, _flen); \ +} while(0) +#else +#define TC_INVALIDATE_FULL_VALGRIND_CHUNK(_tc) do { } while (0) +#endif + +#define TC_INVALIDATE_FULL_CHUNK(_tc) do { \ + TC_INVALIDATE_FULL_FILL_CHUNK(_tc); \ + TC_INVALIDATE_FULL_VALGRIND_CHUNK(_tc); \ +} while (0) + +#define TC_INVALIDATE_SHRINK_FILL_CHUNK(_tc, _new_size) do { \ + if (unlikely(talloc_fill.enabled)) { \ + size_t _flen = (_tc)->size - (_new_size); \ + char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \ + _fptr += (_new_size); \ + memset(_fptr, talloc_fill.fill_value, _flen); \ + } \ +} while (0) + +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS) +/* Mark the unused bytes not accessable */ +#define TC_INVALIDATE_SHRINK_VALGRIND_CHUNK(_tc, _new_size) do { \ + size_t _flen = (_tc)->size - (_new_size); \ + char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \ + _fptr += (_new_size); \ + VALGRIND_MAKE_MEM_NOACCESS(_fptr, _flen); \ +} while (0) +#else +#define TC_INVALIDATE_SHRINK_VALGRIND_CHUNK(_tc, _new_size) do { } while (0) +#endif + +#define TC_INVALIDATE_SHRINK_CHUNK(_tc, _new_size) do { \ + TC_INVALIDATE_SHRINK_FILL_CHUNK(_tc, _new_size); \ + TC_INVALIDATE_SHRINK_VALGRIND_CHUNK(_tc, _new_size); \ +} while (0) + +#define TC_UNDEFINE_SHRINK_FILL_CHUNK(_tc, _new_size) do { \ + if (unlikely(talloc_fill.enabled)) { \ + size_t _flen = (_tc)->size - (_new_size); \ + char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \ + _fptr += (_new_size); \ + memset(_fptr, talloc_fill.fill_value, _flen); \ + } \ +} while (0) + +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED) +/* Mark the unused bytes as undefined */ +#define TC_UNDEFINE_SHRINK_VALGRIND_CHUNK(_tc, _new_size) do { \ + size_t _flen = (_tc)->size - (_new_size); \ + char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \ + _fptr += (_new_size); \ + VALGRIND_MAKE_MEM_UNDEFINED(_fptr, _flen); \ +} while (0) +#else +#define TC_UNDEFINE_SHRINK_VALGRIND_CHUNK(_tc, _new_size) do { } while (0) +#endif + +#define TC_UNDEFINE_SHRINK_CHUNK(_tc, _new_size) do { \ + TC_UNDEFINE_SHRINK_FILL_CHUNK(_tc, _new_size); \ + TC_UNDEFINE_SHRINK_VALGRIND_CHUNK(_tc, _new_size); \ +} while (0) + +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED) +/* Mark the new bytes as undefined */ +#define TC_UNDEFINE_GROW_VALGRIND_CHUNK(_tc, _new_size) do { \ + size_t _old_used = TC_HDR_SIZE + (_tc)->size; \ + size_t _new_used = TC_HDR_SIZE + (_new_size); \ + size_t _flen = _new_used - _old_used; \ + char *_fptr = _old_used + (char *)(_tc); \ + VALGRIND_MAKE_MEM_UNDEFINED(_fptr, _flen); \ +} while (0) +#else +#define TC_UNDEFINE_GROW_VALGRIND_CHUNK(_tc, _new_size) do { } while (0) +#endif + +#define TC_UNDEFINE_GROW_CHUNK(_tc, _new_size) do { \ + TC_UNDEFINE_GROW_VALGRIND_CHUNK(_tc, _new_size); \ +} while (0) + struct talloc_reference_handle { struct talloc_reference_handle *next, *prev; void *ptr; @@ -140,22 +269,23 @@ struct talloc_chunk { }; /* 16 byte alignment seems to keep everyone happy */ -#define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15) +#define TC_ALIGN16(s) (((s)+15)&~15) +#define TC_HDR_SIZE TC_ALIGN16(sizeof(struct talloc_chunk)) #define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc)) -int talloc_version_major(void) +_PUBLIC_ int talloc_version_major(void) { return TALLOC_VERSION_MAJOR; } -int talloc_version_minor(void) +_PUBLIC_ 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)) +_PUBLIC_ void talloc_set_log_fn(void (*log_fn)(const char *message)) { talloc_log_fn = log_fn; } @@ -183,14 +313,14 @@ static void talloc_log_stderr(const char *message) fprintf(stderr, "%s", message); } -void talloc_set_log_stderr(void) +_PUBLIC_ 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)) +_PUBLIC_ void talloc_set_abort_fn(void (*abort_fn)(const char *reason)) { talloc_abort_fn = abort_fn; } @@ -217,9 +347,9 @@ static void talloc_abort_magic(unsigned magic) talloc_abort("Bad talloc magic value - wrong talloc version used/mixed"); } -static void talloc_abort_double_free(void) +static void talloc_abort_access_after_free(void) { - talloc_abort("Bad talloc magic value - double free"); + talloc_abort("Bad talloc magic value - access after free"); } static void talloc_abort_unknown_value(void) @@ -239,8 +369,8 @@ static inline struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr) } 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(); + talloc_log("talloc: access after free error - first free may be at %s\n", tc->name); + talloc_abort_access_after_free(); return NULL; } else { talloc_abort_unknown_value(); @@ -295,7 +425,7 @@ static inline struct talloc_chunk *talloc_parent_chunk(const void *ptr) return tc->parent; } -void *talloc_parent(const void *ptr) +_PUBLIC_ void *talloc_parent(const void *ptr) { struct talloc_chunk *tc = talloc_parent_chunk(ptr); return tc? TC_PTR_FROM_CHUNK(tc) : NULL; @@ -304,7 +434,7 @@ void *talloc_parent(const void *ptr) /* find parents name */ -const char *talloc_parent_name(const void *ptr) +_PUBLIC_ const char *talloc_parent_name(const void *ptr) { struct talloc_chunk *tc = talloc_parent_chunk(ptr); return tc? tc->name : NULL; @@ -325,9 +455,47 @@ const char *talloc_parent_name(const void *ptr) #define TALLOC_POOL_HDR_SIZE 16 +#define TC_POOL_SPACE_LEFT(_pool_tc) \ + PTR_DIFF(TC_HDR_SIZE + (_pool_tc)->size + (char *)(_pool_tc), \ + (_pool_tc)->pool) + +#define TC_POOL_FIRST_CHUNK(_pool_tc) \ + ((void *)(TC_HDR_SIZE + TALLOC_POOL_HDR_SIZE + (char *)(_pool_tc))) + +#define TC_POOLMEM_CHUNK_SIZE(_tc) \ + TC_ALIGN16(TC_HDR_SIZE + (_tc)->size) + +#define TC_POOLMEM_NEXT_CHUNK(_tc) \ + ((void *)(TC_POOLMEM_CHUNK_SIZE(tc) + (char*)(_tc))) + +/* Mark the whole remaining pool as not accessable */ +#define TC_INVALIDATE_FILL_POOL(_pool_tc) do { \ + if (unlikely(talloc_fill.enabled)) { \ + size_t _flen = TC_POOL_SPACE_LEFT(_pool_tc); \ + char *_fptr = (char *)(_pool_tc)->pool; \ + memset(_fptr, talloc_fill.fill_value, _flen); \ + } \ +} while(0) + +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS) +/* Mark the whole remaining pool as not accessable */ +#define TC_INVALIDATE_VALGRIND_POOL(_pool_tc) do { \ + size_t _flen = TC_POOL_SPACE_LEFT(_pool_tc); \ + char *_fptr = (char *)(_pool_tc)->pool; \ + VALGRIND_MAKE_MEM_NOACCESS(_fptr, _flen); \ +} while(0) +#else +#define TC_INVALIDATE_VALGRIND_POOL(_pool_tc) do { } while (0) +#endif + +#define TC_INVALIDATE_POOL(_pool_tc) do { \ + TC_INVALIDATE_FILL_POOL(_pool_tc); \ + TC_INVALIDATE_VALGRIND_POOL(_pool_tc); \ +} while (0) + static unsigned int *talloc_pool_objectcount(struct talloc_chunk *tc) { - return (unsigned int *)((char *)tc + sizeof(struct talloc_chunk)); + return (unsigned int *)((char *)tc + TC_HDR_SIZE); } /* @@ -357,13 +525,12 @@ static struct talloc_chunk *talloc_alloc_pool(struct talloc_chunk *parent, return NULL; } - space_left = ((char *)pool_ctx + TC_HDR_SIZE + pool_ctx->size) - - ((char *)pool_ctx->pool); + space_left = TC_POOL_SPACE_LEFT(pool_ctx); /* * Align size to 16 bytes */ - chunk_size = ((size + 15) & ~15); + chunk_size = TC_ALIGN16(size); if (space_left < chunk_size) { return NULL; @@ -442,7 +609,7 @@ static inline void *__talloc(const void *context, size_t size) * Create a talloc pool */ -void *talloc_pool(const void *context, size_t size) +_PUBLIC_ void *talloc_pool(const void *context, size_t size) { void *result = __talloc(context, size + TALLOC_POOL_HDR_SIZE); struct talloc_chunk *tc; @@ -454,13 +621,11 @@ void *talloc_pool(const void *context, size_t size) tc = talloc_chunk_from_ptr(result); tc->flags |= TALLOC_FLAG_POOL; - tc->pool = (char *)result + TALLOC_POOL_HDR_SIZE; + tc->pool = TC_POOL_FIRST_CHUNK(tc); *talloc_pool_objectcount(tc) = 1; -#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS) - VALGRIND_MAKE_MEM_NOACCESS(tc->pool, size); -#endif + TC_INVALIDATE_POOL(tc); return result; } @@ -471,7 +636,7 @@ void *talloc_pool(const void *context, size_t size) if the destructor fails then the free is failed, and the memory can be continued to be used */ -void _talloc_set_destructor(const void *ptr, int (*destructor)(void *)) +_PUBLIC_ void _talloc_set_destructor(const void *ptr, int (*destructor)(void *)) { struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); tc->destructor = destructor; @@ -480,7 +645,7 @@ void _talloc_set_destructor(const void *ptr, int (*destructor)(void *)) /* increase the reference count on a piece of memory. */ -int talloc_increase_ref_count(const void *ptr) +_PUBLIC_ int talloc_increase_ref_count(const void *ptr) { if (unlikely(!talloc_reference(null_context, ptr))) { return -1; @@ -536,7 +701,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_loc(const void *context, const void *ptr, const char *location) +_PUBLIC_ void *_talloc_reference_loc(const void *context, const void *ptr, const char *location) { struct talloc_chunk *tc; struct talloc_reference_handle *handle; @@ -560,6 +725,69 @@ void *_talloc_reference_loc(const void *context, const void *ptr, const char *lo static void *_talloc_steal_internal(const void *new_ctx, const void *ptr); +static inline void _talloc_free_poolmem(struct talloc_chunk *tc, + const char *location) +{ + struct talloc_chunk *pool; + void *next_tc; + unsigned int *pool_object_count; + + pool = (struct talloc_chunk *)tc->pool; + next_tc = TC_POOLMEM_NEXT_CHUNK(tc); + + 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; + + TC_INVALIDATE_FULL_CHUNK(tc); + + pool_object_count = talloc_pool_objectcount(pool); + + if (unlikely(*pool_object_count == 0)) { + talloc_abort("Pool object count zero!"); + return; + } + + *pool_object_count -= 1; + + if (unlikely(*pool_object_count == 1 && !(pool->flags & TALLOC_FLAG_FREE))) { + /* + * if there is just one object left in the pool + * and pool->flags does not have TALLOC_FLAG_FREE, + * it means this is the pool itself and + * the rest is available for new objects + * again. + */ + pool->pool = TC_POOL_FIRST_CHUNK(pool); + TC_INVALIDATE_POOL(pool); + } else if (unlikely(*pool_object_count == 0)) { + /* + * 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 + */ + pool->name = location; + + TC_INVALIDATE_FULL_CHUNK(pool); + free(pool); + } else if (pool->pool == next_tc) { + /* + * if pool->pool still points to end of + * 'tc' (which is stored in the 'next_tc' variable), + * we can reclaim the memory of 'tc'. + */ + pool->pool = tc; + } +} + +static inline void _talloc_free_children_internal(struct talloc_chunk *tc, + void *ptr, + const char *location); + /* internal talloc_free call */ @@ -571,12 +799,22 @@ static inline int _talloc_free_internal(void *ptr, const char *location) return -1; } + /* possibly initialised the talloc fill value */ + if (unlikely(!talloc_fill.initialised)) { + const char *fill = getenv(TALLOC_FILL_ENV); + if (fill != NULL) { + talloc_fill.enabled = true; + talloc_fill.fill_value = strtoul(fill, NULL, 0); + } + talloc_fill.initialised = true; + } + tc = talloc_chunk_from_ptr(ptr); if (unlikely(tc->refs)) { int is_child; - /* check this is a reference from a child or grantchild - * back to it's parent or grantparent + /* check if this is a reference from a child or + * grandchild back to it's parent or grandparent * * in that case we need to remove the reference and * call another instance of talloc_free() on the current @@ -616,30 +854,12 @@ static inline int _talloc_free_internal(void *ptr, const char *location) } else { if (tc->prev) tc->prev->next = tc->next; if (tc->next) tc->next->prev = tc->prev; + tc->prev = tc->next = NULL; } 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_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_internal(new_parent, child); - } - } + _talloc_free_children_internal(tc, ptr, location); tc->flags |= TALLOC_FLAG_FREE; @@ -649,27 +869,26 @@ static inline int _talloc_free_internal(void *ptr, const char *location) */ tc->name = location; - if (tc->flags & (TALLOC_FLAG_POOL|TALLOC_FLAG_POOLMEM)) { - struct talloc_chunk *pool; + if (tc->flags & TALLOC_FLAG_POOL) { unsigned int *pool_object_count; - pool = (tc->flags & TALLOC_FLAG_POOL) - ? tc : (struct talloc_chunk *)tc->pool; - - pool_object_count = talloc_pool_objectcount(pool); + pool_object_count = talloc_pool_objectcount(tc); - if (*pool_object_count == 0) { + if (unlikely(*pool_object_count == 0)) { talloc_abort("Pool object count zero!"); return 0; } *pool_object_count -= 1; - if (*pool_object_count == 0) { - free(pool); + if (unlikely(*pool_object_count == 0)) { + TC_INVALIDATE_FULL_CHUNK(tc); + free(tc); } - } - else { + } else if (tc->flags & TALLOC_FLAG_POOLMEM) { + _talloc_free_poolmem(tc, location); + } else { + TC_INVALIDATE_FULL_CHUNK(tc); free(tc); } return 0; @@ -723,6 +942,7 @@ static void *_talloc_steal_internal(const void *new_ctx, const void *ptr) } else { if (tc->prev) tc->prev->next = tc->next; if (tc->next) tc->next->prev = tc->prev; + tc->prev = tc->next = NULL; } tc->parent = new_tc; @@ -737,7 +957,7 @@ static void *_talloc_steal_internal(const void *new_ctx, const 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_loc(const void *new_ctx, const void *ptr, const char *location) +_PUBLIC_ void *_talloc_steal_loc(const void *new_ctx, const void *ptr, const char *location) { struct talloc_chunk *tc; @@ -758,6 +978,14 @@ void *_talloc_steal_loc(const void *new_ctx, const void *ptr, const char *locati h->location); } } + +#if 0 + /* this test is probably too expensive to have on in the + normal build, but it useful for debugging */ + if (talloc_is_parent(new_ctx, ptr)) { + talloc_log("WARNING: stealing into talloc child at %s\n", location); + } +#endif return _talloc_steal_internal(new_ctx, ptr); } @@ -769,7 +997,7 @@ void *_talloc_steal_loc(const void *new_ctx, const void *ptr, const char *locati 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) +_PUBLIC_ void *talloc_reparent(const void *old_parent, const void *new_parent, const void *ptr) { struct talloc_chunk *tc; struct talloc_reference_handle *h; @@ -829,7 +1057,7 @@ static inline int talloc_unreference(const void *context, const void *ptr) remove a specific parent context from a pointer. This is a more controlled varient of talloc_free() */ -int talloc_unlink(const void *context, void *ptr) +_PUBLIC_ int talloc_unlink(const void *context, void *ptr) { struct talloc_chunk *tc_p, *new_p; void *new_parent; @@ -896,7 +1124,7 @@ static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va /* add a name to an existing pointer */ -const char *talloc_set_name(const void *ptr, const char *fmt, ...) +_PUBLIC_ const char *talloc_set_name(const void *ptr, const char *fmt, ...) { const char *name; va_list ap; @@ -912,7 +1140,7 @@ const char *talloc_set_name(const void *ptr, const char *fmt, ...) talloc_named() operates just like talloc() except that it allows you to name the pointer. */ -void *talloc_named(const void *context, size_t size, const char *fmt, ...) +_PUBLIC_ void *talloc_named(const void *context, size_t size, const char *fmt, ...) { va_list ap; void *ptr; @@ -936,7 +1164,7 @@ void *talloc_named(const void *context, size_t size, const char *fmt, ...) /* return the name of a talloc ptr, or "UNNAMED" */ -const char *talloc_get_name(const void *ptr) +_PUBLIC_ const char *talloc_get_name(const void *ptr) { struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); if (unlikely(tc->name == TALLOC_MAGIC_REFERENCE)) { @@ -953,7 +1181,7 @@ const char *talloc_get_name(const void *ptr) check if a pointer has the given name. If it does, return the pointer, otherwise return NULL */ -void *talloc_check_name(const void *ptr, const char *name) +_PUBLIC_ void *talloc_check_name(const void *ptr, const char *name) { const char *pname; if (unlikely(ptr == NULL)) return NULL; @@ -982,7 +1210,7 @@ static void talloc_abort_type_missmatch(const char *location, talloc_abort(reason); } -void *_talloc_get_type_abort(const void *ptr, const char *name, const char *location) +_PUBLIC_ void *_talloc_get_type_abort(const void *ptr, const char *name, const char *location) { const char *pname; @@ -1003,19 +1231,12 @@ void *_talloc_get_type_abort(const void *ptr, const char *name, const char *loca /* this is for compatibility with older versions of talloc */ -void *talloc_init(const char *fmt, ...) +_PUBLIC_ void *talloc_init(const char *fmt, ...) { va_list ap; void *ptr; const char *name; - /* - * samba3 expects talloc_report_depth_cb(NULL, ...) - * reports all talloc'ed memory, so we need to enable - * null_tracking - */ - talloc_enable_null_tracking(); - ptr = __talloc(NULL, 0); if (unlikely(ptr == NULL)) return NULL; @@ -1031,21 +1252,10 @@ void *talloc_init(const char *fmt, ...) return ptr; } -/* - this is a replacement for the Samba3 talloc_destroy_pool functionality. It - should probably not be used in new code. It's in here to keep the talloc - code consistent across Samba 3 and 4. -*/ -void talloc_free_children(void *ptr) +static inline void _talloc_free_children_internal(struct talloc_chunk *tc, + void *ptr, + const char *location) { - struct talloc_chunk *tc; - - if (unlikely(ptr == NULL)) { - return; - } - - 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 @@ -1058,7 +1268,7 @@ 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_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); @@ -1066,21 +1276,53 @@ void talloc_free_children(void *ptr) _talloc_steal_internal(new_parent, child); } } +} - if ((tc->flags & TALLOC_FLAG_POOL) - && (*talloc_pool_objectcount(tc) == 1)) { - tc->pool = ((char *)tc + TC_HDR_SIZE + TALLOC_POOL_HDR_SIZE); -#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS) - VALGRIND_MAKE_MEM_NOACCESS( - tc->pool, tc->size - TALLOC_POOL_HDR_SIZE); -#endif +/* + this is a replacement for the Samba3 talloc_destroy_pool functionality. It + should probably not be used in new code. It's in here to keep the talloc + code consistent across Samba 3 and 4. +*/ +_PUBLIC_ void talloc_free_children(void *ptr) +{ + struct talloc_chunk *tc_name = NULL; + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return; + } + + tc = talloc_chunk_from_ptr(ptr); + + /* we do not want to free the context name if it is a child .. */ + if (likely(tc->child)) { + for (tc_name = tc->child; tc_name; tc_name = tc_name->next) { + if (tc->name == TC_PTR_FROM_CHUNK(tc_name)) break; + } + if (tc_name) { + _TLIST_REMOVE(tc->child, tc_name); + if (tc->child) { + tc->child->parent = tc; + } + } + } + + _talloc_free_children_internal(tc, ptr, __location__); + + /* .. so we put it back after all other children have been freed */ + if (tc_name) { + if (tc->child) { + tc->child->parent = NULL; + } + tc_name->parent = tc; + _TLIST_ADD(tc->child, tc_name); } } /* Allocate a bit of memory as a child of an existing pointer */ -void *_talloc(const void *context, size_t size) +_PUBLIC_ void *_talloc(const void *context, size_t size) { return __talloc(context, size); } @@ -1088,7 +1330,7 @@ void *_talloc(const void *context, size_t size) /* externally callable talloc_set_name_const() */ -void talloc_set_name_const(const void *ptr, const char *name) +_PUBLIC_ void talloc_set_name_const(const void *ptr, const char *name) { _talloc_set_name_const(ptr, name); } @@ -1098,7 +1340,7 @@ void talloc_set_name_const(const void *ptr, const char *name) talloc_named() operates just like talloc() except that it allows you to name the pointer. */ -void *talloc_named_const(const void *context, size_t size, const char *name) +_PUBLIC_ void *talloc_named_const(const void *context, size_t size, const char *name) { return _talloc_named_const(context, size, name); } @@ -1111,7 +1353,7 @@ 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, const char *location) +_PUBLIC_ int _talloc_free(void *ptr, const char *location) { struct talloc_chunk *tc; @@ -1124,6 +1366,13 @@ int _talloc_free(void *ptr, const char *location) if (unlikely(tc->refs != NULL)) { struct talloc_reference_handle *h; + if (talloc_parent(ptr) == null_context && tc->refs->next == NULL) { + /* in this case we do know which parent should + get this pointer, as there is really only + one parent */ + return talloc_unlink(null_context, ptr); + } + talloc_log("ERROR: talloc_free with references at %s\n", location); @@ -1143,11 +1392,12 @@ int _talloc_free(void *ptr, const char *location) A talloc version of realloc. The context argument is only used if ptr is NULL */ -void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name) +_PUBLIC_ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name) { struct talloc_chunk *tc; void *new_ptr; bool malloced = false; + struct talloc_chunk *pool_tc = NULL; /* size zero is equivalent to free() */ if (unlikely(size == 0)) { @@ -1176,27 +1426,154 @@ 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_POOLMEM)) { + pool_tc = (struct talloc_chunk *)tc->pool; + } + +#if (ALWAYS_REALLOC == 0) /* don't shrink if we have less than 1k to gain */ - if ((size < tc->size) && ((tc->size - size) < 1024)) { - tc->size = size; + if (size < tc->size) { + if (pool_tc) { + void *next_tc = TC_POOLMEM_NEXT_CHUNK(tc); + TC_INVALIDATE_SHRINK_CHUNK(tc, size); + tc->size = size; + if (next_tc == pool_tc->pool) { + pool_tc->pool = TC_POOLMEM_NEXT_CHUNK(tc); + } + return ptr; + } else if ((tc->size - size) < 1024) { + /* + * if we call TC_INVALIDATE_SHRINK_CHUNK() here + * we would need to call TC_UNDEFINE_GROW_CHUNK() + * after each realloc call, which slows down + * testing a lot :-(. + * + * That is why we only mark memory as undefined here. + */ + TC_UNDEFINE_SHRINK_CHUNK(tc, size); + + /* do not shrink if we have less than 1k to gain */ + tc->size = size; + return ptr; + } + } else if (tc->size == size) { + /* + * do not change the pointer if it is exactly + * the same size. + */ return ptr; } +#endif /* by resetting magic we catch users of the old memory */ tc->flags |= TALLOC_FLAG_FREE; #if ALWAYS_REALLOC - new_ptr = malloc(size + TC_HDR_SIZE); - if (new_ptr) { - memcpy(new_ptr, tc, MIN(tc->size, size) + TC_HDR_SIZE); - free(tc); + if (pool_tc) { + new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE); + *talloc_pool_objectcount(pool_tc) -= 1; + + if (new_ptr == NULL) { + new_ptr = malloc(TC_HDR_SIZE+size); + malloced = true; + } + + if (new_ptr) { + memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE); + TC_INVALIDATE_FULL_CHUNK(tc); + } + } else { + new_ptr = malloc(size + TC_HDR_SIZE); + if (new_ptr) { + memcpy(new_ptr, tc, MIN(tc->size, size) + TC_HDR_SIZE); + free(tc); + } } #else - if (tc->flags & TALLOC_FLAG_POOLMEM) { + if (pool_tc) { + void *next_tc = TC_POOLMEM_NEXT_CHUNK(tc); + size_t old_chunk_size = TC_POOLMEM_CHUNK_SIZE(tc); + size_t new_chunk_size = TC_ALIGN16(TC_HDR_SIZE + size); + size_t space_needed; + size_t space_left; + unsigned int chunk_count = *talloc_pool_objectcount(pool_tc); + + if (!(pool_tc->flags & TALLOC_FLAG_FREE)) { + chunk_count -= 1; + } + + if (chunk_count == 1) { + /* + * optimize for the case where 'tc' is the only + * chunk in the pool. + */ + space_needed = new_chunk_size; + space_left = pool_tc->size - TALLOC_POOL_HDR_SIZE; + + if (space_left >= space_needed) { + size_t old_used = TC_HDR_SIZE + tc->size; + size_t new_used = TC_HDR_SIZE + size; + pool_tc->pool = TC_POOL_FIRST_CHUNK(pool_tc); +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED) + /* + * we need to prepare the memmove into + * the unaccessable area. + */ + { + size_t diff = PTR_DIFF(tc, pool_tc->pool); + size_t flen = MIN(diff, old_used); + char *fptr = (char *)pool_tc->pool; + VALGRIND_MAKE_MEM_UNDEFINED(fptr, flen); + } +#endif + memmove(pool_tc->pool, tc, old_used); + new_ptr = pool_tc->pool; + + tc = (struct talloc_chunk *)new_ptr; + TC_UNDEFINE_GROW_CHUNK(tc, size); + + /* + * first we do not align the pool pointer + * because we want to invalidate the padding + * too. + */ + pool_tc->pool = new_used + (char *)new_ptr; + TC_INVALIDATE_POOL(pool_tc); + + /* now the aligned pointer */ + pool_tc->pool = new_chunk_size + (char *)new_ptr; + goto got_new_ptr; + } + + next_tc = NULL; + } + + if (new_chunk_size == old_chunk_size) { + TC_UNDEFINE_GROW_CHUNK(tc, size); + tc->flags &= ~TALLOC_FLAG_FREE; + tc->size = size; + return ptr; + } + + if (next_tc == pool_tc->pool) { + /* + * optimize for the case where 'tc' is the last + * chunk in the pool. + */ + space_needed = new_chunk_size - old_chunk_size; + space_left = TC_POOL_SPACE_LEFT(pool_tc); + + if (space_left >= space_needed) { + TC_UNDEFINE_GROW_CHUNK(tc, size); + tc->flags &= ~TALLOC_FLAG_FREE; + tc->size = size; + pool_tc->pool = TC_POOLMEM_NEXT_CHUNK(tc); + return ptr; + } + } new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE); - *talloc_pool_objectcount((struct talloc_chunk *) - (tc->pool)) -= 1; if (new_ptr == NULL) { new_ptr = malloc(TC_HDR_SIZE+size); @@ -1205,11 +1582,14 @@ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *n if (new_ptr) { memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE); + + _talloc_free_poolmem(tc, __location__ "_talloc_realloc"); } } else { new_ptr = realloc(tc, size + TC_HDR_SIZE); } +got_new_ptr: #endif if (unlikely(!new_ptr)) { tc->flags &= ~TALLOC_FLAG_FREE; @@ -1245,7 +1625,7 @@ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *n a wrapper around talloc_steal() for situations where you are moving a pointer between two structures, and want the old pointer to be set to NULL */ -void *_talloc_move(const void *new_ctx, const void *_pptr) +_PUBLIC_ 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, discard_const_p(void, *pptr)); @@ -1256,7 +1636,7 @@ void *_talloc_move(const void *new_ctx, const void *_pptr) /* return the total size of a talloc pool (subtree) */ -size_t talloc_total_size(const void *ptr) +_PUBLIC_ size_t talloc_total_size(const void *ptr) { size_t total = 0; struct talloc_chunk *c, *tc; @@ -1291,7 +1671,7 @@ size_t talloc_total_size(const void *ptr) /* return the total number of blocks in a talloc pool (subtree) */ -size_t talloc_total_blocks(const void *ptr) +_PUBLIC_ size_t talloc_total_blocks(const void *ptr) { size_t total = 0; struct talloc_chunk *c, *tc; @@ -1324,7 +1704,7 @@ size_t talloc_total_blocks(const void *ptr) /* return the number of external references to a pointer */ -size_t talloc_reference_count(const void *ptr) +_PUBLIC_ size_t talloc_reference_count(const void *ptr) { struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); struct talloc_reference_handle *h; @@ -1339,7 +1719,7 @@ size_t talloc_reference_count(const void *ptr) /* report on memory usage by all children of a pointer, giving a full tree view */ -void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, +_PUBLIC_ void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, void (*callback)(const void *ptr, int depth, int max_depth, int is_ref, @@ -1423,7 +1803,7 @@ static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_ /* report on memory usage by all children of a pointer, giving a full tree view */ -void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f) +_PUBLIC_ void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f) { if (f) { talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f); @@ -1434,7 +1814,7 @@ void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f /* report on memory usage by all children of a pointer, giving a full tree view */ -void talloc_report_full(const void *ptr, FILE *f) +_PUBLIC_ void talloc_report_full(const void *ptr, FILE *f) { talloc_report_depth_file(ptr, 0, -1, f); } @@ -1442,7 +1822,7 @@ void talloc_report_full(const void *ptr, FILE *f) /* report on memory usage by all children of a pointer */ -void talloc_report(const void *ptr, FILE *f) +_PUBLIC_ void talloc_report(const void *ptr, FILE *f) { talloc_report_depth_file(ptr, 0, 1, f); } @@ -1470,7 +1850,7 @@ static void talloc_report_null_full(void) /* enable tracking of the NULL context */ -void talloc_enable_null_tracking(void) +_PUBLIC_ void talloc_enable_null_tracking(void) { if (null_context == NULL) { null_context = _talloc_named_const(NULL, 0, "null_context"); @@ -1484,7 +1864,7 @@ void talloc_enable_null_tracking(void) 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) +_PUBLIC_ void talloc_enable_null_tracking_no_autofree(void) { if (null_context == NULL) { null_context = _talloc_named_const(NULL, 0, "null_context"); @@ -1494,7 +1874,7 @@ void talloc_enable_null_tracking_no_autofree(void) /* disable tracking of the NULL context */ -void talloc_disable_null_tracking(void) +_PUBLIC_ void talloc_disable_null_tracking(void) { if (null_context != NULL) { /* we have to move any children onto the real NULL @@ -1519,7 +1899,7 @@ void talloc_disable_null_tracking(void) /* enable leak reporting on exit */ -void talloc_enable_leak_report(void) +_PUBLIC_ void talloc_enable_leak_report(void) { talloc_enable_null_tracking(); atexit(talloc_report_null); @@ -1528,7 +1908,7 @@ void talloc_enable_leak_report(void) /* enable full leak reporting on exit */ -void talloc_enable_leak_report_full(void) +_PUBLIC_ void talloc_enable_leak_report_full(void) { talloc_enable_null_tracking(); atexit(talloc_report_null_full); @@ -1537,7 +1917,7 @@ void talloc_enable_leak_report_full(void) /* talloc and zero memory. */ -void *_talloc_zero(const void *ctx, size_t size, const char *name) +_PUBLIC_ void *_talloc_zero(const void *ctx, size_t size, const char *name) { void *p = _talloc_named_const(ctx, size, name); @@ -1551,7 +1931,7 @@ void *_talloc_zero(const void *ctx, size_t size, const char *name) /* memdup with a talloc. */ -void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name) +_PUBLIC_ void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name) { void *newp = _talloc_named_const(t, size, name); @@ -1579,7 +1959,7 @@ static inline char *__talloc_strlendup(const void *t, const char *p, size_t len) /* strdup with a talloc */ -char *talloc_strdup(const void *t, const char *p) +_PUBLIC_ char *talloc_strdup(const void *t, const char *p) { if (unlikely(!p)) return NULL; return __talloc_strlendup(t, p, strlen(p)); @@ -1588,7 +1968,7 @@ char *talloc_strdup(const void *t, const char *p) /* strndup with a talloc */ -char *talloc_strndup(const void *t, const char *p, size_t n) +_PUBLIC_ char *talloc_strndup(const void *t, const char *p, size_t n) { if (unlikely(!p)) return NULL; return __talloc_strlendup(t, p, strnlen(p, n)); @@ -1613,7 +1993,7 @@ static inline char *__talloc_strlendup_append(char *s, size_t slen, /* * Appends at the end of the string. */ -char *talloc_strdup_append(char *s, const char *a) +_PUBLIC_ char *talloc_strdup_append(char *s, const char *a) { if (unlikely(!s)) { return talloc_strdup(NULL, a); @@ -1630,7 +2010,7 @@ char *talloc_strdup_append(char *s, const char *a) * Appends at the end of the talloc'ed buffer, * not the end of the string. */ -char *talloc_strdup_append_buffer(char *s, const char *a) +_PUBLIC_ char *talloc_strdup_append_buffer(char *s, const char *a) { size_t slen; @@ -1653,7 +2033,7 @@ char *talloc_strdup_append_buffer(char *s, const char *a) /* * Appends at the end of the string. */ -char *talloc_strndup_append(char *s, const char *a, size_t n) +_PUBLIC_ char *talloc_strndup_append(char *s, const char *a, size_t n) { if (unlikely(!s)) { return talloc_strdup(NULL, a); @@ -1670,7 +2050,7 @@ char *talloc_strndup_append(char *s, const char *a, size_t n) * Appends at the end of the talloc'ed buffer, * not the end of the string. */ -char *talloc_strndup_append_buffer(char *s, const char *a, size_t n) +_PUBLIC_ char *talloc_strndup_append_buffer(char *s, const char *a, size_t n) { size_t slen; @@ -1690,7 +2070,7 @@ char *talloc_strndup_append_buffer(char *s, const char *a, size_t n) return __talloc_strlendup_append(s, slen, a, strnlen(a, n)); } -char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) +_PUBLIC_ char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) { int len; char *ret; @@ -1721,7 +2101,7 @@ char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) Perform string formatting, and return a pointer to newly allocated memory holding the result, inside a memory pool. */ -char *talloc_asprintf(const void *t, const char *fmt, ...) +_PUBLIC_ char *talloc_asprintf(const void *t, const char *fmt, ...) { va_list ap; char *ret; @@ -1774,7 +2154,7 @@ static inline char *__talloc_vaslenprintf_append(char *s, size_t slen, * accumulating output into a string buffer. Appends at the end * of the string. **/ -char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) +_PUBLIC_ char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) { if (unlikely(!s)) { return talloc_vasprintf(NULL, fmt, ap); @@ -1788,7 +2168,7 @@ char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) * and return @p s, which may have moved. Always appends at the * end of the talloc'ed buffer, not the end of the string. **/ -char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) +_PUBLIC_ char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) { size_t slen; @@ -1809,7 +2189,7 @@ char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) s, which may have moved. Good for gradually accumulating output into a string buffer. */ -char *talloc_asprintf_append(char *s, const char *fmt, ...) +_PUBLIC_ char *talloc_asprintf_append(char *s, const char *fmt, ...) { va_list ap; @@ -1824,7 +2204,7 @@ char *talloc_asprintf_append(char *s, const char *fmt, ...) s, which may have moved. Good for gradually accumulating output into a buffer. */ -char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) +_PUBLIC_ char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) { va_list ap; @@ -1837,7 +2217,7 @@ char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) /* alloc an array, checking for integer overflow in the array size */ -void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name) +_PUBLIC_ void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name) { if (count >= MAX_TALLOC_SIZE/el_size) { return NULL; @@ -1848,7 +2228,7 @@ void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char /* alloc an zero array, checking for integer overflow in the array size */ -void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name) +_PUBLIC_ void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name) { if (count >= MAX_TALLOC_SIZE/el_size) { return NULL; @@ -1859,7 +2239,7 @@ void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const /* realloc an array, checking for integer overflow in the array size */ -void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name) +_PUBLIC_ void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name) { if (count >= MAX_TALLOC_SIZE/el_size) { return NULL; @@ -1872,7 +2252,7 @@ void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned to libraries that want a realloc function (a realloc function encapsulates all the basic capabilities of an allocation library, which is why this is useful) */ -void *talloc_realloc_fn(const void *context, void *ptr, size_t size) +_PUBLIC_ void *talloc_realloc_fn(const void *context, void *ptr, size_t size) { return _talloc_realloc(context, ptr, size, NULL); } @@ -1893,7 +2273,7 @@ static void talloc_autofree(void) return a context which will be auto-freed on exit this is useful for reducing the noise in leak reports */ -void *talloc_autofree_context(void) +_PUBLIC_ void *talloc_autofree_context(void) { if (autofree_context == NULL) { autofree_context = _talloc_named_const(NULL, 0, "autofree_context"); @@ -1903,7 +2283,7 @@ void *talloc_autofree_context(void) return autofree_context; } -size_t talloc_get_size(const void *context) +_PUBLIC_ size_t talloc_get_size(const void *context) { struct talloc_chunk *tc; @@ -1922,7 +2302,7 @@ size_t talloc_get_size(const void *context) /* find a parent of this context that has the given name, if any */ -void *talloc_find_parent_byname(const void *context, const char *name) +_PUBLIC_ void *talloc_find_parent_byname(const void *context, const char *name) { struct talloc_chunk *tc; @@ -1946,7 +2326,7 @@ void *talloc_find_parent_byname(const void *context, const char *name) /* show the parentage of a context */ -void talloc_show_parents(const void *context, FILE *file) +_PUBLIC_ void talloc_show_parents(const void *context, FILE *file) { struct talloc_chunk *tc; @@ -1970,7 +2350,7 @@ void talloc_show_parents(const void *context, FILE *file) /* return 1 if ptr is a parent of context */ -int talloc_is_parent(const void *context, const void *ptr) +static int _talloc_is_parent(const void *context, const void *ptr, int depth) { struct talloc_chunk *tc; @@ -1979,12 +2359,21 @@ int talloc_is_parent(const void *context, const void *ptr) } tc = talloc_chunk_from_ptr(context); - while (tc) { + while (tc && depth > 0) { if (TC_PTR_FROM_CHUNK(tc) == ptr) return 1; while (tc && tc->prev) tc = tc->prev; if (tc) { tc = tc->parent; + depth--; } } return 0; } + +/* + return 1 if ptr is a parent of context +*/ +_PUBLIC_ int talloc_is_parent(const void *context, const void *ptr) +{ + return _talloc_is_parent(context, ptr, TALLOC_MAX_DEPTH); +}