utils: Add malloc/free wrappers returning aligned data
authorMartin Willi <martin@revosec.ch>
Tue, 31 Mar 2015 15:25:05 +0000 (17:25 +0200)
committerMartin Willi <martin@revosec.ch>
Wed, 15 Apr 2015 11:44:40 +0000 (13:44 +0200)
While we could use posix_memalign(3), that is not fully portable. Further, it
might be difficult on some platforms to properly catch it in leak-detective,
which results in invalid free()s when releasing such memory.

We instead use a simple wrapper, which allocates larger data, and saves the
padding size in the allocated header. This requires that memory is released
using a dedicated function.

To reduce the risk of invalid free() when working on corrupted data, we fill up
all the padding with the padding length, and verify it during free_align().

src/libstrongswan/tests/suites/test_utils.c
src/libstrongswan/utils/utils.c
src/libstrongswan/utils/utils.h

index f151fb3..b38f2cb 100644 (file)
@@ -229,6 +229,41 @@ START_TEST(test_strpfx)
 END_TEST
 
 /*******************************************************************************
+ * mallac_align/free_align
+ */
+
+START_TEST(test_malloc_align)
+{
+       void *ptr[128][256];
+       int size, align;
+
+       for (size = 0; size < countof(ptr); size++)
+       {
+               for (align = 0; align < countof(ptr[0]); align++)
+               {
+                       ptr[size][align] = malloc_align(size, align);
+                       if (align)
+                       {
+                               ck_assert((uintptr_t)ptr[size][align] % align == 0);
+                       }
+                       if (size)
+                       {
+                               ck_assert(ptr[size][align]);
+                               memset(ptr[size][align], 0xEF, size);
+                       }
+               }
+       }
+       for (size = 0; size < countof(ptr); size++)
+       {
+               for (align = 0; align < countof(ptr[0]); align++)
+               {
+                       free_align(ptr[size][align]);
+               }
+       }
+}
+END_TEST
+
+/*******************************************************************************
  * memxor
  */
 
@@ -816,6 +851,10 @@ Suite *utils_suite_create()
        tcase_add_loop_test(tc, test_strpfx, 0, countof(strpfx_data));
        suite_add_tcase(s, tc);
 
+       tc = tcase_create("malloc_align");
+       tcase_add_test(tc, test_malloc_align);
+       suite_add_tcase(s, tc);
+
        tc = tcase_create("memxor");
        tcase_add_test(tc, test_memxor);
        tcase_add_test(tc, test_memxor_aligned);
index 3d5e3df..119c656 100644 (file)
@@ -61,6 +61,50 @@ ENUM(status_names, SUCCESS, NEED_MORE,
 /**
  * Described in header.
  */
+void* malloc_align(size_t size, u_int8_t align)
+{
+       u_int8_t pad;
+       void *ptr;
+
+       if (align == 0)
+       {
+               align = 1;
+       }
+       ptr = malloc(align + sizeof(pad) + size);
+       if (!ptr)
+       {
+               return NULL;
+       }
+       /* store padding length just before data, down to the allocation boundary
+        * to do some verification during free_align() */
+       pad = align - ((uintptr_t)ptr % align);
+       memset(ptr, pad, pad);
+       return ptr + pad;
+}
+
+/**
+ * Described in header.
+ */
+void free_align(void *ptr)
+{
+       u_int8_t pad, *pos;
+
+       pos = ptr - 1;
+       /* verify padding to check any corruption */
+       for (pad = *pos; (void*)pos >= ptr - pad; pos--)
+       {
+               if (*pos != pad)
+               {
+                       DBG1(DBG_LIB, "!!!! invalid free_align() !!!!");
+                       return;
+               }
+       }
+       free(ptr - pad);
+}
+
+/**
+ * Described in header.
+ */
 void memxor(u_int8_t dst[], u_int8_t src[], size_t n)
 {
        int m, i;
index 235f283..a9791ed 100644 (file)
@@ -567,6 +567,24 @@ typedef struct timespec timespec_t;
 typedef struct sockaddr sockaddr_t;
 
 /**
+ * malloc(), but returns aligned memory.
+ *
+ * The returned pointer must be freed using free_align(), not free().
+ *
+ * @param size                 size of allocated data
+ * @param align                        alignment, up to 255 bytes, usually a power of 2
+ * @return                             allocated hunk, aligned to align bytes
+ */
+void* malloc_align(size_t size, u_int8_t align);
+
+/**
+ * Free a hunk allocated by malloc_align().
+ *
+ * @param ptr                  hunk to free
+ */
+void free_align(void *ptr);
+
+/**
  * Same as memcpy, but XORs src into dst instead of copy
  */
 void memxor(u_int8_t dest[], u_int8_t src[], size_t n);