Simon Jakobi pushed to branch wip/sjakobi/mr16259 at Glasgow Haskell Compiler / GHC

Commits:

4 changed files:

Changes:

  • libraries/ghc-internal/include/RtsIfaceSymbols.h
    ... ... @@ -59,6 +59,7 @@ CLOSURE(GHCziInternalziExceptionziType, underflowException_closure)
    59 59
     CLOSURE(GHCziInternalziExceptionziType, overflowException_closure)
    
    60 60
     INFO_TBL(GHCziInternalziCString, unpackCStringzh_info)
    
    61 61
     INFO_TBL(GHCziInternalziCString, unpackCStringUtf8zh_info)
    
    62
    +INFO_TBL(GHCziInternalziHeapziClosures, Box_con_info)
    
    62 63
     #if defined(wasm32_HOST_ARCH) && defined(__PIC__)
    
    63 64
     CLOSURE(GHCziInternalziWasmziPrimziImports, raiseJSException_closure)
    
    64 65
     INFO_TBL(GHCziInternalziWasmziPrimziTypes, JSVal_con_info)
    

  • rts/Prelude.h
    ... ... @@ -84,3 +84,4 @@ extern StgClosure ZCMain_main_closure;
    84 84
     #define FunPtr_con_info           ghc_hs_iface->FunPtr_con_info
    
    85 85
     #define StablePtr_static_info     ghc_hs_iface->StablePtr_static_info
    
    86 86
     #define StablePtr_con_info        ghc_hs_iface->StablePtr_con_info
    
    87
    +#define Box_con_info              ghc_hs_iface->Box_con_info

  • rts/include/rts/RtsToHsIface.h
    ... ... @@ -60,6 +60,7 @@ typedef struct {
    60 60
         StgClosure *overflowException_closure;  // GHC.Internal.Exception.Type.overflowException_closure
    
    61 61
         const StgInfoTable *unpackCStringzh_info;  // GHC.Internal.CString.unpackCStringzh_info
    
    62 62
         const StgInfoTable *unpackCStringUtf8zh_info;  // GHC.Internal.CString.unpackCStringUtf8zh_info
    
    63
    +    const StgInfoTable *Box_con_info;  // GHC.Internal.Heap.Closures.Box_con_info
    
    63 64
     #if defined(wasm32_HOST_ARCH)
    
    64 65
         StgClosure *raiseJSException_closure;  // GHC.Internal.Wasm.Prim.Imports.raiseJSException_closure
    
    65 66
         const StgInfoTable *JSVal_con_info;  // GHC.Internal.Wasm.Prim.Types.JSVal_con_info
    

  • rts/sm/Sanity.c
    ... ... @@ -25,6 +25,7 @@
    25 25
     #include "Sanity.h"
    
    26 26
     #include "Schedule.h"
    
    27 27
     #include "Apply.h"
    
    28
    +#include "Prelude.h"
    
    28 29
     #include "Printer.h"
    
    29 30
     #include "Arena.h"
    
    30 31
     #include "RetainerProfile.h"
    
    ... ... @@ -42,6 +43,7 @@ int isHeapAlloced ( StgPtr p);
    42 43
     static void  checkSmallBitmap    ( StgPtr payload, StgWord bitmap, uint32_t );
    
    43 44
     static void  checkLargeBitmap    ( StgPtr payload, StgLargeBitmap*, uint32_t );
    
    44 45
     static void  checkClosureShallow ( const StgClosure * );
    
    46
    +static void  checkPtrTag         ( const StgClosure *, bool );
    
    45 47
     
    
    46 48
     static void  checkCompactObjects (bdescr *bd);
    
    47 49
     
    
    ... ... @@ -72,6 +74,7 @@ checkSmallBitmap( StgPtr payload, StgWord bitmap, uint32_t size )
    72 74
         for(i = 0; i < size; i++, bitmap >>= 1 ) {
    
    73 75
             if ((bitmap & 1) == 0) {
    
    74 76
                 checkClosureShallow((StgClosure *)payload[i]);
    
    77
    +            checkPtrTag((StgClosure *)payload[i], false);
    
    75 78
             }
    
    76 79
         }
    
    77 80
     }
    
    ... ... @@ -89,11 +92,126 @@ checkLargeBitmap( StgPtr payload, StgLargeBitmap* large_bitmap, uint32_t size )
    89 92
             for(; i < size && j < BITS_IN(W_); j++, i++, bitmap >>= 1 ) {
    
    90 93
                 if ((bitmap & 1) == 0) {
    
    91 94
                     checkClosureShallow((StgClosure *)payload[i]);
    
    95
    +                checkPtrTag((StgClosure *)payload[i], false);
    
    92 96
                 }
    
    93 97
             }
    
    94 98
         }
    
    95 99
     }
    
    96 100
     
    
    101
    +/* Note [Sanity-checking pointer tags]
    
    102
    + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    103
    + * checkPtrTag asserts the pointer-tagging invariant (#23173) at rest: a
    
    104
    + * pointer to a constructor carries its constructor tag (see tagConstr in
    
    105
    + * ClosureMacros.h and get_iptr_tag in sm/Compact.c), and a pointer to a boxed
    
    106
    + * unlifted primitive (MVar#, MutVar#, the arrays, ...) carries tag 1 (see
    
    107
    + * Note [Pointer tagging of unlifted boxed primitives] in GHC.StgToCmm.Prim).
    
    108
    + * The invariant is otherwise enforced only by crashing entry code, which
    
    109
    + * catches a stripped tag only if the pointer is subsequently entered; this
    
    110
    + * check catches tag-stripping pointer-rewriting paths (Evac, Compact,
    
    111
    + * NonMovingShortcut, ...) mechanically on every sanity-checked GC.
    
    112
    + *
    
    113
    + * It is called only on user-level fields (stack bitmap slots, PAP/AP
    
    114
    + * payloads, constructor/fun/thunk payloads, array elements, MutVar/TVar/MVar
    
    115
    + * values, IND indirectees), because the RTS also holds internal untagged
    
    116
    + * links. The rules exempt:
    
    117
    + *
    
    118
    + *  - static constructors: RTS sentinels (stg_END_TSO_QUEUE_closure, ...) are
    
    119
    + *    CONSTR_NOCAFs that C code stores untagged, e.g. as an empty MVar's
    
    120
    + *    value, so only heap-allocated constructors are checked;
    
    121
    + *
    
    122
    + *  - large-family constructors (con_tag >= TAG_MASK): tag is capped at
    
    123
    + *    TAG_MASK, so no exact requirement is asserted;
    
    124
    + *
    
    125
    + *  - WEAK, TSO, STACK, BLOCKING_QUEUE, PRIM, MUT_PRIM: user-level references
    
    126
    + *    (Weak#, ThreadId#, ...) to these are tagged, but legitimate untagged
    
    127
    + *    RTS-internal links (weak_ptr_list, run queues, tso->_link, STM
    
    128
    + *    structures) reach the same traversals;
    
    129
    + *
    
    130
    + *  - C_FINALIZER_LIST nodes: although their info table is a CONSTR, they
    
    131
    + *    are RTS-internal. All references to them — StgWeak.cfinalizers and the
    
    132
    + *    nodes' link fields — are untagged links built by stg_addCFinalizerToWeakzh
    
    133
    + *    (PrimOps.cmm) and walked raw by runCFinalizers (Weak.c); user code never
    
    134
    + *    holds a reference to one. (The compacting GC preserves untaggedness:
    
    135
    + *    unthread re-applies get_iptr_tag only to originally-tagged references.)
    
    136
    + *
    
    137
    + *  - fields of ghc-heap's Box (GHC.Internal.Heap.Closures): `data Box = Box
    
    138
    + *    Any` wraps a pointer word captured verbatim by heap/stack introspection
    
    139
    + *    (unpackClosure#, ghc-heap's stack decoding), so it carries whatever tag
    
    140
    + *    the source bits had — possibly none. Box is recognized via
    
    141
    + *    ghc_hs_iface->Box_con_info, NULL-guarded since sanity checks can run
    
    142
    + *    before ghc-internal registers the interface;
    
    143
    + *
    
    144
    + *  - BLACKHOLE indirectees (no call site on that field): tag 0 there means
    
    145
    + *    "not yet updated". Plain IND indirectees are checked;
    
    146
    + *
    
    147
    + *  - bitmap-walked slots (stack frames, PAP/AP payloads; heap_field =
    
    148
    + *    false): hand-written Cmm legitimately stores untagged pointers there.
    
    149
    + *    Codegen untags unlifted boxed primop arguments at the Cmm call
    
    150
    + *    boundary, and generic RTS frames save those already-untagged arguments
    
    151
    + *    on the stack (the stg_block_{take,read,put}mvar frames and the
    
    152
    + *    stg_gc_prim_* heap-check-retry frames in HeapStackCheck.cmm); Cmm code
    
    153
    + *    also keeps deliberately untagged working pointers live across calls
    
    154
    + *    (e.g. stg_compactAddWorkerzh's "p"), landing them in return-frame
    
    155
    + *    slots. Such slots hence get no constructor rule, and the unlifted-
    
    156
    + *    primitive rule is relaxed to tag 0-or-1 (still catching corrupt tags).
    
    157
    + *    The strict rules apply to heap fields, where all the tag-stripping GC
    
    158
    + *    bugs lived.
    
    159
    + */
    
    160
    +static void
    
    161
    +checkPtrTag( const StgClosure *q, bool heap_field )
    
    162
    +{
    
    163
    +    const StgClosure *p = UNTAG_CONST_CLOSURE(q);
    
    164
    +    const StgInfoTable *raw_info = ACQUIRE_LOAD(&p->header.info);
    
    165
    +    if (IS_FORWARDING_PTR(raw_info)) return;
    
    166
    +    const StgInfoTable *info = INFO_PTR_TO_STRUCT(raw_info);
    
    167
    +
    
    168
    +    switch (info->type) {
    
    169
    +    case CONSTR:
    
    170
    +    case CONSTR_1_0:
    
    171
    +    case CONSTR_0_1:
    
    172
    +    case CONSTR_2_0:
    
    173
    +    case CONSTR_1_1:
    
    174
    +    case CONSTR_0_2:
    
    175
    +    case CONSTR_NOCAF:
    
    176
    +    {
    
    177
    +        // RTS-internal untagged links; see the C_FINALIZER_LIST bullet in
    
    178
    +        // Note [Sanity-checking pointer tags].
    
    179
    +        if (raw_info == &stg_C_FINALIZER_LIST_info) {
    
    180
    +            break;
    
    181
    +        }
    
    182
    +        StgWord con_tag = (StgWord)info->srt + 1;
    
    183
    +        if (heap_field && con_tag <= TAG_MASK && HEAP_ALLOCED((StgPtr)p)) {
    
    184
    +            ASSERT(GET_CLOSURE_TAG(q) == con_tag);
    
    185
    +        }
    
    186
    +        break;
    
    187
    +    }
    
    188
    +
    
    189
    +    case ARR_WORDS:
    
    190
    +    case MUT_ARR_PTRS_CLEAN:
    
    191
    +    case MUT_ARR_PTRS_DIRTY:
    
    192
    +    case MUT_ARR_PTRS_FROZEN_CLEAN:
    
    193
    +    case MUT_ARR_PTRS_FROZEN_DIRTY:
    
    194
    +    case SMALL_MUT_ARR_PTRS_CLEAN:
    
    195
    +    case SMALL_MUT_ARR_PTRS_DIRTY:
    
    196
    +    case SMALL_MUT_ARR_PTRS_FROZEN_CLEAN:
    
    197
    +    case SMALL_MUT_ARR_PTRS_FROZEN_DIRTY:
    
    198
    +    case MUT_VAR_CLEAN:
    
    199
    +    case MUT_VAR_DIRTY:
    
    200
    +    case MVAR_CLEAN:
    
    201
    +    case MVAR_DIRTY:
    
    202
    +    case TVAR:
    
    203
    +        if (heap_field) {
    
    204
    +            ASSERT(GET_CLOSURE_TAG(q) == 1);
    
    205
    +        } else {
    
    206
    +            ASSERT(GET_CLOSURE_TAG(q) <= 1);
    
    207
    +        }
    
    208
    +        break;
    
    209
    +
    
    210
    +    default:
    
    211
    +        break;
    
    212
    +    }
    
    213
    +}
    
    214
    +
    
    97 215
     /*
    
    98 216
      * check that it looks like a valid closure - without checking its payload
    
    99 217
      * used to avoid recursion between checking PAPs and checking stack
    
    ... ... @@ -102,6 +220,8 @@ checkLargeBitmap( StgPtr payload, StgLargeBitmap* large_bitmap, uint32_t size )
    102 220
     static void
    
    103 221
     checkClosureShallow( const StgClosure* p )
    
    104 222
     {
    
    223
    +    // No checkPtrTag here: checkCompactObjects calls this on raw
    
    224
    +    // (necessarily untagged) object addresses, not on stored pointers.
    
    105 225
         ASSERT(LOOKS_LIKE_CLOSURE_PTR(UNTAG_CONST_CLOSURE(p)));
    
    106 226
     }
    
    107 227
     
    
    ... ... @@ -129,10 +249,12 @@ checkStackFrame( StgPtr c )
    129 249
         case STOP_FRAME:
    
    130 250
         case RET_SMALL:
    
    131 251
         case ANN_FRAME:
    
    252
    +    {
    
    132 253
             size = BITMAP_SIZE(info->i.layout.bitmap);
    
    133 254
             checkSmallBitmap((StgPtr)c + 1,
    
    134 255
                              BITMAP_BITS(info->i.layout.bitmap), size);
    
    135 256
             return 1 + size;
    
    257
    +    }
    
    136 258
     
    
    137 259
         case RET_BCO: {
    
    138 260
             StgBCO *bco;
    
    ... ... @@ -377,6 +499,8 @@ checkClosure( const StgClosure* p )
    377 499
             ASSERT(LOOKS_LIKE_CLOSURE_PTR(mvar->head));
    
    378 500
             ASSERT(LOOKS_LIKE_CLOSURE_PTR(mvar->tail));
    
    379 501
             ASSERT(LOOKS_LIKE_CLOSURE_PTR(mvar->value));
    
    502
    +        // head/tail are RTS-internal TSO queue links; only value is user-level
    
    503
    +        checkPtrTag(mvar->value, true);
    
    380 504
             return sizeofW(StgMVar);
    
    381 505
           }
    
    382 506
     
    
    ... ... @@ -390,6 +514,7 @@ checkClosure( const StgClosure* p )
    390 514
             uint32_t i;
    
    391 515
             for (i = 0; i < info->layout.payload.ptrs; i++) {
    
    392 516
               ASSERT(LOOKS_LIKE_CLOSURE_PTR(((StgThunk *)p)->payload[i]));
    
    517
    +          checkPtrTag(((StgThunk *)p)->payload[i], true);
    
    393 518
             }
    
    394 519
             return thunk_sizeW_fromITBL(info);
    
    395 520
           }
    
    ... ... @@ -407,14 +532,33 @@ checkClosure( const StgClosure* p )
    407 532
         case CONSTR_1_1:
    
    408 533
         case CONSTR_0_2:
    
    409 534
         case CONSTR_2_0:
    
    410
    -    case BLACKHOLE:
    
    411
    -    case PRIM:
    
    412
    -    case MUT_PRIM:
    
    413 535
         case MUT_VAR_CLEAN:
    
    414 536
         case MUT_VAR_DIRTY:
    
    415 537
         case TVAR:
    
    416 538
         case THUNK_STATIC:
    
    417 539
         case FUN_STATIC:
    
    540
    +        {
    
    541
    +            // ghc-heap's Box holds a raw captured pointer word; see the Box
    
    542
    +            // bullet in Note [Sanity-checking pointer tags].
    
    543
    +            bool box = ghc_hs_iface != NULL
    
    544
    +                && ACQUIRE_LOAD(&p->header.info) == Box_con_info;
    
    545
    +            uint32_t i;
    
    546
    +            for (i = 0; i < info->layout.payload.ptrs; i++) {
    
    547
    +                ASSERT(LOOKS_LIKE_CLOSURE_PTR(p->payload[i]));
    
    548
    +                if (!box) {
    
    549
    +                    checkPtrTag(p->payload[i], true);
    
    550
    +                }
    
    551
    +            }
    
    552
    +            return sizeW_fromITBL(info);
    
    553
    +        }
    
    554
    +
    
    555
    +    // As above, but without checkPtrTag: a BLACKHOLE indirectee legitimately
    
    556
    +    // carries tag 0 ("not yet updated"), and PRIM/MUT_PRIM/COMPACT_NFDATA
    
    557
    +    // payloads are RTS-internal links.
    
    558
    +    // See Note [Sanity-checking pointer tags].
    
    559
    +    case BLACKHOLE:
    
    560
    +    case PRIM:
    
    561
    +    case MUT_PRIM:
    
    418 562
         case COMPACT_NFDATA:
    
    419 563
             {
    
    420 564
                 uint32_t i;
    
    ... ... @@ -480,6 +624,7 @@ checkClosure( const StgClosure* p )
    480 624
                  */
    
    481 625
                 StgInd *ind = (StgInd *)p;
    
    482 626
                 ASSERT(LOOKS_LIKE_CLOSURE_PTR(ind->indirectee));
    
    627
    +            checkPtrTag(ind->indirectee, true);
    
    483 628
                 return sizeofW(StgInd);
    
    484 629
             }
    
    485 630
     
    
    ... ... @@ -529,6 +674,7 @@ checkClosure( const StgClosure* p )
    529 674
                 uint32_t i;
    
    530 675
                 for (i = 0; i < a->ptrs; i++) {
    
    531 676
                     ASSERT(LOOKS_LIKE_CLOSURE_PTR(a->payload[i]));
    
    677
    +                checkPtrTag(a->payload[i], true);
    
    532 678
                 }
    
    533 679
                 return mut_arr_ptrs_sizeW(a);
    
    534 680
             }
    
    ... ... @@ -541,6 +687,7 @@ checkClosure( const StgClosure* p )
    541 687
                 StgSmallMutArrPtrs *a = (StgSmallMutArrPtrs *)p;
    
    542 688
                 for (uint32_t i = 0; i < a->ptrs; i++) {
    
    543 689
                     ASSERT(LOOKS_LIKE_CLOSURE_PTR(a->payload[i]));
    
    690
    +                checkPtrTag(a->payload[i], true);
    
    544 691
                 }
    
    545 692
                 return small_mut_arr_ptrs_sizeW(a);
    
    546 693
             }