Cheng Shao pushed to branch wip/terrorjack/asan at Glasgow Haskell Compiler / GHC

Commits:

12 changed files:

Changes:

  • rts/Hash.c
    ... ... @@ -283,6 +283,7 @@ allocHashList (HashTable *table)
    283 283
         if (table->freeList != NULL) {
    
    284 284
             HashList *hl = table->freeList;
    
    285 285
             table->freeList = hl->next;
    
    286
    +        __asan_unpoison_memory_region(hl, offsetof(HashList, next));
    
    286 287
             return hl;
    
    287 288
         } else {
    
    288 289
             /* We allocate one block of memory which contains:
    
    ... ... @@ -302,8 +303,11 @@ allocHashList (HashTable *table)
    302 303
     
    
    303 304
             table->freeList = hl + 1;
    
    304 305
             HashList *p = table->freeList;
    
    305
    -        for (; p < hl + HCHUNK - 1; p++)
    
    306
    +        for (; p < hl + HCHUNK - 1; p++) {
    
    307
    +            __asan_poison_memory_region(p, offsetof(HashList, next));
    
    306 308
                 p->next = p + 1;
    
    309
    +        }
    
    310
    +        __asan_poison_memory_region(p, offsetof(HashList, next));
    
    307 311
             p->next = NULL;
    
    308 312
             return hl;
    
    309 313
         }
    
    ... ... @@ -318,6 +322,7 @@ freeHashList (HashTable *table, HashList *hl)
    318 322
         // HashListChunks.
    
    319 323
         hl->next = table->freeList;
    
    320 324
         table->freeList = hl;
    
    325
    +    __asan_poison_memory_region(hl, offsetof(HashList, next));
    
    321 326
     }
    
    322 327
     
    
    323 328
     STATIC_INLINE void
    
    ... ... @@ -388,9 +393,10 @@ removeHashTable_inlined(HashTable *table, StgWord key, const void *data,
    388 393
                     table->dir[segment][index] = hl->next;
    
    389 394
                 else
    
    390 395
                     prev->next = hl->next;
    
    396
    +            void *hl_data = (void*)hl->data;
    
    391 397
                 freeHashList(table,hl);
    
    392 398
                 table->kcount--;
    
    393
    -            return (void *) hl->data;
    
    399
    +            return hl_data;
    
    394 400
             }
    
    395 401
             prev = hl;
    
    396 402
         }
    

  • rts/Task.c
    ... ... @@ -183,6 +183,7 @@ freeTask (Task *task)
    183 183
             stgFree(incall);
    
    184 184
         }
    
    185 185
         for (incall = task->spare_incalls; incall != NULL; incall = next) {
    
    186
    +        __asan_unpoison_memory_region(incall, sizeof(InCall));
    
    186 187
             next = incall->next;
    
    187 188
             stgFree(incall);
    
    188 189
         }
    
    ... ... @@ -252,6 +253,7 @@ newInCall (Task *task)
    252 253
     
    
    253 254
         if (task->spare_incalls != NULL) {
    
    254 255
             incall = task->spare_incalls;
    
    256
    +        __asan_unpoison_memory_region(incall, sizeof(InCall));
    
    255 257
             task->spare_incalls = incall->next;
    
    256 258
             task->n_spare_incalls--;
    
    257 259
         } else {
    
    ... ... @@ -283,6 +285,7 @@ endInCall (Task *task)
    283 285
             stgFree(incall);
    
    284 286
         } else {
    
    285 287
             incall->next = task->spare_incalls;
    
    288
    +        __asan_poison_memory_region(incall, sizeof(InCall));
    
    286 289
             task->spare_incalls = incall;
    
    287 290
             task->n_spare_incalls++;
    
    288 291
         }
    

  • rts/include/Stg.h
    ... ... @@ -331,6 +331,7 @@ external prototype return neither of these types to workaround #11395.
    331 331
     #include "stg/MachRegsForHost.h"
    
    332 332
     #include "stg/Regs.h"
    
    333 333
     #include "stg/Ticky.h"
    
    334
    +#include "rts/ASANUtils.h"
    
    334 335
     #include "rts/TSANUtils.h"
    
    335 336
     
    
    336 337
     #if IN_STG_CODE
    

  • rts/include/rts/ASANUtils.h
    1
    +#pragma once
    
    2
    +
    
    3
    +#if defined(__SANITIZE_ADDRESS__)
    
    4
    +#define ASAN_ENABLED
    
    5
    +#elif defined(__has_feature)
    
    6
    +#if __has_feature(address_sanitizer)
    
    7
    +#define ASAN_ENABLED
    
    8
    +#endif
    
    9
    +#endif
    
    10
    +
    
    11
    +#if defined(ASAN_ENABLED)
    
    12
    +#include <sanitizer/asan_interface.h>
    
    13
    +#else
    
    14
    +
    
    15
    +#include <stddef.h>
    
    16
    +
    
    17
    +static inline void __asan_poison_memory_region(void const volatile *addr
    
    18
    +                                               __attribute__((unused)),
    
    19
    +                                               size_t size
    
    20
    +                                               __attribute__((unused))) {}
    
    21
    +
    
    22
    +static inline void __asan_unpoison_memory_region(void const volatile *addr
    
    23
    +                                                 __attribute__((unused)),
    
    24
    +                                                 size_t size
    
    25
    +                                                 __attribute__((unused))) {}
    
    26
    +
    
    27
    +#endif

  • rts/rts.cabal
    ... ... @@ -292,6 +292,7 @@ library
    292 292
                             -- ^ generated
    
    293 293
                             rts/ghc_ffi.h
    
    294 294
                             rts/Adjustor.h
    
    295
    +                        rts/ASANUtils.h
    
    295 296
                             rts/ExecPage.h
    
    296 297
                             rts/BlockSignals.h
    
    297 298
                             rts/Bytecodes.h
    

  • rts/sm/BlockAlloc.c
    ... ... @@ -241,6 +241,9 @@ tail_of (bdescr *bd)
    241 241
     STATIC_INLINE void
    
    242 242
     initGroup(bdescr *head)
    
    243 243
     {
    
    244
    +  __asan_unpoison_memory_region(head,
    
    245
    +      stg_min((W_)head->blocks, (W_)BLOCKS_PER_MBLOCK) * sizeof(bdescr));
    
    246
    +
    
    244 247
       head->free   = head->start;
    
    245 248
       head->link   = NULL;
    
    246 249
     
    
    ... ... @@ -261,6 +264,8 @@ initGroup(bdescr *head)
    261 264
           head[i].flags = 0;
    
    262 265
       }
    
    263 266
     #endif
    
    267
    +
    
    268
    +  __asan_unpoison_memory_region(head->start, (W_)head->blocks * BLOCK_SIZE);
    
    264 269
     }
    
    265 270
     
    
    266 271
     #if SIZEOF_VOID_P == SIZEOF_LONG
    
    ... ... @@ -308,6 +313,7 @@ setup_tail (bdescr *bd)
    308 313
         bdescr *tail;
    
    309 314
         tail = tail_of(bd);
    
    310 315
         if (tail != bd) {
    
    316
    +        __asan_unpoison_memory_region(tail, sizeof(bdescr));
    
    311 317
             tail->blocks = 0;
    
    312 318
             tail->free = 0;
    
    313 319
             tail->link = bd;
    
    ... ... @@ -325,6 +331,7 @@ split_free_block (bdescr *bd, uint32_t node, W_ n, uint32_t ln /* log_2_ceil(n)
    325 331
         ASSERT(bd->blocks > n);
    
    326 332
         dbl_link_remove(bd, &free_list[node][ln]);
    
    327 333
         fg = bd + bd->blocks - n; // take n blocks off the end
    
    334
    +    __asan_unpoison_memory_region(fg, sizeof(bdescr));
    
    328 335
         fg->blocks = n;
    
    329 336
         bd->blocks -= n;
    
    330 337
         setup_tail(bd);
    
    ... ... @@ -474,6 +481,8 @@ alloc_mega_group (uint32_t node, StgWord mblocks)
    474 481
             bd = alloc_mega_group_from_free_list(&deferred_free_mblock_list[node], n, &best);
    
    475 482
             if(bd)
    
    476 483
             {
    
    484
    +            __asan_unpoison_memory_region(MBLOCK_ROUND_DOWN(bd), FIRST_BLOCK_OFF);
    
    485
    +            __asan_unpoison_memory_region(bd->start, (W_)bd->blocks * BLOCK_SIZE);
    
    477 486
                 return bd;
    
    478 487
             }
    
    479 488
             else if(!best)
    
    ... ... @@ -490,6 +499,8 @@ alloc_mega_group (uint32_t node, StgWord mblocks)
    490 499
     
    
    491 500
         if (bd)
    
    492 501
         {
    
    502
    +        __asan_unpoison_memory_region(MBLOCK_ROUND_DOWN(bd), FIRST_BLOCK_OFF);
    
    503
    +        __asan_unpoison_memory_region(bd->start, (W_)bd->blocks * BLOCK_SIZE);
    
    493 504
             return bd;
    
    494 505
         }
    
    495 506
         else if (best)
    
    ... ... @@ -500,6 +511,7 @@ alloc_mega_group (uint32_t node, StgWord mblocks)
    500 511
                               (best_mblocks-mblocks)*MBLOCK_SIZE);
    
    501 512
     
    
    502 513
             best->blocks = MBLOCK_GROUP_BLOCKS(best_mblocks - mblocks);
    
    514
    +        __asan_unpoison_memory_region(MBLOCK_ROUND_DOWN(bd), mblocks * MBLOCK_SIZE);
    
    503 515
             initMBlock(MBLOCK_ROUND_DOWN(bd), node);
    
    504 516
         }
    
    505 517
         else
    
    ... ... @@ -880,6 +892,11 @@ free_mega_group (bdescr *mg)
    880 892
     
    
    881 893
             IF_DEBUG(sanity, checkFreeListSanity());
    
    882 894
         }
    
    895
    +
    
    896
    +    __asan_poison_memory_region(mg->start, (W_)mg->blocks * BLOCK_SIZE);
    
    897
    +    // Only the head bdescr of a free mega group carries meaning; poison the
    
    898
    +    // rest of the first mblock's descriptor area.
    
    899
    +    __asan_poison_memory_region(mg + 1, ((W_)BLOCKS_PER_MBLOCK - 1) * sizeof(bdescr));
    
    883 900
     }
    
    884 901
     
    
    885 902
     static void
    
    ... ... @@ -927,6 +944,9 @@ free_deferred_mega_groups (uint32_t node)
    927 944
             // coalesce forwards
    
    928 945
             coalesce_mblocks(mg);
    
    929 946
     
    
    947
    +        __asan_poison_memory_region(mg->start, (W_)mg->blocks * BLOCK_SIZE);
    
    948
    +        __asan_poison_memory_region(mg + 1, ((W_)BLOCKS_PER_MBLOCK - 1) * sizeof(bdescr));
    
    949
    +
    
    930 950
             // initialize search for next round
    
    931 951
             prev = mg;
    
    932 952
             bd = prev->link;
    
    ... ... @@ -979,7 +999,9 @@ freeGroup(bdescr *p)
    979 999
       RELAXED_STORE(&p->gen, NULL);
    
    980 1000
       RELAXED_STORE(&p->gen_no, 0);
    
    981 1001
       /* fill the block group with garbage if sanity checking is on */
    
    982
    -  IF_DEBUG(zero_on_gc, memset(p->start, 0xaa, (W_)p->blocks * BLOCK_SIZE));
    
    1002
    +  IF_DEBUG(zero_on_gc,
    
    1003
    +           __asan_unpoison_memory_region(p->start, (W_)p->blocks * BLOCK_SIZE);
    
    1004
    +           memset(p->start, 0xaa, (W_)p->blocks * BLOCK_SIZE));
    
    983 1005
     
    
    984 1006
       if (p->blocks == 0) barf("freeGroup: block size is zero");
    
    985 1007
     
    
    ... ... @@ -1050,6 +1072,13 @@ freeGroup(bdescr *p)
    1050 1072
       setup_tail(p);
    
    1051 1073
       free_list_insert(node,p);
    
    1052 1074
     
    
    1075
    +  __asan_poison_memory_region(p->start, (W_)p->blocks * BLOCK_SIZE);
    
    1076
    +  // Only the head and tail bdescrs of a free group carry meaning; poison the
    
    1077
    +  // interior ones to catch stale Bdescr() lookups through dangling pointers.
    
    1078
    +  if (p->blocks > 2) {
    
    1079
    +      __asan_poison_memory_region(p + 1, ((W_)p->blocks - 2) * sizeof(bdescr));
    
    1080
    +  }
    
    1081
    +
    
    1053 1082
       IF_DEBUG(sanity, checkFreeListSanity());
    
    1054 1083
     }
    
    1055 1084
     
    
    ... ... @@ -1421,6 +1450,7 @@ reportUnmarkedBlocks (void)
    1421 1450
         debugBelch("Unreachable blocks:\n");
    
    1422 1451
         for (mblock = getFirstMBlock(&state); mblock != NULL;
    
    1423 1452
              mblock = getNextMBlock(&state, mblock)) {
    
    1453
    +        __asan_unpoison_memory_region(mblock, FIRST_BLOCK_OFF);
    
    1424 1454
             for (bd = FIRST_BDESCR(mblock); bd <= LAST_BDESCR(mblock); ) {
    
    1425 1455
                 if (!(bd->flags & BF_KNOWN) && bd->free != (P_)-1) {
    
    1426 1456
                     debugBelch("  %p\n",bd);
    

  • rts/sm/GCUtils.c
    ... ... @@ -348,6 +348,7 @@ alloc_todo_block (gen_workspace *ws, uint32_t size)
    348 348
             } else {
    
    349 349
                 if (gct->free_blocks) {
    
    350 350
                     bd = gct->free_blocks;
    
    351
    +                __asan_unpoison_memory_region(bd->start, (W_)bd->blocks * BLOCK_SIZE);
    
    351 352
                     gct->free_blocks = bd->link;
    
    352 353
                 } else {
    
    353 354
                     // We allocate in chunks of at most 16 blocks, use one
    
    ... ... @@ -357,6 +358,9 @@ alloc_todo_block (gen_workspace *ws, uint32_t size)
    357 358
                     StgWord n_blocks = stg_min(chunk_size, 1 << (MBLOCK_SHIFT - BLOCK_SHIFT - 1));
    
    358 359
                     allocBlocks_sync(n_blocks, &bd);
    
    359 360
                     gct->free_blocks = bd->link;
    
    361
    +                for (bdescr *bd = gct->free_blocks; bd; bd = bd->link) {
    
    362
    +                    __asan_poison_memory_region(bd->start, (W_)bd->blocks * BLOCK_SIZE);
    
    363
    +                }
    
    360 364
                 }
    
    361 365
             }
    
    362 366
             initBdescr(bd, ws->gen, ws->gen->to);
    

  • rts/sm/MBlock.c
    ... ... @@ -641,6 +641,8 @@ getMBlocks(uint32_t n)
    641 641
     
    
    642 642
         ret = getCommittedMBlocks(n);
    
    643 643
     
    
    644
    +    __asan_unpoison_memory_region(ret, (W_)n * MBLOCK_SIZE);
    
    645
    +
    
    644 646
         debugTrace(DEBUG_gc, "allocated %d megablock(s) at %p",n,ret);
    
    645 647
     
    
    646 648
         mblocks_allocated += n;
    
    ... ... @@ -673,6 +675,8 @@ freeMBlocks(void *addr, uint32_t n)
    673 675
     
    
    674 676
         mblocks_allocated -= n;
    
    675 677
     
    
    678
    +    __asan_poison_memory_region(addr, (W_)n * MBLOCK_SIZE);
    
    679
    +
    
    676 680
         decommitMBlocks(addr, n);
    
    677 681
     }
    
    678 682
     
    

  • rts/sm/NonMoving.c
    ... ... @@ -597,6 +597,8 @@ static void nonmovingExitConcurrentWorker(void);
    597 597
     void nonmovingPushFreeSegment(struct NonmovingSegment *seg)
    
    598 598
     {
    
    599 599
         SET_SEGMENT_STATE(seg, FREE);
    
    600
    +    __asan_poison_memory_region(seg->bitmap,
    
    601
    +        (uintptr_t)seg + NONMOVING_SEGMENT_SIZE - (uintptr_t)seg->bitmap);
    
    600 602
         while (true) {
    
    601 603
             struct NonmovingSegment *old = nonmovingHeap.free;
    
    602 604
             seg->link = old;
    

  • rts/sm/NonMovingAllocate.c
    ... ... @@ -118,6 +118,8 @@ static void nonmovingClearBitmap(struct NonmovingSegment *seg)
    118 118
     static void nonmovingInitSegment(struct NonmovingSegment *seg, uint16_t allocator_idx)
    
    119 119
     {
    
    120 120
         bdescr *bd = Bdescr((P_) seg);
    
    121
    +    __asan_unpoison_memory_region(seg->bitmap,
    
    122
    +        (uintptr_t)seg + NONMOVING_SEGMENT_SIZE - (uintptr_t)seg->bitmap);
    
    121 123
         seg->link = NULL;
    
    122 124
         seg->todo_link = NULL;
    
    123 125
         seg->next_free = 0;
    
    ... ... @@ -126,6 +128,8 @@ static void nonmovingInitSegment(struct NonmovingSegment *seg, uint16_t allocato
    126 128
         bd->nonmoving_segment.next_free_snap = 0;
    
    127 129
         bd->u.scan = nonmovingSegmentGetBlock(seg, 0);
    
    128 130
         nonmovingClearBitmap(seg);
    
    131
    +    __asan_poison_memory_region(bd->u.scan,
    
    132
    +        (uintptr_t)seg + NONMOVING_SEGMENT_SIZE - (uintptr_t)bd->u.scan);
    
    129 133
     }
    
    130 134
     
    
    131 135
     /* Initialize a new capability. Must hold SM_LOCK. */
    
    ... ... @@ -229,6 +233,7 @@ static void *nonmovingAllocate_(enum AllocLockMode mode, Capability *cap, StgWor
    229 233
         unsigned int block_count = nonmovingSegmentBlockCount(current);
    
    230 234
         void *ret = nonmovingSegmentGetBlock_(current, block_size, block_count, current->next_free);
    
    231 235
         ASSERT(GET_CLOSURE_TAG(ret) == 0); // check alignment
    
    236
    +    __asan_unpoison_memory_region(ret, block_size);
    
    232 237
     
    
    233 238
         // Advance the current segment's next_free or allocate a new segment if full
    
    234 239
         bool full = advance_next_free(current, block_count);
    

  • rts/sm/NonMovingSweep.c
    ... ... @@ -33,6 +33,7 @@ nonmovingSweepSegment(struct NonmovingSegment *seg)
    33 33
     {
    
    34 34
         ASSERT_SEGMENT_STATE(seg, FILLED_SWEEPING);
    
    35 35
         const nonmoving_block_idx blk_cnt = nonmovingSegmentBlockCount(seg);
    
    36
    +    const uint16_t blk_size = nonmovingSegmentBlockSize(seg);
    
    36 37
         bool found_free = false;
    
    37 38
         bool found_live = false;
    
    38 39
     
    
    ... ... @@ -42,6 +43,8 @@ nonmovingSweepSegment(struct NonmovingSegment *seg)
    42 43
                 found_live = true;
    
    43 44
             } else {
    
    44 45
                 seg->bitmap[i] = 0;
    
    46
    +            __asan_poison_memory_region(
    
    47
    +                nonmovingSegmentGetBlock_(seg, blk_size, blk_cnt, i), blk_size);
    
    45 48
                 if (!found_free) {
    
    46 49
                     // This is the first free block we've found; set next_free,
    
    47 50
                     // next_free_snap, and the scan pointer.
    
    ... ... @@ -57,6 +60,8 @@ nonmovingSweepSegment(struct NonmovingSegment *seg)
    57 60
                 for (; i < nonmovingSegmentBlockCount(seg); ++i) {
    
    58 61
                     if (seg->bitmap[i] != nonmovingMarkEpoch) {
    
    59 62
                         seg->bitmap[i] = 0;
    
    63
    +                    __asan_poison_memory_region(
    
    64
    +                        nonmovingSegmentGetBlock_(seg, blk_size, blk_cnt, i), blk_size);
    
    60 65
                     }
    
    61 66
                 }
    
    62 67
                 return SEGMENT_PARTIAL;
    
    ... ... @@ -113,7 +118,9 @@ void
    113 118
     nonmovingClearSegment(struct NonmovingSegment* seg)
    
    114 119
     {
    
    115 120
         size_t end = ((size_t)seg) + NONMOVING_SEGMENT_SIZE;
    
    121
    +    __asan_unpoison_memory_region(&seg->bitmap, end - (size_t)&seg->bitmap);
    
    116 122
         memset(&seg->bitmap, 0, end - (size_t)&seg->bitmap);
    
    123
    +    __asan_poison_memory_region(&seg->bitmap, end - (size_t)&seg->bitmap);
    
    117 124
     }
    
    118 125
     
    
    119 126
     void
    
    ... ... @@ -124,7 +131,18 @@ nonmovingClearSegmentFreeBlocks(struct NonmovingSegment* seg)
    124 131
             // N.B. nonmovingSweepSegment helpfully clears the bitmap entries of
    
    125 132
             // dead blocks
    
    126 133
             if (nonmovingGetMark(seg, p_idx) == 0) {
    
    127
    -            memset(nonmovingSegmentGetBlock(seg, p_idx), 0, block_size);
    
    134
    +            void *blk = nonmovingSegmentGetBlock(seg, p_idx);
    
    135
    +            if (p_idx >= seg->next_free) {
    
    136
    +                // Free block: it is poisoned, so unpoison for the memset and
    
    137
    +                // re-poison afterwards. Unmarked blocks below next_free were
    
    138
    +                // allocated since the last sweep and are live; leave those
    
    139
    +                // unpoisoned.
    
    140
    +                __asan_unpoison_memory_region(blk, block_size);
    
    141
    +                memset(blk, 0, block_size);
    
    142
    +                __asan_poison_memory_region(blk, block_size);
    
    143
    +            } else {
    
    144
    +                memset(blk, 0, block_size);
    
    145
    +            }
    
    128 146
             }
    
    129 147
         }
    
    130 148
     }
    

  • rts/sm/Storage.c
    ... ... @@ -1242,6 +1242,10 @@ start_new_pinned_block(Capability *cap)
    1242 1242
             ACQUIRE_SM_LOCK;
    
    1243 1243
             bd = allocNursery(cap->node, NULL, PINNED_EMPTY_SIZE);
    
    1244 1244
             RELEASE_SM_LOCK;
    
    1245
    +
    
    1246
    +        for (bdescr *pbd = bd; pbd; pbd = pbd->link) {
    
    1247
    +            __asan_poison_memory_region(pbd->start, (W_)pbd->blocks * BLOCK_SIZE);
    
    1248
    +        }
    
    1245 1249
         }
    
    1246 1250
     
    
    1247 1251
         // Bump up the nursery pointer to avoid the pathological situation
    
    ... ... @@ -1267,6 +1271,7 @@ start_new_pinned_block(Capability *cap)
    1267 1271
         }
    
    1268 1272
     
    
    1269 1273
         cap->pinned_object_empty = bd->link;
    
    1274
    +    __asan_unpoison_memory_region(bd->start, (W_)bd->blocks * BLOCK_SIZE);
    
    1270 1275
         newNurseryBlock(bd);
    
    1271 1276
         if (bd->link != NULL) {
    
    1272 1277
           bd->link->u.back = cap->pinned_object_empty;