From: Rusty Russell Date: Sat, 21 Nov 2009 02:59:25 +0000 (+1030) Subject: New talloc_set for auto-cleanup. X-Git-Url: https://git.ozlabs.org/?p=ccan;a=commitdiff_plain;h=76ae790f2cc2cb1e46bb0b9e5002c7feb6a79df1;hp=f88058f692a182c249f4d3f3a8879846297b50d0 New talloc_set for auto-cleanup. --- diff --git a/ccan/talloc/talloc.c b/ccan/talloc/talloc.c index 58921e17..ed958a8e 100644 --- a/ccan/talloc/talloc.c +++ b/ccan/talloc/talloc.c @@ -789,6 +789,35 @@ void *_talloc(const void *context, size_t size) return __talloc(context, size); } +static int talloc_destroy_pointer(void ***pptr) +{ + if ((uintptr_t)**pptr < getpagesize()) + TALLOC_ABORT("Double free or invalid talloc_set?"); + /* Invalidate pointer so it can't be used again. */ + **pptr = (void *)1; + return 0; +} + +void _talloc_set(void *ptr, const void *ctx, size_t size, const char *name) +{ + void ***child; + void **pptr = ptr; + + *pptr = talloc_named_const(ctx, size, name); + if (unlikely(!*pptr)) + return; + + child = talloc(*pptr, void **); + if (unlikely(!child)) { + talloc_free(*pptr); + *pptr = NULL; + return; + } + *child = pptr; + talloc_set_name_const(child, "talloc_set destructor"); + talloc_set_destructor(child, talloc_destroy_pointer); +} + /* externally callable talloc_set_name_const() */ diff --git a/ccan/talloc/talloc.h b/ccan/talloc/talloc.h index e38d05b8..54a70205 100644 --- a/ccan/talloc/talloc.h +++ b/ccan/talloc/talloc.h @@ -87,6 +87,30 @@ */ #define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type) +/** + * talloc_set - allocate dynamic memory for a type, into a pointer + * @ptr: pointer to the pointer to assign. + * @ctx: context to be parent of this allocation, or NULL. + * + * talloc_set() does a talloc, but also adds a destructor which will make the + * pointer invalid when it is freed. This can find many use-after-free bugs. + * + * Note that the destructor is chained off a zero-length allocation, and so + * is not affected by talloc_set_destructor(). + * + * Example: + * unsigned int *a; + * a = talloc(NULL, unsigned int); + * talloc_set(&b, a, unsigned int); + * talloc_free(a); + * *b = 1; // This will crash! + * + * See Also: + * talloc. + */ +#define talloc_set(pptr, ctx) \ + _talloc_set((pptr), (ctx), sizeof(&**(pptr)), __location__) + /** * talloc_free - free talloc'ed memory and its children * @ptr: the talloced pointer to free @@ -940,6 +964,7 @@ void *talloc_add_external(const void *ctx, /* The following definitions come from talloc.c */ void *_talloc(const void *context, size_t size); +void _talloc_set(void *ptr, const void *ctx, size_t size, const char *name); void _talloc_set_destructor(const void *ptr, int (*destructor)(void *)); size_t talloc_reference_count(const void *ptr); void *_talloc_reference(const void *context, const void *ptr); diff --git a/ccan/talloc/test/compile_fail-talloc_set.c b/ccan/talloc/test/compile_fail-talloc_set.c new file mode 100644 index 00000000..909076b7 --- /dev/null +++ b/ccan/talloc/test/compile_fail-talloc_set.c @@ -0,0 +1,15 @@ +#include "talloc/talloc.c" + +int main(void) +{ + int *p; + + talloc_set( +#ifdef FAIL + p +#else + &p +#endif + , NULL); + return 0; +} diff --git a/ccan/talloc/test/run-talloc_set.c b/ccan/talloc/test/run-talloc_set.c new file mode 100644 index 00000000..d7b49614 --- /dev/null +++ b/ccan/talloc/test/run-talloc_set.c @@ -0,0 +1,36 @@ +#include "talloc/talloc.c" +#include "tap/tap.h" +#include + +int main(void) +{ + char *c; + int *i; + + plan_tests(12); + + /* Set C to a valid pointer, with correct parent. */ + talloc_set(&c, NULL); + ok1(c >= (char *)(intptr_t)getpagesize()); + ok1(talloc_parent(c) == NULL); + + /* Free it, should blatt c. */ + talloc_free(c); + ok1(c); + ok1(c < (char *)(intptr_t)getpagesize()); + + /* Same test, indirect. */ + talloc_set(&i, NULL); + talloc_set(&c, i); + ok1(c >= (char *)(intptr_t)getpagesize()); + ok1(i >= (int *)(intptr_t)getpagesize()); + ok1(talloc_parent(i) == NULL); + ok1(talloc_parent(c) == i); + talloc_free(i); + ok1(c); + ok1(c < (char *)(intptr_t)getpagesize()); + ok1(i); + ok1(i < (int *)(intptr_t)getpagesize()); + + return exit_status(); +}