diff options
author | Jeremy Allison <jra@samba.org> | 2005-05-03 07:33:49 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 10:56:46 -0500 |
commit | 7b9d6ac23e1a7d8136fffd2e3977b09a815da65a (patch) | |
tree | c224433cb1cbc7ff17bd03aca1b564ebeefe15a2 | |
parent | 114067ced1d301a3c8b67aee0af0eab771d44dd4 (diff) | |
download | samba-7b9d6ac23e1a7d8136fffd2e3977b09a815da65a.tar.gz samba-7b9d6ac23e1a7d8136fffd2e3977b09a815da65a.tar.bz2 samba-7b9d6ac23e1a7d8136fffd2e3977b09a815da65a.zip |
r6595: This is Volkers new-talloc patch. Just got the go-ahead from
Volker to commit. Woo Hoo !
Jeremy.
(This used to be commit 316df944a456f150944761dab34add5e8c4ab699)
-rw-r--r-- | source3/include/smb_macros.h | 39 | ||||
-rw-r--r-- | source3/include/talloc.h | 131 | ||||
-rw-r--r-- | source3/lib/talloc.c | 1220 | ||||
-rw-r--r-- | source3/lib/tallocmsg.c | 11 | ||||
-rw-r--r-- | source3/lib/talloctort.c | 834 | ||||
-rw-r--r-- | source3/libads/ldap.c | 2 | ||||
-rw-r--r-- | source3/libsmb/libsmbclient.c | 2 | ||||
-rw-r--r-- | source3/param/loadparm.c | 2 | ||||
-rw-r--r-- | source3/passdb/secrets.c | 3 | ||||
-rw-r--r-- | source3/printing/notify.c | 6 | ||||
-rw-r--r-- | source3/rpc_parse/parse_prs.c | 6 | ||||
-rw-r--r-- | source3/rpc_server/srv_pipe_hnd.c | 7 | ||||
-rw-r--r-- | source3/smbd/server.c | 12 | ||||
-rw-r--r-- | source3/torture/cmd_vfs.c | 4 |
14 files changed, 1820 insertions, 459 deletions
diff --git a/source3/include/smb_macros.h b/source3/include/smb_macros.h index 37e5a897cb..53c4ad046d 100644 --- a/source3/include/smb_macros.h +++ b/source3/include/smb_macros.h @@ -276,24 +276,26 @@ copy an IP address from one buffer to another /* limiting size of ipc replies */ #define SMB_REALLOC_LIMIT(ptr,size) SMB_REALLOC(ptr,MAX((size),4*1024)) -/* #define PARANOID_MALLOC_CHECKER 1 */ +/* The new talloc is paranoid malloc checker safe. */ -#if defined(PARANOID_MALLOC_CHECKER) +#define TALLOC(ctx, size) talloc_named_const(ctx, size, __location__) +#define TALLOC_P(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type) +#define TALLOC_ARRAY(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type) +#define TALLOC_MEMDUP(ctx, ptr, size) _talloc_memdup(ctx, ptr, size, __location__) +#define TALLOC_ZERO(ctx, size) _talloc_zero(ctx, size, __location__) +#define TALLOC_ZERO_P(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type) +#define TALLOC_ZERO_ARRAY(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type) +#define TALLOC_REALLOC(ctx, ptr, count) _talloc_realloc(ctx, ptr, count, __location__) +#define TALLOC_REALLOC_ARRAY(ctx, ptr, type, count) (type *)_talloc_realloc_array(ctx, ptr, sizeof(type), count, #type) +#define talloc_destroy(ctx) talloc_free(ctx) + +#define PARANOID_MALLOC_CHECKER 1 -#define TALLOC(ctx, size) talloc_((ctx),(size)) -#define TALLOC_P(ctx, type) (type *)talloc_((ctx),sizeof(type)) -#define TALLOC_ARRAY(ctx, type, count) (type *)talloc_array_((ctx),sizeof(type),(count)) -#define TALLOC_MEMDUP(ctx, ptr, size) talloc_memdup_((ctx),(ptr),(size)) -#define TALLOC_ZERO(ctx, size) talloc_zero_((ctx),(size)) -#define TALLOC_ZERO_P(ctx, type) (type *)talloc_zero_((ctx),sizeof(type)) -#define TALLOC_ZERO_ARRAY(ctx, type, count) (type *)talloc_zero_array_((ctx),sizeof(type),(count)) -#define TALLOC_REALLOC(ctx, ptr, count) talloc_realloc_((ctx),(ptr),(count)) -#define TALLOC_REALLOC_ARRAY(ctx, ptr, type, count) (type *)talloc_realloc_array_((ctx),(ptr),sizeof(type),(count)) +#if defined(PARANOID_MALLOC_CHECKER) #define PRS_ALLOC_MEM(ps, type, count) (type *)prs_alloc_mem_((ps),sizeof(type),(count)) #define PRS_ALLOC_MEM_VOID(ps, size) prs_alloc_mem_((ps),(size),1) - /* Get medieval on our ass about malloc.... */ /* Restrictions on malloc/realloc/calloc. */ @@ -330,15 +332,10 @@ copy an IP address from one buffer to another #else -#define TALLOC(ctx, size) talloc((ctx),(size)) -#define TALLOC_P(ctx, type) (type *)talloc((ctx),sizeof(type)) -#define TALLOC_ARRAY(ctx, type, count) (type *)talloc_array((ctx),sizeof(type),(count)) -#define TALLOC_MEMDUP(ctx, ptr, size) talloc_memdup((ctx),(ptr),(size)) -#define TALLOC_ZERO(ctx, size) talloc_zero((ctx),(size)) -#define TALLOC_ZERO_P(ctx, type) (type *)talloc_zero((ctx),sizeof(type)) -#define TALLOC_ZERO_ARRAY(ctx, type, count) (type *)talloc_zero_array((ctx),sizeof(type),(count)) -#define TALLOC_REALLOC(ctx, ptr, count) talloc_realloc((ctx),(ptr),(count)) -#define TALLOC_REALLOC_ARRAY(ctx, ptr, type, count) (type *)talloc_realloc_array((ctx),(ptr),sizeof(type),(count)) +#define _STRING_LINE_(s) #s +#define _STRING_LINE2_(s) _STRING_LINE_(s) +#define __LINESTR__ _STRING_LINE2_(__LINE__) +#define __location__ __FILE__ ":" __LINESTR__ #define PRS_ALLOC_MEM(ps, type, count) (type *)prs_alloc_mem((ps),sizeof(type),(count)) #define PRS_ALLOC_MEM_VOID(ps, size) prs_alloc_mem((ps),(size),1) diff --git a/source3/include/talloc.h b/source3/include/talloc.h index 433b52ec95..a4f19d3587 100644 --- a/source3/include/talloc.h +++ b/source3/include/talloc.h @@ -3,8 +3,8 @@ /* Unix SMB/CIFS implementation. Samba temporary memory allocation functions - Copyright (C) Andrew Tridgell 2000 - Copyright (C) 2001 by Martin Pool <mbp@samba.org> + + Copyright (C) Andrew Tridgell 2004-2005 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,52 +21,107 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/** - * @ingroup talloc - * @{ - * @sa talloc.c - */ +/* this is only needed for compatibility with the old talloc */ +typedef void TALLOC_CTX; + +/* + this uses a little trick to allow __LINE__ to be stringified +*/ +#define _STRING_LINE_(s) #s +#define _STRING_LINE2_(s) _STRING_LINE_(s) +#define __LINESTR__ _STRING_LINE2_(__LINE__) +#define __location__ __FILE__ ":" __LINESTR__ + +#ifndef TALLOC_DEPRECATED +#define TALLOC_DEPRECATED 0 +#endif + +/* useful macros for creating type checked pointers */ +#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type) +#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__) + +#define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__) -/** - * talloc allocation pool. All allocated blocks can be freed in one go. - **/ +#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type) +#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__) -struct talloc_chunk { - struct talloc_chunk *next; - size_t size; - void *ptr; -}; +#define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type) +#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type) +#define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__) -struct talloc_ctx { - struct talloc_chunk *list; - size_t total_alloc_size; +#define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type) +#define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__) - /** The name recorded for this pool, if any. Should describe - * the purpose for which it was allocated. The string is - * allocated within the pool. **/ - char *name; +#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__) - /** Pointer to the next allocate talloc pool, so that we can - * summarize all talloc memory usage. **/ - struct talloc_ctx *next_ctx; -}; +#define malloc_p(type) (type *)malloc(sizeof(type)) +#define malloc_array_p(type, count) (type *)realloc_array(NULL, sizeof(type), count) +#define realloc_p(p, type, count) (type *)realloc_array(p, sizeof(type), count) -typedef struct talloc_ctx TALLOC_CTX; +#define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type) +#define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type) -TALLOC_CTX *talloc_init(char const *fmt, ...) PRINTF_ATTRIBUTE(1, 2); -char *talloc_vasprintf(TALLOC_CTX *t, const char *fmt, va_list ap) - PRINTF_ATTRIBUTE(2, 0); +#if TALLOC_DEPRECATED +#define talloc_zero_p(ctx, type) talloc_zero(ctx, type) +#define talloc_p(ctx, type) talloc(ctx, type) +#define talloc_array_p(ctx, type, count) talloc_array(ctx, type, count) +#define talloc_realloc_p(ctx, p, type, count) talloc_realloc(ctx, p, type, count) +#define talloc_destroy(ctx) talloc_free(ctx) +#endif -char *talloc_asprintf(TALLOC_CTX *t, const char *fmt, ...) - PRINTF_ATTRIBUTE(2, 3); +#ifndef PRINTF_ATTRIBUTE +#if (__GNUC__ >= 3) +/** Use gcc attribute to check printf fns. a1 is the 1-based index of + * the parameter containing the format, and a2 the index of the first + * argument. Note that some gcc 2.x versions don't handle this + * properly **/ +#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) +#else +#define PRINTF_ATTRIBUTE(a1, a2) +#endif +#endif -char *talloc_vasprintf_append(TALLOC_CTX *t, char *, const char *, va_list ap) - PRINTF_ATTRIBUTE(3, 0); -char *talloc_asprintf_append(TALLOC_CTX *t, char *, const char *, ...) - PRINTF_ATTRIBUTE(3, 4); +/* The following definitions come from talloc.c */ +void *_talloc(const void *context, size_t size); +void talloc_set_destructor(const void *ptr, int (*destructor)(void *)); +void talloc_increase_ref_count(const void *ptr); +void *talloc_reference(const void *context, const void *ptr); +int talloc_unlink(const void *context, void *ptr); +void talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +void talloc_set_name_const(const void *ptr, const char *name); +void *talloc_named(const void *context, size_t size, + const char *fmt, ...) PRINTF_ATTRIBUTE(3,4); +void *talloc_named_const(const void *context, size_t size, const char *name); +const char *talloc_get_name(const void *ptr); +void *talloc_check_name(const void *ptr, const char *name); +void talloc_report_depth(const void *ptr, FILE *f, int depth); +void *talloc_parent(const void *ptr); +void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2); +int talloc_free(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); +off_t talloc_total_size(const void *ptr); +off_t talloc_total_blocks(const void *ptr); +void talloc_report_full(const void *ptr, FILE *f); +void talloc_report(const void *ptr, FILE *f); +void talloc_enable_null_tracking(void); +void talloc_enable_leak_report(void); +void talloc_enable_leak_report_full(void); +void *_talloc_zero(const void *ctx, size_t size, const char *name); +void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name); +char *talloc_strdup(const void *t, const char *p); +char *talloc_strndup(const void *t, const char *p, size_t n); +char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); +char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +char *talloc_asprintf_append(char *s, + const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name); +void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name); +void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name); +void *talloc_realloc_fn(const void *context, void *ptr, size_t size); +void *talloc_autofree_context(void); -/** @} */ +#endif -#endif /* ndef _TALLOC_H_ */ diff --git a/source3/lib/talloc.c b/source3/lib/talloc.c index f5e21299b5..da3a2300b0 100644 --- a/source3/lib/talloc.c +++ b/source3/lib/talloc.c @@ -1,8 +1,11 @@ /* Samba Unix SMB/CIFS implementation. - Samba temporary memory allocation functions - Copyright (C) Andrew Tridgell 2000 - Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org> + + Samba trivial allocation library - new interface + + NOTE: Please read talloc_guide.txt for full documentation + + Copyright (C) Andrew Tridgell 2004 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,382 +22,914 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/** - @defgroup talloc Simple memory allocator - @{ - - This is a very simple temporary memory allocator. To use it do the following: +/* + inspired by http://swapped.cc/halloc/ +*/ + + +#ifdef _SAMBA_BUILD_ +#include "includes.h" +#else +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <stdint.h> +#include "talloc.h" +/* assume a modern system */ +#define HAVE_VA_COPY +#endif + +/* use this to force every realloc to change the pointer, to stress test + code that might not cope */ +#define ALWAYS_REALLOC 0 - 1) when you first want to allocate a pool of meomry use - talloc_init() and save the resulting context pointer somewhere - 2) to allocate memory use talloc() +#define MAX_TALLOC_SIZE 0x10000000 +#define TALLOC_MAGIC 0xe814ec4f +#define TALLOC_MAGIC_FREE 0x7faebef3 +#define TALLOC_MAGIC_REFERENCE ((const char *)1) - 3) when _all_ of the memory allocated using this context is no longer needed - use talloc_destroy() +/* by default we abort when given a bad pointer (such as when talloc_free() is called + on a pointer that came from malloc() */ +#ifndef TALLOC_ABORT +#define TALLOC_ABORT(reason) abort() +#endif - talloc does not zero the memory. It guarantees memory of a - TALLOC_ALIGN alignment +#ifndef discard_const_p +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) +# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr))) +#else +# define discard_const_p(type, ptr) ((type *)(ptr)) +#endif +#endif - @sa talloc.h +/* this null_context is only used if talloc_enable_leak_report() or + talloc_enable_leak_report_full() is called, otherwise it remains + NULL */ +static const void *null_context; +static void *cleanup_context; -/** - * @todo We could allocate both the talloc_chunk structure, and the - * memory it contains all in one allocation, which might be a bit - * faster and perhaps use less memory overhead. - * - * That smells like a premature optimization, though. -- mbp - **/ -/** - * If you want testing for memory corruption, link with dmalloc or use - * Insure++. It doesn't seem useful to duplicate them here. - **/ +struct talloc_reference_handle { + struct talloc_reference_handle *next, *prev; + void *ptr; +}; -#include "includes.h" +typedef int (*talloc_destructor_t)(void *); -/* Max allowable allococation - 256mb - 0x10000000 */ -#define MAX_TALLOC_SIZE (1024*1024*256) +struct talloc_chunk { + struct talloc_chunk *next, *prev; + struct talloc_chunk *parent, *child; + struct talloc_reference_handle *refs; + size_t size; + unsigned magic; + talloc_destructor_t destructor; + const char *name; +}; -/** - * Start of linked list of all talloc pools. - * - * @todo We should turn the global list off when using Insure++, - * otherwise all the memory will be seen as still reachable. - **/ -static TALLOC_CTX *list_head = NULL; +/* panic if we get a bad magic value */ +static struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr) +{ + struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, ptr)-1; + if (tc->magic != TALLOC_MAGIC) { + if (tc->magic == TALLOC_MAGIC_FREE) { + TALLOC_ABORT("Bad talloc magic value - double free"); + } else { + TALLOC_ABORT("Bad talloc magic value - unknown value"); + } + } + return tc; +} -/** - * Add to the global list - **/ -static void talloc_enroll(TALLOC_CTX *t) +/* hook into the front of the list */ +#define _TLIST_ADD(list, p) \ +do { \ + if (!(list)) { \ + (list) = (p); \ + (p)->next = (p)->prev = NULL; \ + } else { \ + (list)->prev = (p); \ + (p)->next = (list); \ + (p)->prev = NULL; \ + (list) = (p); \ + }\ +} while (0) + +/* remove an element from a list - element doesn't have to be in list. */ +#define _TLIST_REMOVE(list, p) \ +do { \ + if ((p) == (list)) { \ + (list) = (p)->next; \ + if (list) (list)->prev = NULL; \ + } else { \ + if ((p)->prev) (p)->prev->next = (p)->next; \ + if ((p)->next) (p)->next->prev = (p)->prev; \ + } \ + if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \ +} while (0) + + +/* + return the parent chunk of a pointer +*/ +static struct talloc_chunk *talloc_parent_chunk(const void *ptr) { - t->next_ctx = list_head; - list_head = t; + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + while (tc->prev) tc=tc->prev; + return tc->parent; } +void *talloc_parent(const void *ptr) +{ + struct talloc_chunk *tc = talloc_parent_chunk(ptr); + return (void *)(tc+1); +} -static void talloc_disenroll(TALLOC_CTX *t) +/* + Allocate a bit of memory as a child of an existing pointer +*/ +void *_talloc(const void *context, size_t size) { - TALLOC_CTX **ttmp; + struct talloc_chunk *tc; - /* Use a double-* so that no special case is required for the - * list head. */ - for (ttmp = &list_head; *ttmp; ttmp = &((*ttmp)->next_ctx)) - if (*ttmp == t) { - /* ttmp is the link that points to t, either - * list_head or the next_ctx link in its - * predecessor */ - *ttmp = t->next_ctx; - t->next_ctx = NULL; /* clobber */ - return; + if (context == NULL) { + context = null_context; + } + + if (size >= MAX_TALLOC_SIZE) { + return NULL; + } + + tc = malloc(sizeof(*tc)+size); + if (tc == NULL) return NULL; + + tc->size = size; + tc->magic = TALLOC_MAGIC; + tc->destructor = NULL; + tc->child = NULL; + tc->name = NULL; + tc->refs = NULL; + + if (context) { + struct talloc_chunk *parent = talloc_chunk_from_ptr(context); + + tc->parent = parent; + + if (parent->child) { + parent->child->parent = NULL; } - abort(); /* oops, this talloc was already - * clobbered or something else went - * wrong. */ + + _TLIST_ADD(parent->child, tc); + } else { + tc->next = tc->prev = tc->parent = NULL; + } + + return (void *)(tc+1); } -/** Create a new talloc context. **/ -static TALLOC_CTX *talloc_init_internal(void) +/* + setup a destructor to be called on free of a pointer + the destructor should return 0 on success, or -1 on failure. + 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 *)) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->destructor = destructor; +} + +/* + increase the reference count on a piece of memory. +*/ +void talloc_increase_ref_count(const void *ptr) { - TALLOC_CTX *t; + talloc_reference(null_context, ptr); +} - t = (TALLOC_CTX *)SMB_MALLOC(sizeof(TALLOC_CTX)); - if (t) { - t->list = NULL; - t->total_alloc_size = 0; - t->name = NULL; - talloc_enroll(t); +/* + helper for talloc_reference() +*/ +static int talloc_reference_destructor(void *ptr) +{ + struct talloc_reference_handle *handle = ptr; + struct talloc_chunk *tc1 = talloc_chunk_from_ptr(ptr); + struct talloc_chunk *tc2 = talloc_chunk_from_ptr(handle->ptr); + if (tc1->destructor != (talloc_destructor_t)-1) { + tc1->destructor = NULL; } + _TLIST_REMOVE(tc2->refs, handle); + talloc_free(handle); + return 0; +} + +/* + make a secondary reference to a pointer, hanging off the given context. + the pointer remains valid until both the original caller and this given + context are freed. + + the major use for this is when two different structures need to reference the + 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) +{ + struct talloc_chunk *tc; + struct talloc_reference_handle *handle; + if (ptr == NULL) return NULL; - return t; + tc = talloc_chunk_from_ptr(ptr); + handle = talloc_named_const(context, sizeof(*handle), TALLOC_MAGIC_REFERENCE); + + if (handle == NULL) return NULL; + + /* note that we hang the destructor off the handle, not the + main context as that allows the caller to still setup their + own destructor on the context if they want to */ + talloc_set_destructor(handle, talloc_reference_destructor); + handle->ptr = discard_const_p(void, ptr); + _TLIST_ADD(tc->refs, handle); + return handle->ptr; } +/* + remove a secondary reference to a pointer. This undo's what + talloc_reference() has done. The context and pointer arguments + must match those given to a talloc_reference() +*/ +static int talloc_unreference(const void *context, const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + struct talloc_reference_handle *h; + if (context == NULL) { + context = null_context; + } -/** - * Create a new talloc context, with a name specifying its purpose. - **/ + for (h=tc->refs;h;h=h->next) { + struct talloc_chunk *p = talloc_parent_chunk(h); + if ((p==NULL && context==NULL) || p+1 == context) break; + } + if (h == NULL) { + return -1; + } - TALLOC_CTX *talloc_init(char const *fmt, ...) + talloc_set_destructor(h, NULL); + _TLIST_REMOVE(tc->refs, h); + talloc_free(h); + return 0; +} + +/* + 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) { - TALLOC_CTX *t; - va_list ap; + struct talloc_chunk *tc_p, *new_p; + void *new_parent; + + if (ptr == NULL) { + return -1; + } - t = talloc_init_internal(); - if (t && fmt) { - /* - * t->name must not be talloced. - * as destroying the pool would destroy it. JRA. - */ - t->name = NULL; - va_start(ap, fmt); - vasprintf(&t->name, fmt, ap); - va_end(ap); - if (!t->name) { - talloc_destroy(t); - t = NULL; + if (context == NULL) { + context = null_context; + } + + if (talloc_unreference(context, ptr) == 0) { + return 0; + } + + if (context == NULL) { + if (talloc_parent_chunk(ptr) != NULL) { + return -1; + } + } else { + if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) { + return -1; } } - return t; + tc_p = talloc_chunk_from_ptr(ptr); + + if (tc_p->refs == NULL) { + return talloc_free(ptr); + } + + new_p = talloc_parent_chunk(tc_p->refs); + if (new_p) { + new_parent = new_p+1; + } else { + new_parent = NULL; + } + + if (talloc_unreference(new_parent, ptr) != 0) { + return -1; + } + + talloc_steal(new_parent, ptr); + + return 0; +} + +/* + add a name to an existing pointer - va_list version +*/ +static void talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); + +static void talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->name = talloc_vasprintf(ptr, fmt, ap); + if (tc->name) { + talloc_set_name_const(tc->name, ".name"); + } } +/* + add a name to an existing pointer +*/ +void talloc_set_name(const void *ptr, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + talloc_set_name_v(ptr, fmt, ap); + va_end(ap); +} -/** Allocate a bit of memory from the specified pool **/ -#if defined(PARANOID_MALLOC_CHECKER) -void *talloc_(TALLOC_CTX *t, size_t size) -#else -void *talloc(TALLOC_CTX *t, size_t size) -#endif +/* + more efficient way to add a name to a pointer - the name must point to a + true string constant +*/ +void talloc_set_name_const(const void *ptr, const char *name) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->name = name; +} + +/* + create a named talloc pointer. Any talloc pointer can be named, and + 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, ...) +{ + va_list ap; + void *ptr; + + ptr = _talloc(context, size); + if (ptr == NULL) return NULL; + + va_start(ap, fmt); + talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + + return ptr; +} + +/* + create a named talloc pointer. Any talloc pointer can be named, and + 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) +{ + void *ptr; + + ptr = _talloc(context, size); + if (ptr == NULL) { + return NULL; + } + + talloc_set_name_const(ptr, name); + + return ptr; +} + +/* + return the name of a talloc ptr, or "UNNAMED" +*/ +const char *talloc_get_name(const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + if (tc->name == TALLOC_MAGIC_REFERENCE) { + return ".reference"; + } + if (tc->name) { + return tc->name; + } + return "UNNAMED"; +} + + +/* + 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) +{ + const char *pname; + if (ptr == NULL) return NULL; + pname = talloc_get_name(ptr); + if (pname == name || strcmp(pname, name) == 0) { + return discard_const_p(void, ptr); + } + return NULL; +} + + +/* + this is for compatibility with older versions of talloc +*/ +void *talloc_init(const char *fmt, ...) +{ + va_list ap; + void *ptr; + + ptr = _talloc(NULL, 0); + if (ptr == NULL) return NULL; + + va_start(ap, fmt); + talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + + 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) { - void *p; struct talloc_chunk *tc; - if (!t || size == 0) return NULL; + if (ptr == NULL) { + return; + } - p = SMB_MALLOC(size); - if (p) { - tc = SMB_MALLOC(sizeof(*tc)); - if (tc) { - tc->ptr = p; - tc->size = size; - tc->next = t->list; - t->list = tc; - t->total_alloc_size += size; + 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->child+1; + const void *new_parent = null_context; + if (tc->child->refs) { + struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); + if (p) new_parent = p+1; } - else { - SAFE_FREE(p); + if (talloc_free(child) == -1) { + if (new_parent == null_context) { + struct talloc_chunk *p = talloc_parent_chunk(ptr); + if (p) new_parent = p+1; + } + talloc_steal(new_parent, child); } } - return p; } -/** Allocate an array of count elements of size x */ -#if defined(PARANOID_MALLOC_CHECKER) -void *talloc_array_(TALLOC_CTX *ctx, size_t el_size, unsigned int count) -#else -void *talloc_array(TALLOC_CTX *ctx, size_t el_size, unsigned int count) -#endif +/* + free a talloc pointer. This also frees all child pointers of this + pointer recursively + + return 0 if the memory is actually freed, otherwise -1. The memory + will not be freed if the ref_count is > 1 or the destructor (if + any) returns non-zero +*/ +int talloc_free(void *ptr) { - if (count >= MAX_TALLOC_SIZE/el_size) { - return NULL; - } - return TALLOC(ctx, el_size * count); + struct talloc_chunk *tc; + + if (ptr == NULL) { + return -1; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (tc->refs) { + talloc_reference_destructor(tc->refs); + return -1; + } + + if (tc->destructor) { + talloc_destructor_t d = tc->destructor; + if (d == (talloc_destructor_t)-1) { + return -1; + } + tc->destructor = (talloc_destructor_t)-1; + if (d(ptr) == -1) { + tc->destructor = d; + return -1; + } + tc->destructor = NULL; + } + + talloc_free_children(ptr); + + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->magic = TALLOC_MAGIC_FREE; + + free(tc); + return 0; } -/** A talloc version of realloc */ -#if defined(PARANOID_MALLOC_CHECKER) -void *talloc_realloc_(TALLOC_CTX *t, void *ptr, size_t size) -#else -void *talloc_realloc(TALLOC_CTX *t, void *ptr, size_t size) -#endif + + +/* + 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) { struct talloc_chunk *tc; void *new_ptr; /* size zero is equivalent to free() */ - if (!t || size == 0) + if (size == 0) { + talloc_free(ptr); return NULL; + } + + if (size >= MAX_TALLOC_SIZE) { + return NULL; + } /* realloc(NULL) is equavalent to malloc() */ - if (ptr == NULL) - return TALLOC(t, size); - - for (tc=t->list; tc; tc=tc->next) { - if (tc->ptr == ptr) { - new_ptr = SMB_REALLOC(ptr, size); - if (new_ptr) { - t->total_alloc_size += (size - tc->size); - tc->size = size; - tc->ptr = new_ptr; - } - return new_ptr; - } + if (ptr == NULL) { + return talloc_named_const(context, size, name); } - return NULL; -} -/** Re-allocate an array of count elements of size x */ -#if defined(PARANOID_MALLOC_CHECKER) -void *talloc_realloc_array_(TALLOC_CTX *ctx, void *ptr, size_t el_size, unsigned int count) + tc = talloc_chunk_from_ptr(ptr); + + /* don't allow realloc on referenced pointers */ + if (tc->refs) { + return NULL; + } + + /* by resetting magic we catch users of the old memory */ + tc->magic = TALLOC_MAGIC_FREE; + +#if ALWAYS_REALLOC + new_ptr = malloc(size + sizeof(*tc)); + if (new_ptr) { + memcpy(new_ptr, tc, tc->size + sizeof(*tc)); + free(tc); + } #else -void *talloc_realloc_array(TALLOC_CTX *ctx, void *ptr, size_t el_size, unsigned int count) + new_ptr = realloc(tc, size + sizeof(*tc)); #endif -{ - if (count >= MAX_TALLOC_SIZE/el_size) { - return NULL; - } - return TALLOC_REALLOC(ctx, ptr, el_size * count); + if (!new_ptr) { + tc->magic = TALLOC_MAGIC; + return NULL; + } + + tc = new_ptr; + tc->magic = TALLOC_MAGIC; + if (tc->parent) { + tc->parent->child = new_ptr; + } + if (tc->child) { + tc->child->parent = new_ptr; + } + + if (tc->prev) { + tc->prev->next = tc; + } + if (tc->next) { + tc->next->prev = tc; + } + + tc->size = size; + talloc_set_name_const(tc+1, name); + + return (void *)(tc+1); } -/** Destroy all the memory allocated inside @p t, but not @p t - * itself. */ -void talloc_destroy_pool(TALLOC_CTX *t) +/* + 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) { - struct talloc_chunk *c; - - if (!t) - return; + struct talloc_chunk *tc, *new_tc; + + if (!ptr) { + return NULL; + } - while (t->list) { - c = t->list->next; - SAFE_FREE(t->list->ptr); - SAFE_FREE(t->list); - t->list = c; + if (new_ctx == NULL) { + new_ctx = null_context; } - t->total_alloc_size = 0; + tc = talloc_chunk_from_ptr(ptr); + + if (new_ctx == NULL) { + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->parent = tc->next = tc->prev = NULL; + return discard_const_p(void, ptr); + } + + new_tc = talloc_chunk_from_ptr(new_ctx); + + if (tc == new_tc) { + return discard_const_p(void, ptr); + } + + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->parent = new_tc; + if (new_tc->child) new_tc->child->parent = NULL; + _TLIST_ADD(new_tc->child, tc); + + return discard_const_p(void, ptr); } -/** Destroy a whole pool including the context */ -void talloc_destroy(TALLOC_CTX *t) +/* + return the total size of a talloc pool (subtree) +*/ +off_t talloc_total_size(const void *ptr) { - if (!t) - return; + off_t total = 0; + struct talloc_chunk *c, *tc; + + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) { + return 0; + } + + tc = talloc_chunk_from_ptr(ptr); - talloc_destroy_pool(t); - talloc_disenroll(t); - SAFE_FREE(t->name); - memset(t, 0, sizeof(TALLOC_CTX)); - SAFE_FREE(t); + total = tc->size; + for (c=tc->child;c;c=c->next) { + total += talloc_total_size(c+1); + } + return total; } -/** Return the current total size of the pool. */ -size_t talloc_pool_size(TALLOC_CTX *t) +/* + return the total number of blocks in a talloc pool (subtree) +*/ +off_t talloc_total_blocks(const void *ptr) { - if (t) - return t->total_alloc_size; - else - return 0; + off_t total = 0; + struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr); + + total++; + for (c=tc->child;c;c=c->next) { + total += talloc_total_blocks(c+1); + } + return total; } -const char * talloc_pool_name(TALLOC_CTX const *t) +/* + return the number of external references to a pointer +*/ +static int talloc_reference_count(const void *ptr) { - if (t) - return t->name; - else - return NULL; + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + struct talloc_reference_handle *h; + int ret = 0; + + for (h=tc->refs;h;h=h->next) { + ret++; + } + return ret; } +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +void talloc_report_depth(const void *ptr, FILE *f, int depth) +{ + struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr); -/** talloc and zero memory. */ -#if defined(PARANOID_MALLOC_CHECKER) -void *talloc_zero_(TALLOC_CTX *t, size_t size) -#else -void *talloc_zero(TALLOC_CTX *t, size_t size) -#endif + for (c=tc->child;c;c=c->next) { + if (c->name == TALLOC_MAGIC_REFERENCE) { + struct talloc_reference_handle *handle = (void *)(c+1); + const char *name2 = talloc_get_name(handle->ptr); + fprintf(f, "%*sreference to: %s\n", depth*4, "", name2); + } else { + const char *name = talloc_get_name(c+1); + fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d)\n", + depth*4, "", + name, + (unsigned long)talloc_total_size(c+1), + (unsigned long)talloc_total_blocks(c+1), + talloc_reference_count(c+1)); + talloc_report_depth(c+1, f, depth+1); + } + } + +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +void talloc_report_full(const void *ptr, FILE *f) { - void *p = TALLOC(t, size); + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) return; - if (p) - memset(p, '\0', size); + fprintf(f,"full talloc report on '%s' (total %lu bytes in %lu blocks)\n", + talloc_get_name(ptr), + (unsigned long)talloc_total_size(ptr), + (unsigned long)talloc_total_blocks(ptr)); - return p; + talloc_report_depth(ptr, f, 1); + fflush(f); } -#if defined(PARANOID_MALLOC_CHECKER) -void *talloc_zero_array_(TALLOC_CTX *t, size_t el_size, unsigned int count) -#else -void *talloc_zero_array(TALLOC_CTX *t, size_t el_size, unsigned int count) -#endif +/* + report on memory usage by all children of a pointer +*/ +void talloc_report(const void *ptr, FILE *f) { -#if defined(PARANOID_MALLOC_CHECKER) - void *p = talloc_array_(t, el_size, count); -#else - void *p = talloc_array(t, el_size, count); -#endif + struct talloc_chunk *c, *tc; - if (p) - memset(p, '\0', el_size*count); + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) return; + + fprintf(f,"talloc report on '%s' (total %lu bytes in %lu blocks)\n", + talloc_get_name(ptr), + (unsigned long)talloc_total_size(ptr), + (unsigned long)talloc_total_blocks(ptr)); + + tc = talloc_chunk_from_ptr(ptr); + + for (c=tc->child;c;c=c->next) { + fprintf(f, "\t%-30s contains %6lu bytes in %3lu blocks\n", + talloc_get_name(c+1), + (unsigned long)talloc_total_size(c+1), + (unsigned long)talloc_total_blocks(c+1)); + } + fflush(f); +} - return p; +/* + report on any memory hanging off the null context +*/ +static void talloc_report_null(void) +{ + if (talloc_total_size(null_context) != 0) { + talloc_report(null_context, stderr); + } } -/** memdup with a talloc. */ -#if defined(PARANOID_MALLOC_CHECKER) -void *talloc_memdup_(TALLOC_CTX *t, const void *p, size_t size) -#else -void *talloc_memdup(TALLOC_CTX *t, const void *p, size_t size) -#endif +/* + report on any memory hanging off the null context +*/ +static void talloc_report_null_full(void) { - void *newp = TALLOC(t,size); + if (talloc_total_size(null_context) != 0) { + talloc_report_full(null_context, stderr); + } +} - if (newp) - memcpy(newp, p, size); +/* + enable tracking of the NULL context +*/ +void talloc_enable_null_tracking(void) +{ + if (null_context == NULL) { + null_context = talloc_named_const(NULL, 0, "null_context"); + } +} - return newp; +/* + enable leak reporting on exit +*/ +void talloc_enable_leak_report(void) +{ + talloc_enable_null_tracking(); + atexit(talloc_report_null); } -/** strdup with a talloc */ -char *talloc_strdup(TALLOC_CTX *t, const char *p) +/* + enable full leak reporting on exit +*/ +void talloc_enable_leak_report_full(void) { - if (p) - return TALLOC_MEMDUP(t, p, strlen(p) + 1); - else - return NULL; + talloc_enable_null_tracking(); + atexit(talloc_report_null_full); } -/* strndup with a talloc */ -char *talloc_strndup(TALLOC_CTX *mem_ctx, const char *str, size_t maxlen) +/* + talloc and zero memory. +*/ +void *_talloc_zero(const void *ctx, size_t size, const char *name) { - size_t len = strnlen(str, maxlen); - void *ret = TALLOC(mem_ctx, len+1); + void *p = talloc_named_const(ctx, size, name); - if (ret != NULL) { - memcpy(ret, str, len); - ((char *)ret)[len] = '\0'; + if (p) { + memset(p, '\0', size); } - return ret; + + return p; } -/** strdup_upper with a talloc */ -char *talloc_strdup_upper(TALLOC_CTX *t, const char *p) + +/* + memdup with a talloc. +*/ +void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name) { - char *r; - if (p) { - char *q = strdup_upper(p); - if (q) { - r = talloc_strdup(t, q); - SAFE_FREE(q); - return r; - } else { - return NULL; - } - } else { - return NULL; + void *newp = talloc_named_const(t, size, name); + + if (newp) { + memcpy(newp, p, size); } + + return newp; } -/** strdup_w with a talloc */ -smb_ucs2_t *talloc_strdup_w(TALLOC_CTX *t, const smb_ucs2_t *p) +/* + strdup with a talloc +*/ +char *talloc_strdup(const void *t, const char *p) { - if (p) - return TALLOC_MEMDUP(t, p, (strlen_w(p) + 1) * sizeof(smb_ucs2_t)); - else + char *ret; + if (!p) { return NULL; + } + ret = talloc_memdup(t, p, strlen(p) + 1); + if (ret) { + talloc_set_name_const(ret, ret); + } + return ret; } -/** - * Perform string formatting, and return a pointer to newly allocated - * memory holding the result, inside a memory pool. - **/ - char *talloc_asprintf(TALLOC_CTX *t, const char *fmt, ...) +/* + strndup with a talloc +*/ +char *talloc_strndup(const void *t, const char *p, size_t n) { - va_list ap; + size_t len; char *ret; - va_start(ap, fmt); - ret = talloc_vasprintf(t, fmt, ap); - va_end(ap); + for (len=0; p[len] && len<n; len++) ; + + ret = _talloc(t, len + 1); + if (!ret) { return NULL; } + memcpy(ret, p, len); + ret[len] = 0; + talloc_set_name_const(ret, ret); return ret; } +#ifndef VA_COPY +#ifdef HAVE_VA_COPY +#define VA_COPY(dest, src) va_copy(dest, src) +#elif defined(HAVE___VA_COPY) +#define VA_COPY(dest, src) __va_copy(dest, src) +#else +#define VA_COPY(dest, src) (dest) = (src) +#endif +#endif - char *talloc_vasprintf(TALLOC_CTX *t, const char *fmt, va_list ap) +char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) { int len; char *ret; @@ -404,139 +939,146 @@ smb_ucs2_t *talloc_strdup_w(TALLOC_CTX *t, const smb_ucs2_t *p) len = vsnprintf(NULL, 0, fmt, ap2); - ret = TALLOC(t, len+1); + ret = _talloc(t, len+1); if (ret) { VA_COPY(ap2, ap); vsnprintf(ret, len+1, fmt, ap2); + talloc_set_name_const(ret, ret); } return ret; } -/** - * Realloc @p s to append the formatted result of @p fmt and return @p - * s, which may have moved. Good for gradually accumulating output - * into a string buffer. - **/ - char *talloc_asprintf_append(TALLOC_CTX *t, char *s, - const char *fmt, ...) +/* + 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, ...) { va_list ap; + char *ret; va_start(ap, fmt); - s = talloc_vasprintf_append(t, s, fmt, ap); + ret = talloc_vasprintf(t, fmt, ap); va_end(ap); - return s; + return ret; } - /** * Realloc @p s to append the formatted result of @p fmt and @p ap, * and return @p s, which may have moved. Good for gradually * accumulating output into a string buffer. **/ - char *talloc_vasprintf_append(TALLOC_CTX *t, char *s, - const char *fmt, va_list ap) + +static char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); + +static char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) { + struct talloc_chunk *tc; int len, s_len; va_list ap2; + if (s == NULL) { + return talloc_vasprintf(NULL, fmt, ap); + } + + tc = talloc_chunk_from_ptr(s); + VA_COPY(ap2, ap); - s_len = strlen(s); + s_len = tc->size - 1; len = vsnprintf(NULL, 0, fmt, ap2); - s = TALLOC_REALLOC(t, s, s_len + len+1); + s = talloc_realloc(NULL, s, char, s_len + len+1); if (!s) return NULL; VA_COPY(ap2, ap); vsnprintf(s+s_len, len+1, fmt, ap2); + talloc_set_name_const(s, s); return s; } +/* + Realloc @p s to append the formatted result of @p fmt and return @p + s, which may have moved. Good for gradually accumulating output + into a string buffer. + */ +char *talloc_asprintf_append(char *s, const char *fmt, ...) +{ + va_list ap; -/** - * Return a human-readable description of all talloc memory usage. - * The result is allocated from @p t. - **/ -char *talloc_describe_all(TALLOC_CTX *rt) -{ - int n_pools = 0, total_chunks = 0; - size_t total_bytes = 0; - TALLOC_CTX *it; - char *s; - - if (!rt) return NULL; - - s = talloc_asprintf(rt, "global talloc allocations in pid: %u\n", - (unsigned) sys_getpid()); - s = talloc_asprintf_append(rt, s, "%-40s %8s %8s\n", - "name", "chunks", "bytes"); - s = talloc_asprintf_append(rt, s, "%-40s %8s %8s\n", - "----------------------------------------", - "--------", - "--------"); - - for (it = list_head; it; it = it->next_ctx) { - size_t bytes; - int n_chunks; - fstring what; - - n_pools++; - - talloc_get_allocation(it, &bytes, &n_chunks); + va_start(ap, fmt); + s = talloc_vasprintf_append(s, fmt, ap); + va_end(ap); + return s; +} - if (it->name) - fstrcpy(what, it->name); - else - slprintf(what, sizeof(what), "@%p", it); - - s = talloc_asprintf_append(rt, s, "%-40s %8u %8u\n", - what, - (unsigned) n_chunks, - (unsigned) bytes); - total_bytes += bytes; - total_chunks += n_chunks; +/* + 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) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; } + return talloc_named_const(ctx, el_size * count, name); +} - s = talloc_asprintf_append(rt, s, "%-40s %8s %8s\n", - "----------------------------------------", - "--------", - "--------"); +/* + 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) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_zero(ctx, el_size * count, name); +} - s = talloc_asprintf_append(rt, s, "%-40s %8u %8u\n", - "TOTAL", - (unsigned) total_chunks, (unsigned) total_bytes); - return s; +/* + 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) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_realloc(ctx, ptr, el_size * count, name); } +/* + a function version of talloc_realloc(), so it can be passed as a function pointer + 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) +{ + return _talloc_realloc(context, ptr, size, NULL); +} -/** - * Return an estimated memory usage for the specified pool. This does - * not include memory used by the underlying malloc implementation. - **/ -void talloc_get_allocation(TALLOC_CTX *t, - size_t *total_bytes, - int *n_chunks) +static void talloc_autofree(void) { - struct talloc_chunk *chunk; - - if (t) { - *total_bytes = 0; - *n_chunks = 0; + talloc_free(cleanup_context); + cleanup_context = NULL; +} - for (chunk = t->list; chunk; chunk = chunk->next) { - n_chunks[0]++; - *total_bytes += chunk->size; - } +/* + 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) +{ + if (cleanup_context == NULL) { + cleanup_context = talloc_named_const(NULL, 0, "autofree_context"); + atexit(talloc_autofree); } + return cleanup_context; } -/** @} */ diff --git a/source3/lib/tallocmsg.c b/source3/lib/tallocmsg.c index bbe1ee60a4..8ee3c257f6 100644 --- a/source3/lib/tallocmsg.c +++ b/source3/lib/tallocmsg.c @@ -33,19 +33,18 @@ void msg_pool_usage(int msg_type, pid_t src_pid, void *UNUSED(buf), size_t UNUSED(len)) { - char *reply; - TALLOC_CTX *reply_pool = talloc_init("msg_pool_usage"); + off_t reply; + fstring reply_str; SMB_ASSERT(msg_type == MSG_REQ_POOL_USAGE); DEBUG(2,("Got POOL_USAGE\n")); - reply = talloc_describe_all(reply_pool); + reply = talloc_total_size(NULL); + fstr_sprintf(reply_str, "%lld", reply); message_send_pid(src_pid, MSG_POOL_USAGE, - reply, strlen(reply)+1, True); - - talloc_destroy(reply_pool); + reply_str, strlen(reply_str)+1, True); } /** diff --git a/source3/lib/talloctort.c b/source3/lib/talloctort.c index 0cdf693bb9..967874917d 100644 --- a/source3/lib/talloctort.c +++ b/source3/lib/talloctort.c @@ -1,7 +1,9 @@ /* Unix SMB/CIFS implementation. - Samba temporary memory allocation functions -- torturer - Copyright (C) 2001 by Martin Pool <mbp@samba.org> + + local testing of talloc routines. + + Copyright (C) Andrew Tridgell 2004 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,48 +20,820 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#ifdef _SAMBA_BUILD_ #include "includes.h" +#else +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <sys/time.h> +#include <time.h> +#include "talloc.h" +#endif + +/* the test suite can be built standalone, or as part of Samba */ +#ifndef _SAMBA_BUILD_ +typedef enum {False=0,True=1} BOOL; + +static struct timeval timeval_current(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv; +} + +static double timeval_elapsed(struct timeval *tv) +{ + struct timeval tv2 = timeval_current(); + return (tv2.tv_sec - tv->tv_sec) + + (tv2.tv_usec - tv->tv_usec)*1.0e-6; +} +#endif /* _SAMBA_BUILD_ */ + + +#define CHECK_SIZE(ptr, tsize) do { \ + if (talloc_total_size(ptr) != (tsize)) { \ + printf(__location__ " failed: wrong '%s' tree size: got %u expected %u\n", \ + #ptr, \ + (unsigned)talloc_total_size(ptr), \ + (unsigned)tsize); \ + talloc_report_full(ptr, stdout); \ + return False; \ + } \ +} while (0) + +#define CHECK_BLOCKS(ptr, tblocks) do { \ + if (talloc_total_blocks(ptr) != (tblocks)) { \ + printf(__location__ " failed: wrong '%s' tree blocks: got %u expected %u\n", \ + #ptr, \ + (unsigned)talloc_total_blocks(ptr), \ + (unsigned)tblocks); \ + talloc_report_full(ptr, stdout); \ + return False; \ + } \ +} while (0) -#define NCTX 10 -#define NOBJ 20 -int main(void) +/* + test references +*/ +static BOOL test_ref1(void) { - int i; - TALLOC_CTX *ctx[NCTX]; + void *root, *p1, *p2, *ref, *r1; + + printf("TESTING SINGLE REFERENCE FREE\n"); + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + p2 = talloc_named_const(p1, 1, "p2"); + talloc_named_const(p1, 1, "x1"); + talloc_named_const(p1, 2, "x2"); + talloc_named_const(p1, 3, "x3"); + + r1 = talloc_named_const(root, 1, "r1"); + ref = talloc_reference(r1, p2); + talloc_report_full(root, stdout); - for (i = 0; i < NCTX; i++) { - ctx[i] = talloc_init("torture(%d)", i); + CHECK_BLOCKS(p1, 5); + CHECK_BLOCKS(p2, 1); + CHECK_BLOCKS(r1, 2); + + printf("Freeing p2\n"); + talloc_free(p2); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(p1, 5); + CHECK_BLOCKS(p2, 1); + CHECK_BLOCKS(r1, 1); + + printf("Freeing p1\n"); + talloc_free(p1); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(r1, 1); + + printf("Freeing r1\n"); + talloc_free(r1); + talloc_report_full(NULL, stdout); + + printf("Testing NULL\n"); + if (talloc_reference(root, NULL)) { + return False; } - for (i = 0; i < NCTX; i++) { - int j; - for (j = 0; j < NOBJ; j++) { - char *p; - size_t size = 1<<(i/3+j); + CHECK_BLOCKS(root, 1); + + CHECK_SIZE(root, 0); + + talloc_free(root); + + return True; +} + +/* + test references +*/ +static BOOL test_ref2(void) +{ + void *root, *p1, *p2, *ref, *r1; + + printf("TESTING DOUBLE REFERENCE FREE\n"); + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + talloc_named_const(p1, 1, "x1"); + talloc_named_const(p1, 1, "x2"); + talloc_named_const(p1, 1, "x3"); + p2 = talloc_named_const(p1, 1, "p2"); + + r1 = talloc_named_const(root, 1, "r1"); + ref = talloc_reference(r1, p2); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(p1, 5); + CHECK_BLOCKS(p2, 1); + CHECK_BLOCKS(r1, 2); + + printf("Freeing ref\n"); + talloc_free(ref); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(p1, 5); + CHECK_BLOCKS(p2, 1); + CHECK_BLOCKS(r1, 1); + + printf("Freeing p2\n"); + talloc_free(p2); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(p1, 4); + CHECK_BLOCKS(r1, 1); + + printf("Freeing p1\n"); + talloc_free(p1); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(r1, 1); + + printf("Freeing r1\n"); + talloc_free(r1); + talloc_report_full(root, stdout); + + CHECK_SIZE(root, 0); + + talloc_free(root); + + return True; +} + +/* + test references +*/ +static BOOL test_ref3(void) +{ + void *root, *p1, *p2, *ref, *r1; + + printf("TESTING PARENT REFERENCE FREE\n"); + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + p2 = talloc_named_const(root, 1, "p2"); + r1 = talloc_named_const(p1, 1, "r1"); + ref = talloc_reference(p2, r1); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(p1, 2); + CHECK_BLOCKS(p2, 2); + CHECK_BLOCKS(r1, 1); + + printf("Freeing p1\n"); + talloc_free(p1); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(p2, 2); + CHECK_BLOCKS(r1, 1); + + printf("Freeing p2\n"); + talloc_free(p2); + talloc_report_full(root, stdout); + + CHECK_SIZE(root, 0); + + talloc_free(root); + + return True; +} + +/* + test references +*/ +static BOOL test_ref4(void) +{ + void *root, *p1, *p2, *ref, *r1; + + printf("TESTING REFERRER REFERENCE FREE\n"); + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + talloc_named_const(p1, 1, "x1"); + talloc_named_const(p1, 1, "x2"); + talloc_named_const(p1, 1, "x3"); + p2 = talloc_named_const(p1, 1, "p2"); - p = talloc(ctx[i], size); - if (!p) { - fprintf(stderr, - "failed to talloc %.0f bytes\n", - (double) size); - exit(1); - } + r1 = talloc_named_const(root, 1, "r1"); + ref = talloc_reference(r1, p2); + talloc_report_full(root, stdout); - memset(p, 'A' + j, size); - } + CHECK_BLOCKS(p1, 5); + CHECK_BLOCKS(p2, 1); + CHECK_BLOCKS(r1, 2); + + printf("Freeing r1\n"); + talloc_free(r1); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(p1, 5); + CHECK_BLOCKS(p2, 1); + + printf("Freeing p2\n"); + talloc_free(p2); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(p1, 4); + + printf("Freeing p1\n"); + talloc_free(p1); + talloc_report_full(root, stdout); + + CHECK_SIZE(root, 0); + + talloc_free(root); + + return True; +} + + +/* + test references +*/ +static BOOL test_unlink1(void) +{ + void *root, *p1, *p2, *ref, *r1; + + printf("TESTING UNLINK\n"); + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + talloc_named_const(p1, 1, "x1"); + talloc_named_const(p1, 1, "x2"); + talloc_named_const(p1, 1, "x3"); + p2 = talloc_named_const(p1, 1, "p2"); + + r1 = talloc_named_const(p1, 1, "r1"); + ref = talloc_reference(r1, p2); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(p1, 7); + CHECK_BLOCKS(p2, 1); + CHECK_BLOCKS(r1, 2); + + printf("Unreferencing r1\n"); + talloc_unlink(r1, p2); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(p1, 6); + CHECK_BLOCKS(p2, 1); + CHECK_BLOCKS(r1, 1); + + printf("Freeing p1\n"); + talloc_free(p1); + talloc_report_full(root, stdout); + + CHECK_SIZE(root, 0); + + talloc_free(root); + + return True; +} + +static int fail_destructor(void *ptr) +{ + return -1; +} + +/* + miscellaneous tests to try to get a higher test coverage percentage +*/ +static BOOL test_misc(void) +{ + void *root, *p1; + char *p2; + double *d; + + printf("TESTING MISCELLANEOUS\n"); + + root = talloc_new(NULL); + + p1 = talloc_size(root, 0x7fffffff); + if (p1) { + printf("failed: large talloc allowed\n"); + return False; + } + + p1 = talloc_strdup(root, "foo"); + talloc_increase_ref_count(p1); + talloc_increase_ref_count(p1); + talloc_increase_ref_count(p1); + CHECK_BLOCKS(p1, 1); + CHECK_BLOCKS(root, 2); + talloc_free(p1); + CHECK_BLOCKS(p1, 1); + CHECK_BLOCKS(root, 2); + talloc_unlink(NULL, p1); + CHECK_BLOCKS(p1, 1); + CHECK_BLOCKS(root, 2); + p2 = talloc_strdup(p1, "foo"); + if (talloc_unlink(root, p2) != -1) { + printf("failed: talloc_unlink() of non-reference context should return -1\n"); + return False; + } + if (talloc_unlink(p1, p2) != 0) { + printf("failed: talloc_unlink() of parent should succeed\n"); + return False; + } + talloc_free(p1); + CHECK_BLOCKS(p1, 1); + CHECK_BLOCKS(root, 2); + + talloc_set_name(p1, "my name is %s", "foo"); + if (strcmp(talloc_get_name(p1), "my name is foo") != 0) { + printf("failed: wrong name after talloc_set_name\n"); + return False; + } + CHECK_BLOCKS(p1, 2); + CHECK_BLOCKS(root, 3); + + talloc_set_name_const(p1, NULL); + if (strcmp(talloc_get_name(p1), "UNNAMED") != 0) { + printf("failed: wrong name after talloc_set_name(NULL)\n"); + return False; + } + CHECK_BLOCKS(p1, 2); + CHECK_BLOCKS(root, 3); + + + if (talloc_free(NULL) != -1) { + printf("talloc_free(NULL) should give -1\n"); + return False; + } + + talloc_set_destructor(p1, fail_destructor); + if (talloc_free(p1) != -1) { + printf("Failed destructor should cause talloc_free to fail\n"); + return False; + } + talloc_set_destructor(p1, NULL); + + talloc_report(root, stdout); + + + p2 = talloc_zero_size(p1, 20); + if (p2[19] != 0) { + printf("Failed to give zero memory\n"); + return False; + } + talloc_free(p2); + + if (talloc_strdup(root, NULL) != NULL) { + printf("failed: strdup on NULL should give NULL\n"); + return False; } - for (i = 0; i < NCTX; i++) { - printf("talloc@%p %-40s %ldkB\n", ctx[i], - talloc_pool_name(ctx[i]), - (unsigned long)talloc_pool_size(ctx[i]) >> 10); + p2 = talloc_strndup(p1, "foo", 2); + if (strcmp("fo", p2) != 0) { + printf("failed: strndup doesn't work\n"); + return False; } + p2 = talloc_asprintf_append(p2, "o%c", 'd'); + if (strcmp("food", p2) != 0) { + printf("failed: talloc_asprintf_append doesn't work\n"); + return False; + } + CHECK_BLOCKS(p2, 1); + CHECK_BLOCKS(p1, 3); + + p2 = talloc_asprintf_append(NULL, "hello %s", "world"); + if (strcmp("hello world", p2) != 0) { + printf("failed: talloc_asprintf_append doesn't work\n"); + return False; + } + CHECK_BLOCKS(p2, 1); + CHECK_BLOCKS(p1, 3); + talloc_free(p2); + + d = talloc_array(p1, double, 0x20000000); + if (d) { + printf("failed: integer overflow not detected\n"); + return False; + } + + d = talloc_realloc(p1, d, double, 0x20000000); + if (d) { + printf("failed: integer overflow not detected\n"); + return False; + } + + talloc_free(p1); + CHECK_BLOCKS(root, 1); + + p1 = talloc_named(root, 100, "%d bytes", 100); + CHECK_BLOCKS(p1, 2); + CHECK_BLOCKS(root, 3); + talloc_unlink(root, p1); + + p1 = talloc_init("%d bytes", 200); + p2 = talloc_asprintf(p1, "my test '%s'", "string"); + CHECK_BLOCKS(p1, 3); + CHECK_SIZE(p2, 17); + CHECK_BLOCKS(root, 1); + talloc_unlink(NULL, p1); + + p1 = talloc_named_const(root, 10, "p1"); + p2 = talloc_named_const(root, 20, "p2"); + talloc_reference(p1, p2); + talloc_report_full(root, stdout); + talloc_unlink(root, p2); + talloc_report_full(root, stdout); + CHECK_BLOCKS(p2, 1); + CHECK_BLOCKS(p1, 2); + CHECK_BLOCKS(root, 3); + talloc_unlink(p1, p2); + talloc_unlink(root, p1); - printf("%s", talloc_describe_all(ctx[0])); + p1 = talloc_named_const(root, 10, "p1"); + p2 = talloc_named_const(root, 20, "p2"); + talloc_reference(NULL, p2); + talloc_report_full(root, stdout); + talloc_unlink(root, p2); + talloc_report_full(root, stdout); + CHECK_BLOCKS(p2, 1); + CHECK_BLOCKS(p1, 1); + CHECK_BLOCKS(root, 2); + talloc_unlink(NULL, p2); + talloc_unlink(root, p1); - for (i = NCTX - 1; i >= 0; i--) - talloc_destroy(ctx[i]); + /* Test that talloc_unlink is a no-op */ + if (talloc_unlink(root, NULL) != -1) { + printf("failed: talloc_unlink(root, NULL) == -1\n"); + return False; + } + + talloc_report(root, stdout); + talloc_report(NULL, stdout); + + CHECK_SIZE(root, 0); + + talloc_free(root); + + CHECK_SIZE(NULL, 0); + + talloc_enable_leak_report(); + talloc_enable_leak_report_full(); + + return True; +} + + +/* + test realloc +*/ +static BOOL test_realloc(void) +{ + void *root, *p1, *p2; + + printf("TESTING REALLOC\n"); + + root = talloc_new(NULL); + + p1 = talloc_size(root, 10); + CHECK_SIZE(p1, 10); + + p1 = talloc_realloc_size(NULL, p1, 20); + CHECK_SIZE(p1, 20); + + talloc_new(p1); + + p2 = talloc_realloc_size(p1, NULL, 30); + + talloc_new(p1); + + p2 = talloc_realloc_size(p1, p2, 40); + + CHECK_SIZE(p2, 40); + CHECK_SIZE(root, 60); + CHECK_BLOCKS(p1, 4); + + p1 = talloc_realloc_size(NULL, p1, 20); + CHECK_SIZE(p1, 60); + + talloc_increase_ref_count(p2); + if (talloc_realloc_size(NULL, p2, 5) != NULL) { + printf("failed: talloc_realloc() on a referenced pointer should fail\n"); + return False; + } + CHECK_BLOCKS(p1, 4); + + talloc_realloc_size(NULL, p2, 0); + talloc_realloc_size(NULL, p2, 0); + CHECK_BLOCKS(p1, 3); + + if (talloc_realloc_size(NULL, p1, 0x7fffffff) != NULL) { + printf("failed: oversize talloc should fail\n"); + return False; + } + + talloc_realloc_size(NULL, p1, 0); + + CHECK_BLOCKS(root, 1); + CHECK_SIZE(root, 0); + + talloc_free(root); + + return True; +} + + +/* + test realloc with a child +*/ +static BOOL test_realloc_child(void) +{ + void *root; + struct el1 { + int count; + struct el2 { + const char *name; + } **list, **list2, **list3; + } *el1; + struct el2 *el2; + + printf("TESTING REALLOC WITH CHILD\n"); + + root = talloc_new(NULL); + + el1 = talloc(root, struct el1); + el1->list = talloc(el1, struct el2 *); + el1->list[0] = talloc(el1->list, struct el2); + el1->list[0]->name = talloc_strdup(el1->list[0], "testing"); + + el1->list2 = talloc(el1, struct el2 *); + el1->list2[0] = talloc(el1->list2, struct el2); + el1->list2[0]->name = talloc_strdup(el1->list2[0], "testing2"); + + el1->list3 = talloc(el1, struct el2 *); + el1->list3[0] = talloc(el1->list3, struct el2); + el1->list3[0]->name = talloc_strdup(el1->list3[0], "testing2"); + + el2 = talloc(el1->list, struct el2); + el2 = talloc(el1->list2, struct el2); + el2 = talloc(el1->list3, struct el2); + + el1->list = talloc_realloc(el1, el1->list, struct el2 *, 100); + el1->list2 = talloc_realloc(el1, el1->list2, struct el2 *, 200); + el1->list3 = talloc_realloc(el1, el1->list3, struct el2 *, 300); + + talloc_free(root); + + return True; +} + + +/* + test type checking +*/ +static BOOL test_type(void) +{ + void *root; + struct el1 { + int count; + }; + struct el2 { + int count; + }; + struct el1 *el1; + + printf("TESTING talloc type checking\n"); + + root = talloc_new(NULL); + + el1 = talloc(root, struct el1); + + el1->count = 1; + + if (talloc_get_type(el1, struct el1) != el1) { + printf("type check failed on el1\n"); + return False; + } + if (talloc_get_type(el1, struct el2) != NULL) { + printf("type check failed on el1 with el2\n"); + return False; + } + talloc_set_type(el1, struct el2); + if (talloc_get_type(el1, struct el2) != (struct el2 *)el1) { + printf("type set failed on el1 with el2\n"); + return False; + } + + talloc_free(root); + + return True; +} + +/* + test steal +*/ +static BOOL test_steal(void) +{ + void *root, *p1, *p2; + + printf("TESTING STEAL\n"); + + root = talloc_new(NULL); + + p1 = talloc_array(root, char, 10); + CHECK_SIZE(p1, 10); + + p2 = talloc_realloc(root, NULL, char, 20); + CHECK_SIZE(p1, 10); + CHECK_SIZE(root, 30); + + if (talloc_steal(p1, NULL) != NULL) { + printf("failed: stealing NULL should give NULL\n"); + return False; + } + + if (talloc_steal(p1, p1) != p1) { + printf("failed: stealing to ourselves is a nop\n"); + return False; + } + CHECK_BLOCKS(root, 3); + CHECK_SIZE(root, 30); + + talloc_steal(NULL, p1); + talloc_steal(NULL, p2); + CHECK_BLOCKS(root, 1); + CHECK_SIZE(root, 0); + + talloc_free(p1); + talloc_steal(root, p2); + CHECK_BLOCKS(root, 2); + CHECK_SIZE(root, 20); + + talloc_free(p2); + + CHECK_BLOCKS(root, 1); + CHECK_SIZE(root, 0); + + talloc_free(root); + + p1 = talloc_size(NULL, 3); + CHECK_SIZE(NULL, 3); + talloc_free(p1); + + return True; +} + +/* + test talloc_realloc_fn +*/ +static BOOL test_realloc_fn(void) +{ + void *root, *p1; + + printf("TESTING talloc_realloc_fn\n"); + + root = talloc_new(NULL); + + p1 = talloc_realloc_fn(root, NULL, 10); + CHECK_BLOCKS(root, 2); + CHECK_SIZE(root, 10); + p1 = talloc_realloc_fn(root, p1, 20); + CHECK_BLOCKS(root, 2); + CHECK_SIZE(root, 20); + p1 = talloc_realloc_fn(root, p1, 0); + CHECK_BLOCKS(root, 1); + CHECK_SIZE(root, 0); + + talloc_free(root); + + + return True; +} + + +static BOOL test_unref_reparent(void) +{ + void *root, *p1, *p2, *c1; + + printf("TESTING UNREFERENCE AFTER PARENT FREED\n"); + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "orig parent"); + p2 = talloc_named_const(root, 1, "parent by reference"); + + c1 = talloc_named_const(p1, 1, "child"); + talloc_reference(p2, c1); + + talloc_free(p1); + talloc_unlink(p2, c1); + + CHECK_SIZE(root, 1); + + talloc_free(p2); + talloc_free(root); + + return True; +} + +/* + measure the speed of talloc versus malloc +*/ +static BOOL test_speed(void) +{ + void *ctx = talloc_new(NULL); + unsigned count; + struct timeval tv; + + printf("MEASURING TALLOC VS MALLOC SPEED\n"); + + tv = timeval_current(); + count = 0; + do { + void *p1, *p2, *p3; + p1 = talloc_size(ctx, count); + p2 = talloc_strdup(p1, "foo bar"); + p3 = talloc_size(p1, 300); + talloc_free(p1); + count += 3; + } while (timeval_elapsed(&tv) < 5.0); + + printf("talloc: %.0f ops/sec\n", count/timeval_elapsed(&tv)); + + talloc_free(ctx); + + tv = timeval_current(); + count = 0; + do { + void *p1, *p2, *p3; + p1 = malloc(count); + p2 = strdup("foo bar"); + p3 = malloc(300); + free(p1); + free(p2); + free(p3); + count += 3; + } while (timeval_elapsed(&tv) < 5.0); + + printf("malloc: %.0f ops/sec\n", count/timeval_elapsed(&tv)); + + return True; +} + + +BOOL torture_local_talloc(void) +{ + BOOL ret = True; + + ret &= test_ref1(); + ret &= test_ref2(); + ret &= test_ref3(); + ret &= test_ref4(); + ret &= test_unlink1(); + ret &= test_misc(); + ret &= test_realloc(); + ret &= test_realloc_child(); + ret &= test_steal(); + ret &= test_unref_reparent(); + ret &= test_realloc_fn(); + ret &= test_type(); + if (ret) { + ret &= test_speed(); + } + + return ret; +} + + + +#ifndef _SAMBA_BUILD_ + int main(void) +{ + if (!torture_local_talloc()) { + printf("ERROR: TESTSUIE FAILED\n"); + return -1; + } return 0; } +#endif diff --git a/source3/libads/ldap.c b/source3/libads/ldap.c index 7a59da5a6d..04754f4e9e 100644 --- a/source3/libads/ldap.c +++ b/source3/libads/ldap.c @@ -1703,7 +1703,7 @@ void ads_process_results(ADS_STRUCT *ads, void *res, ldap_memfree(utf8_field); } ber_free(b, 0); - talloc_destroy_pool(ctx); + talloc_free_children(ctx); fn(NULL, NULL, data_area); /* completed an entry */ } diff --git a/source3/libsmb/libsmbclient.c b/source3/libsmb/libsmbclient.c index 791427f249..ee7516630b 100644 --- a/source3/libsmb/libsmbclient.c +++ b/source3/libsmb/libsmbclient.c @@ -3303,7 +3303,7 @@ static DOS_ATTR_DESC *dos_attr_query(SMBCCTX *context, SMB_INO_T inode = 0; DOS_ATTR_DESC *ret; - ret = talloc(ctx, sizeof(DOS_ATTR_DESC)); + ret = TALLOC_P(ctx, DOS_ATTR_DESC); if (!ret) { errno = ENOMEM; return NULL; diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index 06070b0700..14bec052b8 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -1603,7 +1603,7 @@ void lp_talloc_free(void) { if (!lp_talloc) return; - talloc_destroy(lp_talloc); + talloc_free(lp_talloc); lp_talloc = NULL; } diff --git a/source3/passdb/secrets.c b/source3/passdb/secrets.c index c7ff2f80b0..d1dcc694e8 100644 --- a/source3/passdb/secrets.c +++ b/source3/passdb/secrets.c @@ -640,7 +640,8 @@ NTSTATUS secrets_get_trusted_domains(TALLOC_CTX* ctx, int* enum_ctx, unsigned in memcpy(&(dom->sid), &(pass->domain_sid), sizeof(dom->sid)); /* copy unicode domain name */ - dom->name = talloc_strdup_w(ctx, pass->uni_name); + dom->name = TALLOC_MEMDUP(ctx, pass->uni_name, + (strlen_w(pass->uni_name) + 1) * sizeof(smb_ucs2_t)); (*domains)[idx - start_idx] = dom; diff --git a/source3/printing/notify.c b/source3/printing/notify.c index 0071d3d8c0..e289eba1b6 100644 --- a/source3/printing/notify.c +++ b/source3/printing/notify.c @@ -130,7 +130,7 @@ static void print_notify_send_messages_to_printer(const char *printer, unsigned if (strequal(printer, pq->msg->printer)) { if (!flatten_message(pq)) { DEBUG(0,("print_notify_send_messages: Out of memory\n")); - talloc_destroy_pool(send_ctx); + talloc_free_children(send_ctx); num_messages = 0; return; } @@ -143,7 +143,7 @@ static void print_notify_send_messages_to_printer(const char *printer, unsigned buf = TALLOC(send_ctx, offset); if (!buf) { DEBUG(0,("print_notify_send_messages: Out of memory\n")); - talloc_destroy_pool(send_ctx); + talloc_free_children(send_ctx); num_messages = 0; return; } @@ -201,7 +201,7 @@ void print_notify_send_messages(unsigned int timeout) while (print_notify_messages_pending()) print_notify_send_messages_to_printer(notify_queue_head->msg->printer, timeout); - talloc_destroy_pool(send_ctx); + talloc_free_children(send_ctx); num_messages = 0; } diff --git a/source3/rpc_parse/parse_prs.c b/source3/rpc_parse/parse_prs.c index 1b9ac51c61..cccad9ad55 100644 --- a/source3/rpc_parse/parse_prs.c +++ b/source3/rpc_parse/parse_prs.c @@ -153,11 +153,7 @@ char *prs_alloc_mem(prs_struct *ps, size_t size, unsigned int count) if (size) { /* We can't call the type-safe version here. */ -#if defined(PARANOID_MALLOC_CHECKER) - ret = talloc_zero_array_(ps->mem_ctx, size, count); -#else - ret = talloc_zero_array(ps->mem_ctx, size, count); -#endif + ret = _talloc_zero_array(ps->mem_ctx, size, count, "parse_prs"); } return ret; } diff --git a/source3/rpc_server/srv_pipe_hnd.c b/source3/rpc_server/srv_pipe_hnd.c index 8720a4df68..83b78f8d2f 100644 --- a/source3/rpc_server/srv_pipe_hnd.c +++ b/source3/rpc_server/srv_pipe_hnd.c @@ -536,11 +536,12 @@ static ssize_t unmarshall_rpc_header(pipes_struct *p) a complete PDU. ****************************************************************************/ -void free_pipe_context(pipes_struct *p) +static void free_pipe_context(pipes_struct *p) { if (p->mem_ctx) { - DEBUG(3,("free_pipe_context: destroying talloc pool of size %lu\n", (unsigned long)talloc_pool_size(p->mem_ctx) )); - talloc_destroy_pool(p->mem_ctx); + DEBUG(3,("free_pipe_context: destroying talloc pool of size " + "%llu\n", talloc_total_size(p->mem_ctx) )); + talloc_free_children(p->mem_ctx); } else { p->mem_ctx = talloc_init("pipe %s %p", p->name, p); if (p->mem_ctx == NULL) diff --git a/source3/smbd/server.c b/source3/smbd/server.c index 9d910cd14a..301534d750 100644 --- a/source3/smbd/server.c +++ b/source3/smbd/server.c @@ -747,6 +747,10 @@ void build_options(BOOL screen); log_stdout = True; } + if (interactive && (DEBUGLEVEL >= 9)) { + talloc_enable_leak_report(); + } + if (log_stdout && Fork) { DEBUG(0,("ERROR: Can't log to stdout (-S) unless daemon is in foreground (-F) or interactive (-i)\n")); exit(1); @@ -952,14 +956,6 @@ void build_options(BOOL screen); namecache_shutdown(); - if (interactive) { - TALLOC_CTX *mem_ctx = talloc_init("end_description"); - char *description = talloc_describe_all(mem_ctx); - - DEBUG(3, ("tallocs left:\n%s\n", description)); - talloc_destroy(mem_ctx); - } - exit_server("normal exit"); return(0); } diff --git a/source3/torture/cmd_vfs.c b/source3/torture/cmd_vfs.c index 04c40db72e..f8eb56c50a 100644 --- a/source3/torture/cmd_vfs.c +++ b/source3/torture/cmd_vfs.c @@ -54,7 +54,7 @@ static NTSTATUS cmd_populate(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int arg } c = argv[1][0]; size = atoi(argv[2]); - vfs->data = (char *)talloc(mem_ctx, size); + vfs->data = TALLOC_ARRAY(mem_ctx, char, size); if (vfs->data == NULL) { printf("populate: error=-1 (not enough memory)"); return NT_STATUS_UNSUCCESSFUL; @@ -364,7 +364,7 @@ static NTSTATUS cmd_read(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, c /* do some error checking on these */ fd = atoi(argv[1]); size = atoi(argv[2]); - vfs->data = (char *)talloc(mem_ctx, size); + vfs->data = TALLOC_ARRAY(mem_ctx, char, size); if (vfs->data == NULL) { printf("read: error=-1 (not enough memory)"); return NT_STATUS_UNSUCCESSFUL; |