]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add function for allocating talloc pools on page boundaries
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 3 Aug 2018 16:43:28 +0000 (12:43 -0400)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 3 Aug 2018 16:43:36 +0000 (12:43 -0400)
src/lib/util/talloc.c
src/lib/util/talloc.h

index cc0a28d8f5d704c3023ad71dbfb023455a1da23c..174affa43ff33fc7d598255bea92b6d6f5458a57 100644 (file)
@@ -25,7 +25,11 @@ RCSID("$Id$")
 
 #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;
 
@@ -53,7 +57,6 @@ void *talloc_null_ctx(void)
        return null_ctx;
 }
 
-
 /** Called when the parent CTX is freed
  *
  */
@@ -75,7 +78,6 @@ static int _link_ctx_link_free(fr_talloc_link_t *link)
        return 0;
 }
 
-
 /** Called when the child CTX is freed
  *
  */
@@ -102,7 +104,6 @@ static int _link_ctx_self_free(fr_talloc_link_t **link_p)
        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
@@ -138,6 +139,88 @@ int talloc_link_ctx(TALLOC_CTX *parent, TALLOC_CTX *child)
        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
index 827bf9d21c84db9d49e3a2dfd0b65a675902895f..919224adcde242d2f5d83772e0bad58771540854 100644 (file)
@@ -40,6 +40,8 @@ void          *talloc_null_ctx(void);
 
 int            talloc_link_ctx(TALLOC_CTX *parent, TALLOC_CTX *child);
 
+TALLOC_CTX     *talloc_page_aligned_pool(TALLOC_CTX *ctx, void **start, void **end, size_t size);
+
 char           *talloc_typed_strdup(void const *t, char const *p);
 
 char           *talloc_typed_asprintf(void const *t, char const *fmt, ...) CC_HINT(format (printf, 2, 3));