This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFC] Simple checking allocator


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 ();
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]