Changeset 275969 in webkit


Ignore:
Timestamp:
Apr 14, 2021, 2:50:30 PM (4 years ago)
Author:
mark.lam@apple.com
Message:

Defer TerminationExceptions when evaluating ASSERT in HashMapIml::addNormalized().
https://bugs.webkit.org/show_bug.cgi?id=224565
rdar://76645980

Reviewed by Yusuke Suzuki.

JSTests:

  • stress/suppress-TerrminationException-in-ASSERT-in-HashMapImpl-addNormalized.js: Added.

Source/JavaScriptCore:

HashMapImpl::addNormalized() has an ASSERT that calls jsMapHash(), which can
potentially throw exceptions. As a result, it has a RETURN_IF_EXCEPTION which
provides an opportunity to handle traps and throw a TerminationException. This
in turn causes the ASSERT to fail.

To fix this, we do:

  1. Introduce VMTraps::DeferAction, which gives us DeferForAWhile and DeferUntilEndOfScope.
  1. Templatize the DeferTermination RAII object on VMTraps::DeferAction. Introduce DeferTerrminationForAWhile, which is DeferTermination<VMTraps::DeferAction::DeferForAWhile>. DeferForAWhile means that the deferScope will not throw the TerminationException on exit. Instead, it will re-set the NeedTermination bit in the traps, and let the next trap check handle it.
  1. Introduce DEFER_TERMINATION_AND_ASSERT_WITH_MESSAGE (and friends) which creates a DeferTerrminationForAWhile scope before doing an ASSERT_WITH_MESSAGE.
  1. Use DEFER_TERMINATION_AND_ASSERT_WITH_MESSAGE instead in HashMapImpl::addNormalized().
  • runtime/DeferTermination.h:

(JSC::DeferTermination::DeferTermination):
(JSC::DeferTermination::~DeferTermination):

  • runtime/ExceptionHelpers.h:
  • runtime/HashMapImpl.h:

(JSC::HashMapImpl::addNormalized):

  • runtime/VMTraps.cpp:

(JSC::VMTraps::deferTermination):
(JSC::VMTraps::undoDeferTermination):

  • runtime/VMTraps.h:
Location:
trunk
Files:
1 added
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r275965 r275969  
     12021-04-14  Mark Lam  <mark.lam@apple.com>
     2
     3        Defer TerminationExceptions when evaluating ASSERT in HashMapIml::addNormalized().
     4        https://bugs.webkit.org/show_bug.cgi?id=224565
     5        rdar://76645980
     6
     7        Reviewed by Yusuke Suzuki.
     8
     9        * stress/suppress-TerrminationException-in-ASSERT-in-HashMapImpl-addNormalized.js: Added.
     10
    1112021-04-14  Guillaume Emont  <guijemont@igalia.com>
    212
  • trunk/Source/JavaScriptCore/ChangeLog

    r275924 r275969  
     12021-04-14  Mark Lam  <mark.lam@apple.com>
     2
     3        Defer TerminationExceptions when evaluating ASSERT in HashMapIml::addNormalized().
     4        https://bugs.webkit.org/show_bug.cgi?id=224565
     5        rdar://76645980
     6
     7        Reviewed by Yusuke Suzuki.
     8
     9        HashMapImpl::addNormalized() has an ASSERT that calls jsMapHash(), which can
     10        potentially throw exceptions.  As a result, it has a RETURN_IF_EXCEPTION which
     11        provides an opportunity to handle traps and throw a TerminationException.  This
     12        in turn causes the ASSERT to fail.
     13
     14        To fix this, we do:
     15
     16        1. Introduce VMTraps::DeferAction, which gives us DeferForAWhile and DeferUntilEndOfScope.
     17
     18        2. Templatize the DeferTermination RAII object on VMTraps::DeferAction.
     19           Introduce DeferTerrminationForAWhile, which is DeferTermination<VMTraps::DeferAction::DeferForAWhile>.
     20           DeferForAWhile means that the deferScope will not throw the TerminationException
     21           on exit.  Instead, it will re-set the NeedTermination bit in the traps, and let
     22           the next trap check handle it.
     23
     24        3. Introduce DEFER_TERMINATION_AND_ASSERT_WITH_MESSAGE (and friends) which creates
     25           a DeferTerrminationForAWhile scope before doing an ASSERT_WITH_MESSAGE.
     26
     27        4. Use DEFER_TERMINATION_AND_ASSERT_WITH_MESSAGE instead in HashMapImpl::addNormalized().
     28
     29        * runtime/DeferTermination.h:
     30        (JSC::DeferTermination::DeferTermination):
     31        (JSC::DeferTermination::~DeferTermination):
     32        * runtime/ExceptionHelpers.h:
     33        * runtime/HashMapImpl.h:
     34        (JSC::HashMapImpl::addNormalized):
     35        * runtime/VMTraps.cpp:
     36        (JSC::VMTraps::deferTermination):
     37        (JSC::VMTraps::undoDeferTermination):
     38        * runtime/VMTraps.h:
     39
    1402021-04-13  Mark Lam  <mark.lam@apple.com>
    241
  • trunk/Source/JavaScriptCore/runtime/DeferTermination.h

    r275797 r275969  
    3232namespace JSC {
    3333
     34template<VMTraps::DeferAction deferAction = VMTraps::DeferAction::DeferUntilEndOfScope>
    3435class DeferTermination {
    3536    WTF_MAKE_NONCOPYABLE(DeferTermination);
     
    3940        : m_vm(vm)
    4041    {
    41         m_vm.traps().deferTermination();
     42        m_vm.traps().deferTermination(deferAction);
    4243    }
    4344   
    4445    ~DeferTermination()
    4546    {
    46         m_vm.traps().undoDeferTermination();
     47        m_vm.traps().undoDeferTermination(deferAction);
    4748    }
    4849
     
    5152};
    5253
     54using DeferTerminationForAWhile = DeferTermination<VMTraps::DeferAction::DeferForAWhile>;
     55
    5356} // namespace JSC
  • trunk/Source/JavaScriptCore/runtime/ExceptionHelpers.h

    r275648 r275969  
    2929#pragma once
    3030
     31#include "DeferTermination.h"
    3132#include "ErrorInstance.h"
    3233#include "Exception.h"
     
    6162JS_EXPORT_PRIVATE Exception* throwStackOverflowError(JSGlobalObject*, ThrowScope&);
    6263
     64#if ASSERT_ENABLED
     65
     66#define DEFER_TERMINATION_AND_ASSERT(vm, assertion, ...) do { \
     67        JSC::DeferTerminationForAWhile deferScope(vm); \
     68        ASSERT(assertion, __VA_ARGS__); \
     69    } while (false)
     70
     71#define DEFER_TERMINATION_AND_ASSERT_WITH_MESSAGE(vm, assertion, ...) do { \
     72        JSC::DeferTerminationForAWhile deferScope(vm); \
     73        ASSERT_WITH_MESSAGE(assertion, __VA_ARGS__); \
     74    } while (false)
     75
     76#else
     77
     78#define DEFER_TERMINATION_AND_ASSERT(vm, assertion, ...) UNUSED_PARAM(vm)
     79#define DEFER_TERMINATION_AND_ASSERT_WITH_MESSAGE(vm, assertion, ...) UNUSED_PARAM(vm)
     80
     81#endif // ASSERT_ENABLED
     82
     83#define DEFER_TERMINATION_AND_RELEASE_ASSERT(vm, assertion, ...) do { \
     84        JSC::DeferTerminationForAWhile deferScope(vm); \
     85        RELEASE_ASSERT(assertion, __VA_ARGS__); \
     86    } while (false)
     87
     88#define DEFER_TERMINATION_AND_RELEASE_ASSERT_WITH_MESSAGE(vm, assertion, ...) do { \
     89        JSC::DeferTerminationForAWhile deferScope(vm); \
     90        RELEASE_ASSERT_WITH_MESSAGE(assertion, __VA_ARGS__); \
     91    } while (false)
     92
    6393} // namespace JSC
  • trunk/Source/JavaScriptCore/runtime/HashMapImpl.h

    r275299 r275969  
    497497    ALWAYS_INLINE HashMapBucketType* addNormalized(JSGlobalObject* globalObject, JSValue key, JSValue value, uint32_t hash)
    498498    {
     499        VM& vm = getVM(globalObject);
    499500        ASSERT_WITH_MESSAGE(normalizeMapKey(key) == key, "We expect normalized values flowing into this function.");
    500         ASSERT_WITH_MESSAGE(jsMapHash(globalObject, getVM(globalObject), key) == hash, "We expect hash value is what we expect.");
    501 
    502         auto* bucket = addNormalizedInternal(getVM(globalObject), key, value, hash, [&] (HashMapBucketType* bucket) {
     501        DEFER_TERMINATION_AND_ASSERT_WITH_MESSAGE(vm, jsMapHash(globalObject, getVM(globalObject), key) == hash, "We expect hash value is what we expect.");
     502
     503        auto* bucket = addNormalizedInternal(vm, key, value, hash, [&] (HashMapBucketType* bucket) {
    503504            return !isDeleted(bucket) && areKeysEqual(globalObject, key, bucket->key());
    504505        });
  • trunk/Source/JavaScriptCore/runtime/VMTraps.cpp

    r275924 r275969  
    416416}
    417417
    418 void VMTraps::deferTermination()
     418void VMTraps::deferTermination(DeferAction)
    419419{
    420420    auto locker = holdLock(*m_lock);
     
    430430}
    431431
    432 void VMTraps::undoDeferTermination()
     432void VMTraps::undoDeferTermination(DeferAction action)
    433433{
    434434    auto locker = holdLock(*m_lock);
     
    436436    if (--m_deferTerminationCount == 0) {
    437437        VM& vm = this->vm();
    438         if (m_suspendedTerminationException || vm.terminationInProgress())
    439             vm.setException(vm.terminationException());
     438        if (m_suspendedTerminationException
     439            || ((action == DeferAction::DeferUntilEndOfScope) && vm.terminationInProgress()))
     440            vm.throwTerminationException();
     441        else if ((action == DeferAction::DeferForAWhile) && vm.terminationInProgress())
     442            setTrapBit(NeedTermination); // Let the next trap check handle it.
    440443    }
    441444}
  • trunk/Source/JavaScriptCore/runtime/VMTraps.h

    r275797 r275969  
    192192    void* trapBitsAddress() { return &m_trapBits; }
    193193
     194    enum class DeferAction {
     195        DeferForAWhile,
     196        DeferUntilEndOfScope
     197    };
     198
    194199    bool isDeferringTermination() const { return m_deferTerminationCount; }
    195     JS_EXPORT_PRIVATE void deferTermination();
    196     JS_EXPORT_PRIVATE void undoDeferTermination();
     200    JS_EXPORT_PRIVATE void deferTermination(DeferAction);
     201    JS_EXPORT_PRIVATE void undoDeferTermination(DeferAction);
    197202
    198203    void notifyGrabAllLocks()
Note: See TracChangeset for help on using the changeset viewer.