Index: runtime/szrt_asan.c |
diff --git a/runtime/szrt_asan.c b/runtime/szrt_asan.c |
index 8600ed5ddf86253b0c6486b648635416fb87f544..a57c384115bb2c9e15df8ce4439a75c9ccba267b 100644 |
--- a/runtime/szrt_asan.c |
+++ b/runtime/szrt_asan.c |
@@ -18,6 +18,7 @@ |
#include <assert.h> |
#include <errno.h> |
#include <limits.h> |
+#include <sched.h> |
#include <stdbool.h> |
#include <stddef.h> |
#include <stdint.h> |
@@ -26,10 +27,30 @@ |
#include <string.h> |
#include <sys/mman.h> |
+#if _POSIX_THREADS |
+ |
+#include <pthread.h> |
+typedef pthread_mutex_t mutex_t; |
+#define MUTEX_INITIALIZER (PTHREAD_MUTEX_INITIALIZER) |
+#define MUTEX_LOCK(mutex) (pthread_mutex_lock(&(mutex))) |
+#define MUTEX_UNLOCK(mutex) (pthread_mutex_unlock(&(mutex))) |
+ |
+#else // !_POSIX_THREADS |
+ |
+typedef uint32_t mutex_t; |
+#define MUTEX_INITIALIZER (0) |
+#define MUTEX_LOCK(mutex) \ |
+ while (__sync_swap((mutex), 1) != 0) { \ |
+ sched_yield(); \ |
+ } |
+#define MUTEX_UNLOCK(mutex) (__sync_swap((mutex), 0)) |
+ |
+#endif // _POSIX_THREADS |
+ |
#define RZ_SIZE (32) |
#define SHADOW_SCALE_LOG2 (3) |
#define SHADOW_SCALE ((size_t)1 << SHADOW_SCALE_LOG2) |
-#define DEBUG (0) |
+#define DEBUG (1) |
// Assuming 48 bit address space on 64 bit systems |
#define SHADOW_LENGTH_64 (1u << (48 - SHADOW_SCALE_LOG2)) |
@@ -44,11 +65,14 @@ |
#define SHADOW2MEM(p) \ |
((uintptr_t)((char *)(p)-shadow_offset) << SHADOW_SCALE_LOG2) |
+#define QUARANTINE_MAX_SIZE ((size_t)1 << 28) // 256 MB |
+ |
#define STACK_POISON_VAL ((char)-1) |
#define HEAP_POISON_VAL ((char)-2) |
#define GLOBAL_POISON_VAL ((char)-3) |
+#define FREED_POISON_VAL ((char)-4) |
#define MEMTYPE_INDEX(x) (-1 - (x)) |
-static const char *memtype_names[] = {"stack", "heap", "global"}; |
+static const char *memtype_names[] = {"stack", "heap", "global", "freed"}; |
#define ACCESS_LOAD (0) |
#define ACCESS_STORE (1) |
@@ -79,6 +103,16 @@ void __asan_free(char *); |
void __asan_poison(char *, int, char); |
void __asan_unpoison(char *, int); |
+struct quarantine_entry { |
+ struct quarantine_entry *next; |
+ size_t size; |
+}; |
+ |
+mutex_t quarantine_lock = MUTEX_INITIALIZER; |
+uint64_t quarantine_size = 0; |
+struct quarantine_entry *quarantine_head = NULL; |
+struct quarantine_entry *quarantine_tail = NULL; |
+ |
static void __asan_error(char *ptr, int size, int access) { |
char *shadow_addr = MEM2SHADOW(ptr); |
char shadow_val = *shadow_addr; |
@@ -87,7 +121,7 @@ static void __asan_error(char *ptr, int size, int access) { |
assert(access == ACCESS_LOAD || access == ACCESS_STORE); |
const char *access_name = access_names[access]; |
assert(shadow_val == STACK_POISON_VAL || shadow_val == HEAP_POISON_VAL || |
- shadow_val == GLOBAL_POISON_VAL); |
+ shadow_val == GLOBAL_POISON_VAL || shadow_val == FREED_POISON_VAL); |
const char *memtype = memtype_names[MEMTYPE_INDEX(shadow_val)]; |
fprintf(stderr, "Illegal %d byte %s %s object at %p\n", size, access_name, |
memtype, ptr); |
@@ -213,19 +247,51 @@ void *__asan_realloc(char *ptr, size_t size) { |
void __asan_free(char *ptr) { |
DUMP("free() called on %p\n", ptr); |
+ if (ptr == NULL) |
+ return; |
+ if (*(char *)MEM2SHADOW(ptr) == FREED_POISON_VAL) { |
+ fprintf(stderr, "Double free of object at %p\n", ptr); |
+ abort(); |
+ } |
char *rz_left, *rz_right; |
__asan_get_redzones(ptr, &rz_left, &rz_right); |
size_t rz_right_size = *(size_t *)rz_right; |
- __asan_unpoison(rz_left, RZ_SIZE); |
- __asan_unpoison(rz_right, rz_right_size); |
- free(rz_left); |
+ size_t total_size = rz_right_size + (rz_right - rz_left); |
+ __asan_poison(rz_left, total_size, FREED_POISON_VAL); |
+ |
+ // place allocation in quarantine |
+ struct quarantine_entry *entry = (struct quarantine_entry *)rz_left; |
+ assert(entry != NULL); |
+ entry->next = NULL; |
+ entry->size = total_size; |
+ |
+ DUMP("Placing %d bytes at %p in quarantine\n", entry->size, entry); |
+ MUTEX_LOCK(&quarantine_lock); |
+ if (quarantine_tail != NULL) |
+ quarantine_tail->next = entry; |
+ quarantine_tail = entry; |
+ if (quarantine_head == NULL) |
+ quarantine_head = entry; |
+ quarantine_size += total_size; |
+ DUMP("Quarantine size is %llu\n", quarantine_size); |
+ |
+ // free old objects as necessary |
+ while (quarantine_size > QUARANTINE_MAX_SIZE) { |
+ struct quarantine_entry *freed = quarantine_head; |
+ assert(freed != NULL); |
+ __asan_unpoison((char *)freed, freed->size); |
+ quarantine_size -= freed->size; |
+ quarantine_head = freed->next; |
+ DUMP("Releasing %d bytes at %p from quarantine\n", freed->size, freed); |
+ DUMP("Quarantine size is %llu\n", quarantine_size); |
+ free(freed); |
+ } |
+ MUTEX_UNLOCK(&quarantine_lock); |
} |
void __asan_poison(char *ptr, int size, char poison_val) { |
char *end = ptr + size; |
assert(IS_SHADOW_ALIGNED(end)); |
- // redzones should be no greater than RZ_SIZE + RZ_SIZE-1 for alignment |
- assert(size < 2 * RZ_SIZE); |
DUMP("poison %d bytes at %p: %p - %p\n", size, ptr, MEM2SHADOW(ptr), |
MEM2SHADOW(end)); |
size_t offset = SHADOW_OFFSET(ptr); |
@@ -239,7 +305,6 @@ void __asan_poison(char *ptr, int size, char poison_val) { |
void __asan_unpoison(char *ptr, int size) { |
char *end = ptr + size; |
assert(IS_SHADOW_ALIGNED(end)); |
- assert(size < 2 * RZ_SIZE); |
DUMP("unpoison %d bytes at %p: %p - %p\n", size, ptr, MEM2SHADOW(ptr), |
MEM2SHADOW(end)); |
*(char *)MEM2SHADOW(ptr) = 0; |