Bug 844513: Add ASan memory check annotations (manual poisoning) to NSPR_4_10_BETA1
authorMats Palmgren <matspal@gmail.com>
Wed, 24 Apr 2013 18:03:30 -0700
changeset 4460 608d1530807de789b5f4fdfa492309b048aac3cf
parent 4459 8927f44164de3678019e634cf410c249c59ec44f
child 4461 74ffa3061a8995f47070ad50294aeb311295c3da
push id11
push userwtc@google.com
push dateThu, 25 Apr 2013 01:03:39 +0000
bugs844513
Bug 844513: Add ASan memory check annotations (manual poisoning) to PLArena. r=wtc,choller.
lib/ds/plarena.c
lib/ds/plarena.h
--- a/lib/ds/plarena.c
+++ b/lib/ds/plarena.c
@@ -152,16 +152,17 @@ PR_IMPLEMENT(void *) PL_ArenaAllocate(PL
     /* attempt to allocate from arenas at pool->current */
     {
         a = pool->current;
         do {
             if ( a->avail +nb <= a->limit )  {
                 pool->current = a;
                 rp = (char *)a->avail;
                 a->avail += nb;
+                PL_MAKE_MEM_UNDEFINED(rp, nb);
                 return rp;
             }
         } while( NULL != (a = a->next) );
     }
 
     /* attempt to allocate from arena_freelist */
     {
         PLArena *p; /* previous pointer, for unlinking from freelist */
@@ -182,41 +183,44 @@ PR_IMPLEMENT(void *) PL_ArenaAllocate(PL
                 a->avail += nb;
                 /* the newly allocated arena is linked after pool->current 
                 *  and becomes pool->current */
                 a->next = pool->current->next;
                 pool->current->next = a;
                 pool->current = a;
                 if ( NULL == pool->first.next )
                     pool->first.next = a;
+                PL_MAKE_MEM_UNDEFINED(rp, nb);
                 return(rp);
             }
         }
         UnlockArena();
     }
 
     /* attempt to allocate from the heap */ 
     {  
         PRUint32 sz = PR_MAX(pool->arenasize, nb);
         sz += sizeof *a + pool->mask;  /* header and alignment slop */
         a = (PLArena*)PR_MALLOC(sz);
         if ( NULL != a )  {
             a->limit = (PRUword)a + sz;
             a->base = a->avail = (PRUword)PL_ARENA_ALIGN(pool, a + 1);
+            PL_MAKE_MEM_NOACCESS((void*)a->avail, a->limit - a->avail);
             rp = (char *)a->avail;
             a->avail += nb;
             /* the newly allocated arena is linked after pool->current 
             *  and becomes pool->current */
             a->next = pool->current->next;
             pool->current->next = a;
             pool->current = a;
             if ( NULL == pool->first.next )
                 pool->first.next = a;
             PL_COUNT_ARENA(pool,++);
             COUNT(pool, nmallocs);
+            PL_MAKE_MEM_UNDEFINED(rp, nb);
             return(rp);
         }
     }
 
     /* we got to here, and there's no memory to allocate */
     return(NULL);
 } /* --- end PL_ArenaAllocate() --- */
 
@@ -232,17 +236,18 @@ PR_IMPLEMENT(void *) PL_ArenaGrow(
 }
 
 static void ClearArenaList(PLArena *a, PRInt32 pattern)
 {
 
     for (; a; a = a->next) {
         PR_ASSERT(a->base <= a->avail && a->avail <= a->limit);
         a->avail = a->base;
-	PL_CLEAR_UNUSED_PATTERN(a, pattern);
+        PL_CLEAR_UNUSED_PATTERN(a, pattern);
+        PL_MAKE_MEM_NOACCESS((void*)a->avail, a->limit - a->avail);
     }
 }
 
 PR_IMPLEMENT(void) PL_ClearArenaPool(PLArenaPool *pool, PRInt32 pattern)
 {
     ClearArenaList(pool->first.next, pattern);
 }
 
@@ -268,16 +273,18 @@ static void FreeArenaList(PLArenaPool *p
             *ap = a->next;
             PL_CLEAR_ARENA(a);
             PL_COUNT_ARENA(pool,--);
             PR_DELETE(a);
         } while ((a = *ap) != 0);
     } else {
         /* Insert the whole arena chain at the front of the freelist. */
         do {
+            PL_MAKE_MEM_NOACCESS((void*)(*ap)->base,
+                                 (*ap)->limit - (*ap)->base);
             ap = &(*ap)->next;
         } while (*ap);
         LockArena();
         *ap = arena_freelist;
         arena_freelist = a;
         head->next = 0;
         UnlockArena();
     }
--- a/lib/ds/plarena.h
+++ b/lib/ds/plarena.h
@@ -53,16 +53,78 @@ struct PLArenaPool {
     PRUint32    arenasize;      /* net exact size of a new arena */
     PRUword     mask;           /* alignment mask (power-of-2 - 1) */
 #ifdef PL_ARENAMETER
     PLArenaStats stats;
 #endif
 };
 
 /*
+ * WARNING: The PL_MAKE_MEM_ macros are for internal use by NSPR. Do NOT use
+ * them in your code.
+ *
+ * NOTE: Valgrind support to be added.
+ *
+ * The PL_MAKE_MEM_ macros are modeled after the MOZ_MAKE_MEM_ macros in
+ * Mozilla's mfbt/MemoryChecking.h. Only AddressSanitizer is supported now.
+ *
+ * Provides a common interface to the ASan (AddressSanitizer) and Valgrind
+ * functions used to mark memory in certain ways. In detail, the following
+ * three macros are provided:
+ *
+ *   PL_MAKE_MEM_NOACCESS  - Mark memory as unsafe to access (e.g. freed)
+ *   PL_MAKE_MEM_UNDEFINED - Mark memory as accessible, with content undefined
+ *   PL_MAKE_MEM_DEFINED - Mark memory as accessible, with content defined
+ *
+ * With Valgrind in use, these directly map to the three respective Valgrind
+ * macros. With ASan in use, the NOACCESS macro maps to poisoning the memory,
+ * while the UNDEFINED/DEFINED macros unpoison memory.
+ *
+ * With no memory checker available, all macros expand to the empty statement.
+ */
+
+/* WARNING: PL_SANITIZE_ADDRESS is for internal use by this header. Do NOT
+ * define or test this macro in your code.
+ */
+#if defined(__has_feature)
+#if __has_feature(address_sanitizer)
+#define PL_SANITIZE_ADDRESS 1
+#endif
+#elif defined(__SANITIZE_ADDRESS__)
+#define PL_SANITIZE_ADDRESS 1
+#endif
+
+#if defined(PL_SANITIZE_ADDRESS)
+
+/* These definitions are usually provided through the
+ * sanitizer/asan_interface.h header installed by ASan.
+ * See https://code.google.com/p/address-sanitizer/wiki/ManualPoisoning
+ */
+
+void __asan_poison_memory_region(void const volatile *addr, size_t size);
+void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
+
+#define PL_MAKE_MEM_NOACCESS(addr, size) \
+    __asan_poison_memory_region((addr), (size))
+
+#define PL_MAKE_MEM_UNDEFINED(addr, size) \
+    __asan_unpoison_memory_region((addr), (size))
+
+#define PL_MAKE_MEM_DEFINED(addr, size) \
+    __asan_unpoison_memory_region((addr), (size))
+
+#else
+
+#define PL_MAKE_MEM_NOACCESS(addr, size)
+#define PL_MAKE_MEM_UNDEFINED(addr, size)
+#define PL_MAKE_MEM_DEFINED(addr, size)
+
+#endif
+
+/*
  * If the including .c file uses only one power-of-2 alignment, it may define
  * PL_ARENA_CONST_ALIGN_MASK to the alignment mask and save a few instructions
  * per ALLOCATE and GROW.
  */
 #ifdef PL_ARENA_CONST_ALIGN_MASK
 #define PL_ARENA_ALIGN(pool, n) (((PRUword)(n) + PL_ARENA_CONST_ALIGN_MASK) \
                                 & ~PL_ARENA_CONST_ALIGN_MASK)
 
@@ -73,63 +135,73 @@ struct PLArenaPool {
 #endif
 
 #define PL_ARENA_ALLOCATE(p, pool, nb) \
     PR_BEGIN_MACRO \
         PLArena *_a = (pool)->current; \
         PRUint32 _nb = PL_ARENA_ALIGN(pool, nb); \
         PRUword _p = _a->avail; \
         PRUword _q = _p + _nb; \
-        if (_q > _a->limit) \
+        if (_q > _a->limit) { \
             _p = (PRUword)PL_ArenaAllocate(pool, _nb); \
-        else \
+        } else { \
+            PL_MAKE_MEM_UNDEFINED((void *)_p, nb); \
             _a->avail = _q; \
+        } \
         p = (void *)_p; \
         PL_ArenaCountAllocation(pool, nb); \
     PR_END_MACRO
 
 #define PL_ARENA_GROW(p, pool, size, incr) \
     PR_BEGIN_MACRO \
         PLArena *_a = (pool)->current; \
         PRUint32 _incr = PL_ARENA_ALIGN(pool, incr); \
         PRUword _p = _a->avail; \
         PRUword _q = _p + _incr; \
         if (_p == (PRUword)(p) + PL_ARENA_ALIGN(pool, size) && \
             _q <= _a->limit) { \
+            PL_MAKE_MEM_UNDEFINED((void *)((PRUword)(p) + size), incr); \
             _a->avail = _q; \
             PL_ArenaCountInplaceGrowth(pool, size, incr); \
         } else { \
             p = PL_ArenaGrow(pool, p, size, incr); \
         } \
         PL_ArenaCountGrowth(pool, size, incr); \
     PR_END_MACRO
 
 #define PL_ARENA_MARK(pool) ((void *) (pool)->current->avail)
 #define PR_UPTRDIFF(p,q) ((PRUword)(p) - (PRUword)(q))
 
 #define PL_CLEAR_UNUSED_PATTERN(a, pattern) \
-	   (PR_ASSERT((a)->avail <= (a)->limit), \
-	   memset((void*)(a)->avail, (pattern), (a)->limit - (a)->avail))
+    PR_BEGIN_MACRO \
+        PR_ASSERT((a)->avail <= (a)->limit); \
+        PL_MAKE_MEM_UNDEFINED((void*)(a)->avail, (a)->limit - (a)->avail); \
+        memset((void*)(a)->avail, (pattern), (a)->limit - (a)->avail); \
+    PR_END_MACRO
 #ifdef DEBUG
 #define PL_FREE_PATTERN 0xDA
 #define PL_CLEAR_UNUSED(a) PL_CLEAR_UNUSED_PATTERN((a), PL_FREE_PATTERN)
-#define PL_CLEAR_ARENA(a)  memset((void*)(a), PL_FREE_PATTERN, \
-                           (a)->limit - (PRUword)(a))
+#define PL_CLEAR_ARENA(a) \
+    PR_BEGIN_MACRO \
+        PL_MAKE_MEM_UNDEFINED((void*)(a), (a)->limit - (PRUword)(a)); \
+        memset((void*)(a), PL_FREE_PATTERN, (a)->limit - (PRUword)(a)); \
+    PR_END_MACRO
 #else
 #define PL_CLEAR_UNUSED(a)
 #define PL_CLEAR_ARENA(a)
 #endif
 
 #define PL_ARENA_RELEASE(pool, mark) \
     PR_BEGIN_MACRO \
         char *_m = (char *)(mark); \
         PLArena *_a = (pool)->current; \
         if (PR_UPTRDIFF(_m, _a->base) <= PR_UPTRDIFF(_a->avail, _a->base)) { \
             _a->avail = (PRUword)PL_ARENA_ALIGN(pool, _m); \
             PL_CLEAR_UNUSED(_a); \
+            PL_MAKE_MEM_NOACCESS((void*)_a->avail, _a->limit - _a->avail); \
             PL_ArenaCountRetract(pool, _m); \
         } else { \
             PL_ArenaRelease(pool, _m); \
         } \
         PL_ArenaCountRelease(pool, _m); \
     PR_END_MACRO
 
 #ifdef PL_ARENAMETER