This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
[RFC] Simple checking allocator
- From: OndÅej BÃlka <neleai at seznam dot cz>
- To: libc-alpha at sourceware dot org
- Date: Tue, 22 Oct 2013 13:39:59 +0200
- Subject: [RFC] Simple checking allocator
- Authentication-results: sourceware.org; auth=none
- References: <20131021193617 dot GA29829 at domone dot podge> <20131021212655 dot GA32168 at domone dot podge>
Another area of interest are malloc errors. For this I wrote a simple
allocator. It currently does detect writing past allocated area and
double frees.
I plan for double free also write location of previous free but I got to
technical problems as backtrace_symbols uses malloc.
A detection of overruns like efence but much faster would be possible
but it would require more special casing. One case is that we detect
invalid writes by sentinel which is not needed after they cross page.
Next possibility is adding function that for given memory position
returns start and end of malloced memory. This would be useful for
bounds checking of strcpy et al.
Comments?
diff --git a/preload/malloc.c b/preload/malloc.c
new file mode 100644
index 0000000..b7a735a
--- /dev/null
+++ b/preload/malloc.c
@@ -0,0 +1,241 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include "common.h"
+#define INFO(...)
+
+#define ALIGN_UP(x, no) ((((uintptr_t) x) + no - 1) - (((uintptr_t) x) + no - 1) % no)
+#define SENTINEL 5877815476798078077UL
+#define BIG_SENTINEL 1204925723299285449UL
+#define DOUBLE_FREE 10943064412161398437UL
+
+static pthread_mutex_t mutex;
+static int inited;
+size_t page_size;
+static void
+init ()
+{
+ page_size = sysconf (_SC_PAGE_SIZE);
+ pthread_mutex_init (&mutex, NULL);
+ inited = 1;
+}
+static char *memory;
+static size_t memsize = 0, capacity = 0;
+void *
+malloc (size_t len)
+{
+ INFO ("malloc %i\n", len);
+ void *ptr;
+ posix_memalign (&ptr, 16, len);
+ return ptr;
+}
+void *
+calloc (size_t nmemb, size_t size)
+{
+ INFO ("calloc %i %i\n", nmemb, size);
+ if (!nmemb | !size)
+ return NULL;
+ if (SIZE_MAX / nmemb < size)
+ {
+ log ("allocation overflow in calloc(%i, %i)\n", nmemb, size);
+ abort ();
+ }
+ void *ptr = malloc (nmemb * size);
+ if (ptr != NULL)
+ memset (ptr, 0, nmemb * size);
+ return ptr;
+}
+
+void *
+valloc (size_t size)
+{
+ INFO ("valloc %i\n", size);
+
+ void *ret;
+ posix_memalign (&ret, page_size, size);
+ return ret;
+}
+void *
+pvalloc (size_t size)
+{
+ INFO ("pvalloc %i\n", size);
+
+ void *ret;
+ posix_memalign (&ret, page_size, size);
+ return ret;
+}
+void *
+memalign (size_t alignment, size_t size)
+{
+ INFO ("memalign %i %i\n", alignment, size);
+ void *ret;
+ posix_memalign (&ret, alignment, size);
+ return ret;
+}
+void *
+realloc (void *ptr, size_t size)
+{
+ if (!ptr)
+ return malloc (size);
+ if (((uint64_t *) (ptr))[-1] != SENTINEL)
+ {
+ if (((uint64_t *) (ptr))[-1] == DOUBLE_FREE)
+ log ("realloc: already freed\n");
+ log ("corrupt sentinel\n");
+ abort ();
+ }
+
+ size_t old_size = ((uint64_t *) (ptr))[-2];
+ INFO ("realloc %x-%x %i %i\n", ptr - 16, ptr + old_size + 8, old_size, size);
+ if (old_size == -1)
+ {
+ log ("realloc used freed memory");
+ abort ();
+ }
+
+ if (old_size > size)
+ return ptr;
+
+ char *new = malloc (size);
+ memcpy (new, ptr, old_size);
+ free (ptr);
+ return new;
+}
+
+static char *
+wrap_mmap (size_t size)
+{
+ char *ret = mmap (NULL, ALIGN_UP (size, page_size) + 2 * page_size,
+ PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (!ret)
+ abort;
+ munmap (ret, page_size);
+ munmap (ret + page_size + ALIGN_UP (size, page_size), page_size);
+ return ret + page_size;
+}
+
+static void **bucket[64];
+
+static void
+return_bucket (char *ptr, size_t size)
+{
+ int pow;
+ for (pow = 0; 1UL << pow < size; pow++)
+ ;
+ char *old = (char *) bucket[pow];
+ bucket[pow] = (void **) ptr;
+ *bucket[pow] = old;
+}
+
+
+static void
+add_bucket (size_t size)
+{
+ int i, pow;
+ for (pow = 0; 1UL << pow < size; pow++)
+ ;
+ if (1UL << pow >= page_size)
+ { /* tighter checks. */
+ char *ptr = wrap_mmap ((1UL << pow));
+ return_bucket (ptr, size);
+ }
+ else
+ {
+ char *ptr = wrap_mmap (page_size);
+ for (i = 0; i < page_size; i += 1UL << pow)
+ {
+ return_bucket (ptr + i, size);
+ }
+ }
+}
+
+static void *
+get_bucket (size_t size)
+{
+ int pow;
+ for (pow = 0; 1UL << pow < size; pow++)
+ ;
+ if (!bucket[pow])
+ add_bucket (size);
+ void **ret = bucket[pow];
+ bucket[pow] = *ret;
+ return (void *) ret;
+}
+
+int
+posix_memalign (void **memptr, size_t alignment, size_t size)
+{
+ if (!inited)
+ init ();
+ INFO ("posix_memalign\n");
+ if (pthread_mutex_lock (&mutex))
+ abort ();
+
+ /* separate for tigther checks. */
+ if (size < alignment)
+ size = alignment;
+
+ if (alignment > 16)
+ { /*TODO, excessive alignment can cause leaks. */
+ *memptr = get_bucket (size + alignment + 24);
+ *memptr = ((char *) ALIGN_UP ((((char *) *memptr) + 16), alignment)) - 16;
+ }
+ else
+ *memptr = get_bucket (size + 24);
+
+ *memptr += 16;
+
+ if (pthread_mutex_unlock (&mutex))
+ abort ();
+
+ *((uint64_t *) (*memptr - 8)) = SENTINEL;
+ *((uint64_t *) (*memptr - 16)) = size;
+ *((uint64_t *) (*memptr + size)) = SENTINEL;
+
+
+ INFO ("%i %x %x-%x\n", size, memory, *memptr - 16, *memptr + size + 8);
+
+
+ return 0;
+}
+void
+free (void *ptr)
+{
+ if (!ptr)
+ return;
+
+ if (((uint64_t *) (ptr))[-1] != SENTINEL)
+ {
+ if (((uint64_t *) (ptr))[-1] == DOUBLE_FREE)
+ log ("free: double free\n");
+ else
+ log ("free: corrupt left sentinel\n");
+ abort ();
+ }
+ ((uint64_t *) (ptr))[-1] = DOUBLE_FREE;
+
+ size_t old_size = ((uint64_t *) (ptr))[-2];
+ INFO ("free %x-%x\n", ptr - 16, ptr + old_size + 8);
+ if (old_size == -1)
+ {
+ log ("double free");
+ abort ();
+ }
+
+ if (((uint64_t *) (ptr + old_size))[0] != SENTINEL)
+ {
+ log ("free: corrupt rigth sentinel\n");
+ abort ();
+ }
+
+ if (pthread_mutex_lock (&mutex))
+ abort ();
+ return_bucket (((char *) ptr) - 16, old_size + 24);
+ if (pthread_mutex_unlock (&mutex))
+ abort ();
+}