Changeset 245514 in webkit


Ignore:
Timestamp:
May 20, 2019 6:48:04 AM (5 years ago)
Author:
Carlos Garcia Campos
Message:

[GLIB] Crash when instantiating a js object registered with jsc_context_register_class on window object cleared
https://bugs.webkit.org/show_bug.cgi?id=198037

Reviewed by Michael Catanzaro.

Source/JavaScriptCore:

This happens because JSCClass is keeping a pointer to the JSCContext used when the class is registered, and the
context can be destroyed before the class. We can't a reference to the context, because we don't really want to
keep it alive. The life of the JSCClass is not attached to the JSCContext, but to its wrapped global context, so
we can keep a pointer to the JSGlobalContextRef instead and create a new JSCContext wrapping it when
needed. This patch is also making the context property of JSCClass non-readable, which was always the intention,
that's why there isn't a public getter in the API.

  • API/glib/JSCCallbackFunction.cpp:

(JSC::JSCCallbackFunction::construct): Pass the context to jscClassGetOrCreateJSWrapper().

  • API/glib/JSCClass.cpp:

(jscClassGetProperty): Remove the getter for context property.
(jscClassSetProperty): Get the JSGlobalContextRef from the given JSCContext.
(jsc_class_class_init): Make context writable only.
(jscClassCreate): Use the passed in context instead of the member.
(jscClassGetOrCreateJSWrapper): It receives now the context as parameter.
(jscClassCreateContextWithJSWrapper): Ditto.
(jscClassCreateConstructor): Get or create a JSCContext for our JSGlobalContextRef.
(jscClassAddMethod): Ditto.
(jsc_class_add_property): Ditto.

  • API/glib/JSCClassPrivate.h:
  • API/glib/JSCContext.cpp:

(jsc_context_evaluate_in_object): Pass the context to jscClassCreateContextWithJSWrapper().

  • API/glib/JSCValue.cpp:

(jsc_value_new_object): Pass the context to jscClassGetOrCreateJSWrapper().

Tools:

Add a test case to check the crash is fixed.

  • TestWebKitAPI/Tests/WebKitGLib/TestWebExtensions.cpp:

(testWebExtensionWindowObjectCleared):

  • TestWebKitAPI/Tests/WebKitGLib/WebExtensionTest.cpp:

(windowObjectCleared):

Location:
trunk
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/API/glib/JSCCallbackFunction.cpp

    r243283 r245514  
    208208    case G_TYPE_OBJECT:
    209209        if (auto* ptr = returnValue.data[0].v_pointer)
    210             return toRef(jscClassGetOrCreateJSWrapper(m_class.get(), ptr));
     210            return toRef(jscClassGetOrCreateJSWrapper(m_class.get(), context.get(), ptr));
    211211        *exception = toRef(JSC::createTypeError(toJS(jsContext), "constructor returned null"_s));
    212212        break;
  • trunk/Source/JavaScriptCore/API/glib/JSCClass.cpp

    r243289 r245514  
    5757
    5858typedef struct _JSCClassPrivate {
    59     JSCContext* context;
     59    JSGlobalContextRef context;
    6060    CString name;
    6161    JSClassRef jsClass;
     
    6464    JSCClass* parentClass;
    6565    JSC::Weak<JSC::JSObject> prototype;
    66     HashMap<CString, JSC::Weak<JSC::JSObject>> constructors;
    6766} JSCClassPrivate;
    6867
     
    284283
    285284    switch (propID) {
    286     case PROP_CONTEXT:
    287         g_value_set_object(value, jscClass->priv->context);
    288         break;
    289285    case PROP_NAME:
    290286        g_value_set_string(value, jscClass->priv->name.data());
     
    304300    switch (propID) {
    305301    case PROP_CONTEXT:
    306         jscClass->priv->context = JSC_CONTEXT(g_value_get_object(value));
     302        jscClass->priv->context = jscContextGetJSContext(JSC_CONTEXT(g_value_get_object(value)));
    307303        break;
    308304    case PROP_NAME:
     
    348344            "JSC Context",
    349345            JSC_TYPE_CONTEXT,
    350             static_cast<GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));
     346            static_cast<GParamFlags>(WEBKIT_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)));
    351347
    352348    /**
     
    493489    prototypeDefinition.className = prototypeName.get();
    494490    JSClassRef prototypeClass = JSClassCreate(&prototypeDefinition);
    495     priv->prototype = jscContextGetOrCreateJSWrapper(priv->context, prototypeClass);
     491    priv->prototype = jscContextGetOrCreateJSWrapper(context, prototypeClass);
    496492    JSClassRelease(prototypeClass);
    497493
    498494    if (priv->parentClass)
    499         JSObjectSetPrototype(jscContextGetJSContext(priv->context), toRef(priv->prototype.get()), toRef(priv->parentClass->priv->prototype.get()));
     495        JSObjectSetPrototype(jscContextGetJSContext(context), toRef(priv->prototype.get()), toRef(priv->parentClass->priv->prototype.get()));
    500496    return jscClass;
    501497}
     
    506502}
    507503
    508 JSC::JSObject* jscClassGetOrCreateJSWrapper(JSCClass* jscClass, gpointer wrappedObject)
     504JSC::JSObject* jscClassGetOrCreateJSWrapper(JSCClass* jscClass, JSCContext* context, gpointer wrappedObject)
    509505{
    510506    JSCClassPrivate* priv = jscClass->priv;
    511     return jscContextGetOrCreateJSWrapper(priv->context, priv->jsClass, toRef(priv->prototype.get()), wrappedObject, priv->destroyFunction);
    512 }
    513 
    514 JSGlobalContextRef jscClassCreateContextWithJSWrapper(JSCClass* jscClass, gpointer wrappedObject)
     507    return jscContextGetOrCreateJSWrapper(context, priv->jsClass, toRef(priv->prototype.get()), wrappedObject, priv->destroyFunction);
     508}
     509
     510JSGlobalContextRef jscClassCreateContextWithJSWrapper(JSCClass* jscClass, JSCContext* context, gpointer wrappedObject)
    515511{
    516512    JSCClassPrivate* priv = jscClass->priv;
    517     return jscContextCreateContextWithJSWrapper(priv->context, priv->jsClass, toRef(priv->prototype.get()), wrappedObject, priv->destroyFunction);
     513    return jscContextCreateContextWithJSWrapper(context, priv->jsClass, toRef(priv->prototype.get()), wrappedObject, priv->destroyFunction);
    518514}
    519515
     
    563559        closure = adoptGRef(g_cclosure_new(callback, userData, reinterpret_cast<GClosureNotify>(reinterpret_cast<GCallback>(destroyNotify))));
    564560    JSCClassPrivate* priv = jscClass->priv;
    565     JSC::ExecState* exec = toJS(jscContextGetJSContext(priv->context));
     561    JSC::ExecState* exec = toJS(priv->context);
    566562    JSC::VM& vm = exec->vm();
    567563    JSC::JSLockHolder locker(vm);
    568564    auto* functionObject = JSC::JSCCallbackFunction::create(vm, exec->lexicalGlobalObject(), String::fromUTF8(name),
    569565        JSC::JSCCallbackFunction::Type::Constructor, jscClass, WTFMove(closure), returnType, WTFMove(parameters));
    570     auto constructor = jscContextGetOrCreateValue(priv->context, toRef(functionObject));
    571     GRefPtr<JSCValue> prototype = jscContextGetOrCreateValue(priv->context, toRef(priv->prototype.get()));
     566    auto context = jscContextGetOrCreate(priv->context);
     567    auto constructor = jscContextGetOrCreateValue(context.get(), toRef(functionObject));
     568    GRefPtr<JSCValue> prototype = jscContextGetOrCreateValue(context.get(), toRef(priv->prototype.get()));
    572569    auto nonEnumerable = static_cast<JSCValuePropertyFlags>(JSC_VALUE_PROPERTY_CONFIGURABLE | JSC_VALUE_PROPERTY_WRITABLE);
    573570    jsc_value_object_define_property_data(constructor.get(), "prototype", nonEnumerable, prototype.get());
    574571    jsc_value_object_define_property_data(prototype.get(), "constructor", nonEnumerable, constructor.get());
    575     priv->constructors.set(name, functionObject);
    576572    return constructor;
    577573}
     
    712708    JSCClassPrivate* priv = jscClass->priv;
    713709    GRefPtr<GClosure> closure = adoptGRef(g_cclosure_new(callback, userData, reinterpret_cast<GClosureNotify>(reinterpret_cast<GCallback>(destroyNotify))));
    714     JSC::ExecState* exec = toJS(jscContextGetJSContext(priv->context));
     710    JSC::ExecState* exec = toJS(priv->context);
    715711    JSC::VM& vm = exec->vm();
    716712    JSC::JSLockHolder locker(vm);
    717713    auto* functionObject = toRef(JSC::JSCCallbackFunction::create(vm, exec->lexicalGlobalObject(), String::fromUTF8(name),
    718714        JSC::JSCCallbackFunction::Type::Method, jscClass, WTFMove(closure), returnType, WTFMove(parameters)));
    719     auto method = jscContextGetOrCreateValue(priv->context, functionObject);
    720     GRefPtr<JSCValue> prototype = jscContextGetOrCreateValue(priv->context, toRef(priv->prototype.get()));
     715    auto context = jscContextGetOrCreate(priv->context);
     716    auto method = jscContextGetOrCreateValue(context.get(), functionObject);
     717    GRefPtr<JSCValue> prototype = jscContextGetOrCreateValue(context.get(), toRef(priv->prototype.get()));
    721718    auto nonEnumerable = static_cast<JSCValuePropertyFlags>(JSC_VALUE_PROPERTY_CONFIGURABLE | JSC_VALUE_PROPERTY_WRITABLE);
    722719    jsc_value_object_define_property_data(prototype.get(), name, nonEnumerable, method.get());
     
    863860    g_return_if_fail(priv->context);
    864861
    865     GRefPtr<JSCValue> prototype = jscContextGetOrCreateValue(priv->context, toRef(priv->prototype.get()));
     862    auto context = jscContextGetOrCreate(priv->context);
     863    GRefPtr<JSCValue> prototype = jscContextGetOrCreateValue(context.get(), toRef(priv->prototype.get()));
    866864    jsc_value_object_define_property_accessor(prototype.get(), name, JSC_VALUE_PROPERTY_CONFIGURABLE, propertyType, getter, setter, userData, destroyNotify);
    867865}
  • trunk/Source/JavaScriptCore/API/glib/JSCClassPrivate.h

    r234025 r245514  
    2828GRefPtr<JSCClass> jscClassCreate(JSCContext*, const char*, JSCClass*, JSCClassVTable*, GDestroyNotify);
    2929JSClassRef jscClassGetJSClass(JSCClass*);
    30 JSC::JSObject* jscClassGetOrCreateJSWrapper(JSCClass*, gpointer);
    31 JSGlobalContextRef jscClassCreateContextWithJSWrapper(JSCClass*, gpointer);
     30JSC::JSObject* jscClassGetOrCreateJSWrapper(JSCClass*, JSCContext*, gpointer);
     31JSGlobalContextRef jscClassCreateContextWithJSWrapper(JSCClass*, JSCContext*, gpointer);
    3232void jscClassInvalidate(JSCClass*);
  • trunk/Source/JavaScriptCore/API/glib/JSCContext.cpp

    r239569 r245514  
    879879
    880880    JSRetainPtr<JSGlobalContextRef> objectContext(Adopt,
    881         instance ? jscClassCreateContextWithJSWrapper(objectClass, instance) : JSGlobalContextCreateInGroup(jscVirtualMachineGetContextGroup(context->priv->vm.get()), nullptr));
     881        instance ? jscClassCreateContextWithJSWrapper(objectClass, context, instance) : JSGlobalContextCreateInGroup(jscVirtualMachineGetContextGroup(context->priv->vm.get()), nullptr));
    882882    JSC::ExecState* exec = toJS(objectContext.get());
    883883    JSC::VM& vm = exec->vm();
  • trunk/Source/JavaScriptCore/API/glib/JSCValue.cpp

    r243289 r245514  
    603603    g_return_val_if_fail(!instance || JSC_IS_CLASS(jscClass), nullptr);
    604604
    605     return jscContextGetOrCreateValue(context, instance ? toRef(jscClassGetOrCreateJSWrapper(jscClass, instance)) : JSObjectMake(jscContextGetJSContext(context), nullptr, nullptr)).leakRef();
     605    return jscContextGetOrCreateValue(context, instance ? toRef(jscClassGetOrCreateJSWrapper(jscClass, context, instance)) : JSObjectMake(jscContextGetJSContext(context), nullptr, nullptr)).leakRef();
    606606}
    607607
  • trunk/Source/JavaScriptCore/ChangeLog

    r245511 r245514  
     12019-05-20  Carlos Garcia Campos  <cgarcia@igalia.com>
     2
     3        [GLIB] Crash when instantiating a js object registered with jsc_context_register_class on window object cleared
     4        https://bugs.webkit.org/show_bug.cgi?id=198037
     5
     6        Reviewed by Michael Catanzaro.
     7
     8        This happens because JSCClass is keeping a pointer to the JSCContext used when the class is registered, and the
     9        context can be destroyed before the class. We can't a reference to the context, because we don't really want to
     10        keep it alive. The life of the JSCClass is not attached to the JSCContext, but to its wrapped global context, so
     11        we can keep a pointer to the JSGlobalContextRef instead and create a new JSCContext wrapping it when
     12        needed. This patch is also making the context property of JSCClass non-readable, which was always the intention,
     13        that's why there isn't a public getter in the API.
     14
     15        * API/glib/JSCCallbackFunction.cpp:
     16        (JSC::JSCCallbackFunction::construct): Pass the context to jscClassGetOrCreateJSWrapper().
     17        * API/glib/JSCClass.cpp:
     18        (jscClassGetProperty): Remove the getter for context property.
     19        (jscClassSetProperty): Get the JSGlobalContextRef from the given JSCContext.
     20        (jsc_class_class_init): Make context writable only.
     21        (jscClassCreate): Use the passed in context instead of the member.
     22        (jscClassGetOrCreateJSWrapper): It receives now the context as parameter.
     23        (jscClassCreateContextWithJSWrapper): Ditto.
     24        (jscClassCreateConstructor): Get or create a JSCContext for our JSGlobalContextRef.
     25        (jscClassAddMethod): Ditto.
     26        (jsc_class_add_property): Ditto.
     27        * API/glib/JSCClassPrivate.h:
     28        * API/glib/JSCContext.cpp:
     29        (jsc_context_evaluate_in_object): Pass the context to jscClassCreateContextWithJSWrapper().
     30        * API/glib/JSCValue.cpp:
     31        (jsc_value_new_object): Pass the context to jscClassGetOrCreateJSWrapper().
     32
    1332019-05-19  Tadeu Zagallo  <tzagallo@apple.com>
    234
  • trunk/Tools/ChangeLog

    r245512 r245514  
     12019-05-20  Carlos Garcia Campos  <cgarcia@igalia.com>
     2
     3        [GLIB] Crash when instantiating a js object registered with jsc_context_register_class on window object cleared
     4        https://bugs.webkit.org/show_bug.cgi?id=198037
     5
     6        Reviewed by Michael Catanzaro.
     7
     8        Add a test case to check the crash is fixed.
     9
     10        * TestWebKitAPI/Tests/WebKitGLib/TestWebExtensions.cpp:
     11        (testWebExtensionWindowObjectCleared):
     12        * TestWebKitAPI/Tests/WebKitGLib/WebExtensionTest.cpp:
     13        (windowObjectCleared):
     14
    1152019-05-20  Carlos Garcia Campos  <cgarcia@igalia.com>
    216
  • trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebExtensions.cpp

    r239772 r245514  
    180180    GUniquePtr<char> valueString(WebViewTest::javascriptResultToCString(javascriptResult));
    181181    g_assert_cmpstr(valueString.get(), ==, "Foo");
     182
     183    javascriptResult = test->runJavaScriptAndWaitUntilFinished("var f = new GFile('.'); f.path();", &error.outPtr());
     184    g_assert_nonnull(javascriptResult);
     185    g_assert_no_error(error.get());
     186    valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult));
     187    GUniquePtr<char> currentDirectory(g_get_current_dir());
     188    g_assert_cmpstr(valueString.get(), ==, currentDirectory.get());
    182189}
    183190
  • trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/WebExtensionTest.cpp

    r245176 r245514  
    428428    GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_function(jsContext.get(), "echo", G_CALLBACK(echoCallback), NULL, NULL, G_TYPE_STRING, 1, G_TYPE_STRING));
    429429    jsc_context_set_value(jsContext.get(), "echo", function.get());
     430
     431    auto* fileClass = jsc_context_register_class(jsContext.get(), "GFile", nullptr, nullptr, static_cast<GDestroyNotify>(g_object_unref));
     432    GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(fileClass, "GFile", G_CALLBACK(g_file_new_for_path), nullptr, nullptr, G_TYPE_OBJECT, 1, G_TYPE_STRING));
     433    jsc_class_add_method(fileClass, "path", G_CALLBACK(g_file_get_path), nullptr, nullptr, G_TYPE_STRING, 0, G_TYPE_NONE);
     434    jsc_context_set_value(jsContext.get(), "GFile", constructor.get());
    430435}
    431436
Note: See TracChangeset for help on using the changeset viewer.