Opened 9 months ago

Closed 9 months ago

#15906 closed bug (fixed)

Stable name allocation causes heap corruption when GC is triggered in the primop

Reported by: osa1 Owned by:
Priority: highest Milestone: 8.6.3
Component: Runtime System Version: 8.7
Keywords: Cc: simonmar
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Test Case:
Blocked By: Blocking:
Related Tickets: Differential Rev(s): Phab:D5342
Wiki Page:

Description

The error is originally reported in #15241, and is caught by the test memo001 when run with -debug (which only happens in sanity way currently).

Here's the problem. mkStableName# is defined like this:

stg_makeStableNamezh ( P_ obj )
{
    W_ index, sn_obj;

    (index) = ccall lookupStableName(obj "ptr");

    /* Is there already a StableName for this heap object?
     *  stable_name_table is a pointer to an array of snEntry structs.
     */
    if ( snEntry_sn_obj(W_[stable_name_table] + index*SIZEOF_snEntry) == NULL ) {
        ALLOC_PRIM (SIZEOF_StgStableName); <------------ PROBLEM HERE ----------
        sn_obj = Hp - SIZEOF_StgStableName + WDS(1);
        SET_HDR(sn_obj, stg_STABLE_NAME_info, CCCS);
        StgStableName_sn(sn_obj) = index;
        snEntry_sn_obj(W_[stable_name_table] + index*SIZEOF_snEntry) = sn_obj;
    } else {
        sn_obj = snEntry_sn_obj(W_[stable_name_table] + index*SIZEOF_snEntry);
    }

    return (sn_obj);
}

There's a problem in the annotated line: if we allocate a snEntry in the stable name table, but run out of heap to actually allocate the StgStableName we call GC with incorrect snEntry contents. As a reminder, this is snEntry:

typedef struct {
    StgPtr  addr;        // Haskell object when entry is in use, next free
                         // entry (NULL when this is the last free entry)
                         // otherwise. May be NULL temporarily during GC (when
                         // pointee dies).

    StgPtr  old;         // Old Haskell object, used during GC

    StgClosure *sn_obj;  // The StableName object, or NULL when the entry is
                         // free
} snEntry;

In summary, sn_obj == NULL means the entry is free. When we trigger the GC after allocating the snEntry but before allocating the StgStableName, we end up calling the GC with sn_obj == NULL even though the snEntry is not actually free. In particular, the addr field should be updated by gcStableNameTable, but it's currently not because gcStableNameTable sees sn_obj as NULL and skips the entry.

The is caught by memo001 when run with -debug.

I already have a fix and will submit a patch soon.

Change History (4)

comment:1 Changed 9 months ago by osa1

Cc: simonmar added
Differential Rev(s): Phab:D5342
Status: newpatch

comment:2 Changed 9 months ago by Ömer Sinan Ağacan <omeragacan@…>

In 691aa715/ghc:

Fix heap corruption during stable name allocation

See #15906 for the problem. To fix we simply call `allocate()` instead of
`ALLOC_PRIM()`. `allocate()` does not trigger GC when the nursery is full,
instead it extends it.

Test Plan:
This validates. memo001 now passes with `-debug` compile parameter. I'll add
another test that runs memo001 with `-debug` once I figure out how to use
stdout files for multiple tests.

Reviewers: simonmar, bgamari, erikd

Reviewed By: simonmar

Subscribers: rwbarton, carter

GHC Trac Issues: #15906

Differential Revision: https://phabricator.haskell.org/D5342

comment:3 Changed 9 months ago by osa1

Status: patchmerge

comment:4 Changed 9 months ago by bgamari

Resolution: fixed
Status: mergeclosed
Note: See TracTickets for help on using tickets.