#include "talloc.h"
+#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/util/strerror.h>
+
#include <string.h>
+#include <unistd.h>
typedef struct fr_talloc_link fr_talloc_link_t;
return null_ctx;
}
-
/** Called when the parent CTX is freed
*
*/
return 0;
}
-
/** Called when the child CTX is freed
*
*/
return 0;
}
-
/** Link two different parent and child contexts, so the child is freed before the parent
*
* @note This is not thread safe. Do not free parent before threads are joined, do not call from a
return 0;
}
+/** Return a page aligned talloc memory pool
+ *
+ * Because we can't intercept talloc's malloc() calls, we need to do some tricks
+ * in order to get the first allocation in the pool page aligned, and to limit
+ * the size of the pool to a multiple of the page size.
+ *
+ * The reason for wanting a page aligned talloc pool, is it allows us to
+ * mprotect() the pages that belong to the pool.
+ *
+ * Talloc chunks appear to be allocated within the protected region, so this should
+ * catch frees too.
+ *
+ * @param[in] ctx to allocate pool memory in.
+ * @param[out] start A page aligned address within the pool. This can be passed
+ * to mprotect().
+ * @param[out] end of the pages that should be protected.
+ * @param[in] size How big to make the pool. Will be corrected to a multiple
+ * of the page size. The actual pool size will be size
+ * rounded to a multiple of the (page_size), + page_size
+ */
+TALLOC_CTX *talloc_page_aligned_pool(TALLOC_CTX *ctx, void **start, void **end, size_t size)
+{
+ size_t rounded, page_size = (size_t)getpagesize();
+ size_t hdr_size, pool_size;
+ void *next, *chunk;
+ TALLOC_CTX *pool;
+
+#define ROUND_UP(_num, _mul) (((((_num) + ((_mul) - 1))) / (_mul)) * (_mul))
+
+ rounded = ROUND_UP(size, page_size); /* Round up to a multiple of the page size */
+ if (rounded == 0) rounded = page_size;
+
+ pool_size = rounded + page_size;
+ pool = talloc_pool(ctx, pool_size); /* Over allocate */
+ if (!pool) {
+ fr_strerror_printf("Out of memory");
+ return NULL;
+ }
+
+ chunk = talloc_size(pool, 1); /* Get the starting address */
+ if (!fr_cond_assert((chunk > pool) && ((uintptr_t)chunk < ((uintptr_t)pool + rounded)))) {
+ fr_strerror_printf("Initial allocation outside of pool memory");
+ error:
+ talloc_free(pool);
+ return NULL;
+ }
+ hdr_size = (uintptr_t)pool - (uintptr_t)chunk;
+
+ next = (void *)ROUND_UP((uintptr_t)chunk, page_size); /* Round up address to the next page */
+
+ /*
+ * Depending on how talloc allocates the chunk headers,
+ * the memory allocated here might not align to a page
+ * boundary, but that's ok, we just need future allocations
+ * to occur on or after 'next'.
+ */
+ if (((uintptr_t)next - (uintptr_t)chunk) > 0) {
+ size_t pad_size;
+ void *padding;
+
+ pad_size = ((uintptr_t)next - (uintptr_t)chunk);
+ if (pad_size > hdr_size) {
+ pad_size -= hdr_size; /* Save ~111 bytes by not over-padding */
+ } else {
+ pad_size = 1;
+ }
+
+ padding = talloc_size(pool, pad_size);
+ if (!fr_cond_assert(((uintptr_t)padding + (uintptr_t)pad_size) >= (uintptr_t)next)) {
+ fr_strerror_printf("Failed padding pool memory");
+ goto error;
+ }
+ }
+
+ *start = next; /* This is the address we feed into mprotect */
+ *end = (uintptr_t)next + (uintptr_t)rounded;
+
+ talloc_set_memlimit(pool, pool_size); /* Don't allow allocations outside of the pool */
+
+ return pool;
+}
+
/** Call talloc_strdup, setting the type on the new chunk correctly
*
* For some bizarre reason the talloc string functions don't set the