Ignore:
Timestamp:
Jan 19, 2017, 6:38:45 PM (8 years ago)
Author:
[email protected]
Message:

Structure::pin() needs to be called while holding a lock
https://bugs.webkit.org/show_bug.cgi?id=167220

Reviewed by Saam Barati.

Imagine this race: the mutator calls pin() and the collector calls visitChildren(),
on the same Structure at the same time. In trunk pin() does not require a lock to be
held and it doesn't grab any locks. Meanwhile visitChildren() grabs the lock, checks
if the structure is pinned, and if not, it removes it by overwriting with zero. Now
imagine how this plays out when pin() runs. Since pin() grabs no locks, it is
irrelevant that visitChildren() grabs any locks. So, visitChildren() might check if
the table is pinned before pin() pins it, and then clear the table after it was
already pinned.

The problem here is that pin() should be holding a lock. We could either make pin()
grab that lock by itself, or what this patch does is makes the caller grab the lock.
This is great because it means that sometimes we don't have to introduce any new
locking.

This fixes a materializePropertyTable() checkOffsetConsistency() crash that happens
very rarely, but I was able to get it to reproduce with run-webkit-tests and
aggressive GC settings.

  • runtime/ConcurrentJSLock.h:
  • runtime/Structure.cpp:

(JSC::Structure::materializePropertyTable):
(JSC::Structure::changePrototypeTransition):
(JSC::Structure::attributeChangeTransition):
(JSC::Structure::toDictionaryTransition):
(JSC::Structure::nonPropertyTransition):
(JSC::Structure::pin):
(JSC::Structure::pinForCaching):
(JSC::Structure::add):

  • runtime/Structure.h:
  • runtime/StructureInlines.h:

(JSC::Structure::checkOffsetConsistency):
(JSC::Structure::add):
(JSC::Structure::addPropertyWithoutTransition):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/runtime/StructureInlines.h

    r210829 r210947  
    244244}
    245245
    246 ALWAYS_INLINE bool Structure::checkOffsetConsistency() const
    247 {
    248     PropertyTable* propertyTable = propertyTableOrNull();
    249 
    250     if (!propertyTable) {
    251         ASSERT(!isPinnedPropertyTable());
    252         return true;
    253     }
    254 
     246template<typename DetailsFunc>
     247ALWAYS_INLINE bool Structure::checkOffsetConsistency(PropertyTable* propertyTable, const DetailsFunc& detailsFunc) const
     248{
    255249    // We cannot reliably assert things about the property table in the concurrent
    256250    // compilation thread. It is possible for the table to be stolen and then have
     
    273267        dataLog("inlineOverflowAccordingToTotalSize = ", inlineOverflowAccordingToTotalSize, "\n");
    274268        dataLog("numberOfOutOfLineSlotsForLastOffset = ", numberOfOutOfLineSlotsForLastOffset(m_offset), "\n");
     269        detailsFunc();
    275270        UNREACHABLE_FOR_PLATFORM();
    276271    };
     
    284279}
    285280
     281ALWAYS_INLINE bool Structure::checkOffsetConsistency() const
     282{
     283    PropertyTable* propertyTable = propertyTableOrNull();
     284
     285    if (!propertyTable) {
     286        ASSERT(!isPinnedPropertyTable());
     287        return true;
     288    }
     289
     290    // We cannot reliably assert things about the property table in the concurrent
     291    // compilation thread. It is possible for the table to be stolen and then have
     292    // things added to it, which leads to the offsets being all messed up. We could
     293    // get around this by grabbing a lock here, but I think that would be overkill.
     294    if (isCompilationThread())
     295        return true;
     296
     297    return checkOffsetConsistency(propertyTable, [] () { });
     298}
     299
    286300inline void Structure::checkConsistency()
    287301{
     
    303317}
    304318
    305 template<typename Func>
     319template<Structure::ShouldPin shouldPin, typename Func>
    306320inline PropertyOffset Structure::add(VM& vm, PropertyName propertyName, unsigned attributes, const Func& func)
    307321{
     
    309323
    310324    GCSafeConcurrentJSLocker locker(m_lock, vm.heap);
    311    
    312     setPropertyTable(vm, table);
     325
     326    switch (shouldPin) {
     327    case ShouldPin::Yes:
     328        pin(locker, vm, table);
     329        break;
     330    case ShouldPin::No:
     331        setPropertyTable(vm, table);
     332        break;
     333    }
    313334   
    314335    ASSERT(!JSC::isValidOffset(get(vm, propertyName)));
     
    367388inline PropertyOffset Structure::addPropertyWithoutTransition(VM& vm, PropertyName propertyName, unsigned attributes, const Func& func)
    368389{
    369     pin(vm, ensurePropertyTable(vm));
    370    
    371     return add(vm, propertyName, attributes, func);
     390    return add<ShouldPin::Yes>(vm, propertyName, attributes, func);
    372391}
    373392
Note: See TracChangeset for help on using the changeset viewer.