#include "replace.h"
#include "talloc.h"
-#ifdef TALLOC_BUILD_VERSION_MAJOR
+#ifdef HAVE_SYS_AUXV_H
+#include <sys/auxv.h>
+#endif
+
#if (TALLOC_VERSION_MAJOR != TALLOC_BUILD_VERSION_MAJOR)
#error "TALLOC_VERSION_MAJOR != TALLOC_BUILD_VERSION_MAJOR"
#endif
-#endif
-#ifdef TALLOC_BUILD_VERSION_MINOR
#if (TALLOC_VERSION_MINOR != TALLOC_BUILD_VERSION_MINOR)
#error "TALLOC_VERSION_MINOR != TALLOC_BUILD_VERSION_MINOR"
#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 */
#define MAX_TALLOC_SIZE 0x10000000
-#define TALLOC_MAGIC_BASE 0xe814ec70
-#define TALLOC_MAGIC ( \
- TALLOC_MAGIC_BASE + \
- (TALLOC_VERSION_MAJOR << 12) + \
- (TALLOC_VERSION_MINOR << 4) \
-)
#define TALLOC_FLAG_FREE 0x01
#define TALLOC_FLAG_LOOP 0x02
#define TALLOC_FLAG_POOL 0x04 /* This is a talloc pool */
#define TALLOC_FLAG_POOLMEM 0x08 /* This is allocated in a pool */
+/*
+ * Bits above this are random, used to make it harder to fake talloc
+ * headers during an attack. Try not to change this without good reason.
+ */
+#define TALLOC_FLAG_MASK 0x0F
+
#define TALLOC_MAGIC_REFERENCE ((const char *)1)
+#define TALLOC_MAGIC_BASE 0xe814ec70
+static unsigned int talloc_magic = (
+ ~TALLOC_FLAG_MASK & (
+ TALLOC_MAGIC_BASE +
+ (TALLOC_BUILD_VERSION_MAJOR << 24) +
+ (TALLOC_BUILD_VERSION_MINOR << 16) +
+ (TALLOC_BUILD_VERSION_RELEASE << 8)));
+
/* 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
size_t size);
static inline void talloc_memlimit_shrink(struct talloc_memlimit *limit,
size_t size);
-static inline void talloc_memlimit_update_on_free(struct talloc_chunk *tc);
+static inline void tc_memlimit_update_on_free(struct talloc_chunk *tc);
-static inline void _talloc_set_name_const(const void *ptr, const char *name);
+static inline void _tc_set_name_const(struct talloc_chunk *tc,
+ const char *name);
+static struct talloc_chunk *_vasprintf_tc(const void *t,
+ const char *fmt,
+ va_list ap);
typedef int (*talloc_destructor_t)(void *);
struct talloc_pool_hdr;
struct talloc_chunk {
+ /*
+ * flags includes the talloc magic, which is randomised to
+ * make overwrite attacks harder
+ */
+ unsigned flags;
+
+ /*
+ * If you have a logical tree like:
+ *
+ * <parent>
+ * / | \
+ * / | \
+ * / | \
+ * <child 1> <child 2> <child 3>
+ *
+ * The actual talloc tree is:
+ *
+ * <parent>
+ * |
+ * <child 1> - <child 2> - <child 3>
+ *
+ * The children are linked with next/prev pointers, and
+ * child 1 is linked to the parent with parent/child
+ * pointers.
+ */
+
struct talloc_chunk *next, *prev;
struct talloc_chunk *parent, *child;
struct talloc_reference_handle *refs;
talloc_destructor_t destructor;
const char *name;
size_t size;
- unsigned flags;
/*
* limit semantics:
return TALLOC_VERSION_MINOR;
}
+_PUBLIC_ int talloc_test_get_magic(void)
+{
+ return talloc_magic;
+}
+
static void (*talloc_log_fn)(const char *message);
_PUBLIC_ void talloc_set_log_fn(void (*log_fn)(const char *message))
talloc_log_fn = log_fn;
}
+#ifdef HAVE_CONSTRUCTOR_ATTRIBUTE
+void talloc_lib_init(void) __attribute__((constructor));
+void talloc_lib_init(void)
+{
+ uint32_t random_value;
+#if defined(HAVE_GETAUXVAL) && defined(AT_RANDOM)
+ uint8_t *p;
+ /*
+ * Use the kernel-provided random values used for
+ * ASLR. This won't change per-exec, which is ideal for us
+ */
+ p = (uint8_t *) getauxval(AT_RANDOM);
+ if (p) {
+ /*
+ * We get 16 bytes from getauxval. By calling rand(),
+ * a totally insecure PRNG, but one that will
+ * deterministically have a different value when called
+ * twice, we ensure that if two talloc-like libraries
+ * are somehow loaded in the same address space, that
+ * because we choose different bytes, we will keep the
+ * protection against collision of multiple talloc
+ * libs.
+ *
+ * This protection is important because the effects of
+ * passing a talloc pointer from one to the other may
+ * be very hard to determine.
+ */
+ int offset = rand() % (16 - sizeof(random_value));
+ memcpy(&random_value, p + offset, sizeof(random_value));
+ } else
+#endif
+ {
+ /*
+ * Otherwise, hope the location we are loaded in
+ * memory is randomised by someone else
+ */
+ random_value = ((uintptr_t)talloc_lib_init & 0xFFFFFFFF);
+ }
+ talloc_magic = random_value & ~TALLOC_FLAG_MASK;
+}
+#else
+#warning "No __attribute__((constructor)) support found on this platform, additional talloc security measures not available"
+#endif
+
static void talloc_log(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2);
static void talloc_log(const char *fmt, ...)
{
static void talloc_abort_magic(unsigned magic)
{
- unsigned striped = magic - TALLOC_MAGIC_BASE;
- unsigned major = (striped & 0xFFFFF000) >> 12;
- unsigned minor = (striped & 0x00000FF0) >> 4;
- talloc_log("Bad talloc magic[0x%08X/%u/%u] expected[0x%08X/%u/%u]\n",
- magic, major, minor,
- TALLOC_MAGIC, TALLOC_VERSION_MAJOR, TALLOC_VERSION_MINOR);
talloc_abort("Bad talloc magic value - wrong talloc version used/mixed");
}
{
const char *pp = (const char *)ptr;
struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE);
- if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) {
- if ((tc->flags & (~0xFFF)) == TALLOC_MAGIC_BASE) {
- talloc_abort_magic(tc->flags & (~0xF));
+ if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~TALLOC_FLAG_MASK)) != talloc_magic)) {
+ if ((tc->flags & (~TALLOC_FLAG_MASK)) == talloc_magic) {
+ talloc_abort_magic(tc->flags & (~TALLOC_FLAG_MASK));
return NULL;
}
Allocate from a pool
*/
-static inline struct talloc_chunk *talloc_alloc_pool(struct talloc_chunk *parent,
+static inline struct talloc_chunk *tc_alloc_pool(struct talloc_chunk *parent,
size_t size, size_t prefix_len)
{
struct talloc_pool_hdr *pool_hdr = NULL;
pool_hdr->end = (void *)((char *)pool_hdr->end + chunk_size);
- result->flags = TALLOC_MAGIC | TALLOC_FLAG_POOLMEM;
+ result->flags = talloc_magic | TALLOC_FLAG_POOLMEM;
result->pool = pool_hdr;
pool_hdr->object_count++;
/*
Allocate a bit of memory as a child of an existing pointer
*/
-static inline void *__talloc_with_prefix(const void *context, size_t size,
- size_t prefix_len)
+static inline void *__talloc_with_prefix(const void *context,
+ size_t size,
+ size_t prefix_len,
+ struct talloc_chunk **tc_ret)
{
struct talloc_chunk *tc = NULL;
struct talloc_memlimit *limit = NULL;
size_t total_len = TC_HDR_SIZE + size + prefix_len;
+ struct talloc_chunk *parent = NULL;
if (unlikely(context == NULL)) {
context = null_context;
return NULL;
}
- if (context != NULL) {
- struct talloc_chunk *ptc = talloc_chunk_from_ptr(context);
+ if (likely(context != NULL)) {
+ parent = talloc_chunk_from_ptr(context);
- if (ptc->limit != NULL) {
- limit = ptc->limit;
+ if (parent->limit != NULL) {
+ limit = parent->limit;
}
- tc = talloc_alloc_pool(ptc, TC_HDR_SIZE+size, prefix_len);
+ tc = tc_alloc_pool(parent, TC_HDR_SIZE+size, prefix_len);
}
if (tc == NULL) {
return NULL;
}
tc = (struct talloc_chunk *)(ptr + prefix_len);
- tc->flags = TALLOC_MAGIC;
+ tc->flags = talloc_magic;
tc->pool = NULL;
talloc_memlimit_grow(limit, total_len);
tc->name = NULL;
tc->refs = NULL;
- if (likely(context)) {
- struct talloc_chunk *parent = talloc_chunk_from_ptr(context);
-
+ if (likely(context != NULL)) {
if (parent->child) {
parent->child->parent = NULL;
tc->next = parent->child;
tc->next = tc->prev = tc->parent = NULL;
}
+ *tc_ret = tc;
return TC_PTR_FROM_CHUNK(tc);
}
-static inline void *__talloc(const void *context, size_t size)
+static inline void *__talloc(const void *context,
+ size_t size,
+ struct talloc_chunk **tc)
{
- return __talloc_with_prefix(context, size, 0);
+ return __talloc_with_prefix(context, size, 0, tc);
}
/*
struct talloc_pool_hdr *pool_hdr;
void *result;
- result = __talloc_with_prefix(context, size, TP_HDR_SIZE);
+ result = __talloc_with_prefix(context, size, TP_HDR_SIZE, &tc);
if (unlikely(result == NULL)) {
return NULL;
}
- tc = talloc_chunk_from_ptr(result);
pool_hdr = talloc_pool_from_chunk(tc);
tc->flags |= TALLOC_FLAG_POOL;
pool_hdr->end = ((char *)pool_hdr->end + TC_ALIGN16(type_size));
- _talloc_set_name_const(ret, type_name);
+ _tc_set_name_const(tc, type_name);
return ret;
overflow:
more efficient way to add a name to a pointer - the name must point to a
true string constant
*/
-static inline void _talloc_set_name_const(const void *ptr, const char *name)
+static inline void _tc_set_name_const(struct talloc_chunk *tc,
+ const char *name)
{
- struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
tc->name = name;
}
static inline void *_talloc_named_const(const void *context, size_t size, const char *name)
{
void *ptr;
+ struct talloc_chunk *tc;
- ptr = __talloc(context, size);
+ ptr = __talloc(context, size, &tc);
if (unlikely(ptr == NULL)) {
return NULL;
}
- _talloc_set_name_const(ptr, name);
+ _tc_set_name_const(tc, name);
return ptr;
}
static void *_talloc_steal_internal(const void *new_ctx, const void *ptr);
-static inline void _talloc_free_poolmem(struct talloc_chunk *tc,
+static inline void _tc_free_poolmem(struct talloc_chunk *tc,
const char *location)
{
struct talloc_pool_hdr *pool;
pool_tc->name = location;
if (pool_tc->flags & TALLOC_FLAG_POOLMEM) {
- _talloc_free_poolmem(pool_tc, location);
+ _tc_free_poolmem(pool_tc, location);
} else {
/*
- * The talloc_memlimit_update_on_free()
+ * The tc_memlimit_update_on_free()
* call takes into account the
* prefix TP_HDR_SIZE allocated before
* the pool talloc_chunk.
*/
- talloc_memlimit_update_on_free(pool_tc);
+ tc_memlimit_update_on_free(pool_tc);
TC_INVALIDATE_FULL_CHUNK(pool_tc);
free(pool);
}
*/
}
-static inline void _talloc_free_children_internal(struct talloc_chunk *tc,
+static inline void _tc_free_children_internal(struct talloc_chunk *tc,
void *ptr,
const char *location);
+static inline int _talloc_free_internal(void *ptr, const char *location);
+
/*
- internal talloc_free call
+ internal free call that takes a struct talloc_chunk *.
*/
-static inline int _talloc_free_internal(void *ptr, const char *location)
+static inline int _tc_free_internal(struct talloc_chunk *tc,
+ const char *location)
{
- struct talloc_chunk *tc;
void *ptr_to_free;
-
- if (unlikely(ptr == NULL)) {
- 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);
+ void *ptr = TC_PTR_FROM_CHUNK(tc);
if (unlikely(tc->refs)) {
int is_child;
if (unlikely(tc->destructor)) {
talloc_destructor_t d = tc->destructor;
+
+ /*
+ * Protect the destructor against some overwrite
+ * attacks, by explicitly checking it has the right
+ * magic here.
+ */
+ if (talloc_chunk_from_ptr(ptr) != tc) {
+ /*
+ * This can't actually happen, the
+ * call itself will panic.
+ */
+ TALLOC_ABORT("talloc_chunk_from_ptr failed!");
+ }
+
if (d == (talloc_destructor_t)-1) {
return -1;
}
tc->flags |= TALLOC_FLAG_LOOP;
- _talloc_free_children_internal(tc, ptr, location);
+ _tc_free_children_internal(tc, ptr, location);
tc->flags |= TALLOC_FLAG_FREE;
}
if (tc->flags & TALLOC_FLAG_POOLMEM) {
- _talloc_free_poolmem(tc, location);
+ _tc_free_poolmem(tc, location);
return 0;
}
- talloc_memlimit_update_on_free(tc);
+ tc_memlimit_update_on_free(tc);
TC_INVALIDATE_FULL_CHUNK(tc);
free(ptr_to_free);
return 0;
}
+/*
+ internal talloc_free call
+*/
+static inline int _talloc_free_internal(void *ptr, const char *location)
+{
+ struct talloc_chunk *tc;
+
+ if (unlikely(ptr == NULL)) {
+ 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);
+ return _tc_free_internal(tc, location);
+}
+
static inline size_t _talloc_total_limit_size(const void *ptr,
struct talloc_memlimit *old_limit,
struct talloc_memlimit *new_limit);
/*
add a name to an existing pointer - va_list version
*/
-static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+static inline const char *tc_set_name_v(struct talloc_chunk *tc,
+ const char *fmt,
+ va_list ap) PRINTF_ATTRIBUTE(2,0);
-static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap)
+static inline const char *tc_set_name_v(struct talloc_chunk *tc,
+ const char *fmt,
+ va_list ap)
{
- struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
- tc->name = talloc_vasprintf(ptr, fmt, ap);
- if (likely(tc->name)) {
- _talloc_set_name_const(tc->name, ".name");
+ struct talloc_chunk *name_tc = _vasprintf_tc(TC_PTR_FROM_CHUNK(tc),
+ fmt,
+ ap);
+ if (likely(name_tc)) {
+ tc->name = TC_PTR_FROM_CHUNK(name_tc);
+ _tc_set_name_const(name_tc, ".name");
+ } else {
+ tc->name = NULL;
}
return tc->name;
}
*/
_PUBLIC_ const char *talloc_set_name(const void *ptr, const char *fmt, ...)
{
+ struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
const char *name;
va_list ap;
va_start(ap, fmt);
- name = talloc_set_name_v(ptr, fmt, ap);
+ name = tc_set_name_v(tc, fmt, ap);
va_end(ap);
return name;
}
va_list ap;
void *ptr;
const char *name;
+ struct talloc_chunk *tc;
- ptr = __talloc(context, size);
+ ptr = __talloc(context, size, &tc);
if (unlikely(ptr == NULL)) return NULL;
va_start(ap, fmt);
- name = talloc_set_name_v(ptr, fmt, ap);
+ name = tc_set_name_v(tc, fmt, ap);
va_end(ap);
if (unlikely(name == NULL)) {
va_list ap;
void *ptr;
const char *name;
+ struct talloc_chunk *tc;
- ptr = __talloc(NULL, 0);
+ ptr = __talloc(NULL, 0, &tc);
if (unlikely(ptr == NULL)) return NULL;
va_start(ap, fmt);
- name = talloc_set_name_v(ptr, fmt, ap);
+ name = tc_set_name_v(tc, fmt, ap);
va_end(ap);
if (unlikely(name == NULL)) {
return ptr;
}
-static inline void _talloc_free_children_internal(struct talloc_chunk *tc,
+static inline void _tc_free_children_internal(struct talloc_chunk *tc,
void *ptr,
const char *location)
{
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 (unlikely(_tc_free_internal(tc->child, location) == -1)) {
if (talloc_parent_chunk(child) != tc) {
/*
* Destructor already reparented this child.
* No further reparenting needed.
*/
- return;
+ continue;
}
if (new_parent == null_context) {
struct talloc_chunk *p = talloc_parent_chunk(ptr);
}
}
- _talloc_free_children_internal(tc, ptr, __location__);
+ _tc_free_children_internal(tc, ptr, __location__);
/* .. so we put it back after all other children have been freed */
if (tc_name) {
*/
_PUBLIC_ void *_talloc(const void *context, size_t size)
{
- return __talloc(context, size);
+ struct talloc_chunk *tc;
+ return __talloc(context, size, &tc);
}
/*
*/
_PUBLIC_ void talloc_set_name_const(const void *ptr, const char *name)
{
- _talloc_set_name_const(ptr, name);
+ _tc_set_name_const(talloc_chunk_from_ptr(ptr), name);
}
/*
#if ALWAYS_REALLOC
if (pool_hdr) {
- new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE, 0);
+ new_ptr = tc_alloc_pool(tc, size + TC_HDR_SIZE, 0);
pool_hdr->object_count--;
if (new_ptr == NULL) {
}
}
- new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE, 0);
+ new_ptr = tc_alloc_pool(tc, size + TC_HDR_SIZE, 0);
if (new_ptr == NULL) {
new_ptr = malloc(TC_HDR_SIZE+size);
if (new_ptr) {
memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE);
- _talloc_free_poolmem(tc, __location__ "_talloc_realloc");
+ _tc_free_poolmem(tc, __location__ "_talloc_realloc");
}
}
else {
}
tc->size = size;
- _talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name);
+ _tc_set_name_const(tc, name);
return TC_PTR_FROM_CHUNK(tc);
}
static inline char *__talloc_strlendup(const void *t, const char *p, size_t len)
{
char *ret;
+ struct talloc_chunk *tc;
- ret = (char *)__talloc(t, len + 1);
+ ret = (char *)__talloc(t, len + 1, &tc);
if (unlikely(!ret)) return NULL;
memcpy(ret, p, len);
ret[len] = 0;
- _talloc_set_name_const(ret, ret);
+ _tc_set_name_const(tc, ret);
return ret;
}
memcpy(&ret[slen], a, alen);
ret[slen+alen] = 0;
- _talloc_set_name_const(ret, ret);
+ _tc_set_name_const(talloc_chunk_from_ptr(ret), ret);
return ret;
}
#endif
#endif
-_PUBLIC_ char *talloc_vasprintf(const void *t, const char *fmt, va_list ap)
+static struct talloc_chunk *_vasprintf_tc(const void *t,
+ const char *fmt,
+ va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+static struct talloc_chunk *_vasprintf_tc(const void *t,
+ const char *fmt,
+ va_list ap)
{
int len;
char *ret;
va_list ap2;
+ struct talloc_chunk *tc;
char buf[1024];
/* this call looks strange, but it makes it work on older solaris boxes */
return NULL;
}
- ret = (char *)__talloc(t, len+1);
+ ret = (char *)__talloc(t, len+1, &tc);
if (unlikely(!ret)) return NULL;
if (len < sizeof(buf)) {
va_end(ap2);
}
- _talloc_set_name_const(ret, ret);
- return ret;
+ _tc_set_name_const(tc, ret);
+ return tc;
+}
+
+_PUBLIC_ char *talloc_vasprintf(const void *t, const char *fmt, va_list ap)
+{
+ struct talloc_chunk *tc = _vasprintf_tc(t, fmt, ap);
+ if (tc == NULL) {
+ return NULL;
+ }
+ return TC_PTR_FROM_CHUNK(tc);
}
vsnprintf(s + slen, alen + 1, fmt, ap2);
va_end(ap2);
- _talloc_set_name_const(s, s);
+ _tc_set_name_const(talloc_chunk_from_ptr(s), s);
return s;
}
{
struct talloc_chunk *tc;
- if (context == NULL) {
- context = null_context;
- }
if (context == NULL) {
return 0;
}
/*
Update memory limits when freeing a talloc_chunk.
*/
-static void talloc_memlimit_update_on_free(struct talloc_chunk *tc)
+static void tc_memlimit_update_on_free(struct talloc_chunk *tc)
{
size_t limit_shrink_size;