[asan] make __asan::Deallocate immune to racy double-free (issue #57)

git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@154097 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/asan/asan_allocator.cc b/lib/asan/asan_allocator.cc
index 3797a09..8c3cc00 100644
--- a/lib/asan/asan_allocator.cc
+++ b/lib/asan/asan_allocator.cc
@@ -704,18 +704,22 @@
 
   // Printf("Deallocate %p\n", ptr);
   AsanChunk *m = PtrToChunk((uintptr_t)ptr);
-  if (m->chunk_state == CHUNK_QUARANTINE) {
+
+  // Flip the state atomically to avoid race on double-free.
+  uint16_t old_chunk_state = AtomicExchange(&m->chunk_state, CHUNK_QUARANTINE);
+
+  if (old_chunk_state == CHUNK_QUARANTINE) {
     Report("ERROR: AddressSanitizer attempting double-free on %p:\n", ptr);
     stack->PrintStack();
     Describe((uintptr_t)ptr, 1);
     ShowStatsAndAbort();
-  } else if (m->chunk_state != CHUNK_ALLOCATED) {
+  } else if (old_chunk_state != CHUNK_ALLOCATED) {
     Report("ERROR: AddressSanitizer attempting free on address which was not"
            " malloc()-ed: %p\n", ptr);
     stack->PrintStack();
     ShowStatsAndAbort();
   }
-  CHECK(m->chunk_state == CHUNK_ALLOCATED);
+  CHECK(old_chunk_state == CHUNK_ALLOCATED);
   CHECK(m->free_tid == AsanThread::kInvalidTid);
   CHECK(m->alloc_tid >= 0);
   AsanThread *t = asanThreadRegistry().GetCurrent();
@@ -731,7 +735,7 @@
   thread_stats.freed += m->used_size;
   thread_stats.freed_by_size[m->SizeClass()]++;
 
-  m->chunk_state = CHUNK_QUARANTINE;
+  CHECK(m->chunk_state == CHUNK_QUARANTINE);
   if (t) {
     AsanThreadLocalMallocStorage *ms = &t->malloc_storage();
     CHECK(!m->next);
diff --git a/lib/asan/asan_internal.h b/lib/asan/asan_internal.h
index 39d3adf..3a25cf2 100644
--- a/lib/asan/asan_internal.h
+++ b/lib/asan/asan_internal.h
@@ -187,6 +187,7 @@
 int GetPid();
 uintptr_t GetThreadSelf();
 int AtomicInc(int *a);
+uint16_t AtomicExchange(uint16_t *a, uint16_t new_val);
 
 // Wrapper for TLS/TSD.
 void AsanTSDInit(void (*destructor)(void *tsd));
diff --git a/lib/asan/asan_posix.cc b/lib/asan/asan_posix.cc
index 0ab09e6..03bd057 100644
--- a/lib/asan/asan_posix.cc
+++ b/lib/asan/asan_posix.cc
@@ -152,6 +152,10 @@
 #endif
 }
 
+uint16_t AtomicExchange(uint16_t *a, uint16_t new_val) {
+  return __sync_lock_test_and_set(a, new_val);
+}
+
 void SortArray(uintptr_t *array, size_t size) {
   std::sort(array, array + size);
 }
diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc
index 94f4d04..baa8268 100644
--- a/lib/asan/asan_win.cc
+++ b/lib/asan/asan_win.cc
@@ -234,6 +234,10 @@
   return InterlockedExchangeAdd((LONG*)a, 1) + 1;
 }
 
+uint16_t AtomicExchange(uint16_t *a, uint16_t new_val) {
+  return InterlockedExchange16(a, new_val);
+}
+
 const char* AsanGetEnv(const char* name) {
   static char env_buffer[32767] = {};