Skip to content

Commit

Permalink
8242361: JavaFX Web View crashes with Segmentation Fault, when HTML c…
Browse files Browse the repository at this point in the history
…ontains Data-URIs

Reviewed-by: kcr, ajoseph
  • Loading branch information
matthiasblaesing authored and kevinrushforth committed Dec 17, 2020
1 parent fb8e0cd commit e61b923
Show file tree
Hide file tree
Showing 6 changed files with 353 additions and 20 deletions.
4 changes: 3 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3600,6 +3600,7 @@ project(":systemTests") {
testapp4
testapp5
testapp6
testapp7
testscriptapp1
testscriptapp2
}
Expand All @@ -3615,6 +3616,7 @@ project(":systemTests") {
sourceSets.testapp4,
sourceSets.testapp5,
sourceSets.testapp6,
sourceSets.testapp7,
sourceSets.testscriptapp1,
sourceSets.testscriptapp2
]
Expand Down Expand Up @@ -3716,7 +3718,7 @@ project(":systemTests") {
}
test.dependsOn(createTestApps);

def modtestapps = [ "testapp2", "testapp3", "testapp4", "testapp5", "testapp6", "testscriptapp1", "testscriptapp2" ]
def modtestapps = [ "testapp2", "testapp3", "testapp4", "testapp5", "testapp6", "testapp7", "testscriptapp1", "testscriptapp2" ]
modtestapps.each { testapp ->
def testappCapital = testapp.capitalize()
def copyTestAppTask = task("copy${testappCapital}", type: Copy) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,59 @@
#include <wtf/RunLoop.h>

namespace WTF {
static JGClass jMainThreadCls;
static jmethodID fwkIsMainThread;
static jmethodID fwkScheduleDispatchFunctions;

void scheduleDispatchFunctionsOnMainThread()
{
AttachThreadAsNonDaemonToJavaEnv autoAttach;
JNIEnv* env = autoAttach.env();
static JGClass jMainThreadCls(env->FindClass("com/sun/webkit/MainThread"));
env->CallStaticVoidMethod(jMainThreadCls, fwkScheduleDispatchFunctions);
WTF::CheckAndClearException(env);
}

void initializeMainThreadPlatform()
{
// Initialize the class reference and methodids for the MainThread. The
// initialization has to be done from a context where the class
// com.sun.webkit.MainThread is accessible. When
// scheduleDispatchFunctionsOnMainThread is invoked, the system class loader
// would be used to locate the class, which fails if the JavaFX modules are
// not loaded from the boot module layer.
//
// initializeMainThreadPlatform is called through the chain:
// - com.sun.webkit.WebPage.WebPage
// - com.sun.webkit.WebPage.twkCreatePage
// - WTF::initializeMainThread
// - WTF::initializeMainThreadPlatform
//
// As we are invoked through JNI from java, the class loader, that loaded
// WebPage will be used by FindClass.
//
// WTF::initializeMainThread has a guard, so that initialization is only run
// once

AttachThreadAsNonDaemonToJavaEnv autoAttach;
JNIEnv* env = autoAttach.env();

static JGClass jMainThreadRef(env->FindClass("com/sun/webkit/MainThread"));
jMainThreadCls = jMainThreadRef;

fwkIsMainThread = env->GetStaticMethodID(
jMainThreadCls,
"fwkIsMainThread",
"()Z");

ASSERT(fwkIsMainThread);

static jmethodID mid = env->GetStaticMethodID(
fwkScheduleDispatchFunctions = env->GetStaticMethodID(
jMainThreadCls,
"fwkScheduleDispatchFunctions",
"()V");

ASSERT(mid);
ASSERT(fwkScheduleDispatchFunctions);

env->CallStaticVoidMethod(jMainThreadCls, mid);
WTF::CheckAndClearException(env);
}

void initializeMainThreadPlatform()
{
#if OS(WINDOWS)
RunLoop::registerRunLoopMessageWindowClass();
#endif
Expand All @@ -64,16 +98,7 @@ bool isMainThread()
{
AttachThreadAsNonDaemonToJavaEnv autoAttach;
JNIEnv* env = autoAttach.env();
static JGClass jMainThreadCls(env->FindClass("com/sun/webkit/MainThread"));

static jmethodID mid = env->GetStaticMethodID(
jMainThreadCls,
"fwkIsMainThread",
"()Z");

ASSERT(mid);

jboolean isMainThread = env->CallStaticBooleanMethod(jMainThreadCls, mid);
jboolean isMainThread = env->CallStaticBooleanMethod(jMainThreadCls, fwkIsMainThread);
WTF::CheckAndClearException(env);
return isMainThread == JNI_TRUE;
}
Expand Down
75 changes: 75 additions & 0 deletions tests/system/src/test/java/test/com/sun/webkit/MainThreadTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package test.com.sun.webkit;

import java.io.File;
import static java.util.Arrays.asList;
import java.util.List;
import static org.junit.Assert.assertEquals;
import org.junit.Test;

/**
* @test
* @bug 8242361
* @summary Check if webkit main thread <-> java integration works correctly
*/
public class MainThreadTest {
@Test (timeout = 15000)
public void testMainThreadDoesNotSegfault() throws Exception {
// This is an indirect test of the webkit main thread <-> java
// integration. It was observed, that using a data-url caused the
// JVM to segfault. That case is executed by this test:
//
// A new JVM is started with a custom launcher (classpath based). This
// launcher sets up the module layer required for OpenJFX and starts
// the test application. That way the OpenJFX classes are not loaded by
// the system class loader, but by the classloader that is associated
// with the new module layer.
//

final String appModulePath = System.getProperty("launchertest.testapp7.module.path");
final String workerModulePath = System.getProperty("worker.module.path");
final String javaLibraryPath = System.getProperty("java.library.path");
final String workerJavaCmd = System.getProperty("worker.java.cmd");

final List<String> cmd = asList(
workerJavaCmd,
"-cp", appModulePath + "/mymod",
"-Djava.library.path=" + javaLibraryPath,
"-Dmodule.path=" + appModulePath + "/mymod" + File.pathSeparator + workerModulePath,
"myapp7.DataUrlWithModuleLayerLauncher"
);

final ProcessBuilder builder = new ProcessBuilder(cmd);

builder.redirectError(ProcessBuilder.Redirect.INHERIT);
builder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
Process process = builder.start();
int retVal = process.waitFor();

assertEquals("Process did not exit cleanly", 0, retVal);
}
}
33 changes: 33 additions & 0 deletions tests/system/src/testapp7/java/mymod/module-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

module mymod {
requires javafx.controls;
requires javafx.media;
requires javafx.web;
requires javafx.graphics;
requires javafx.base;
exports myapp7;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package myapp7;

import java.lang.module.ModuleDescriptor;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker.State;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

public class DataUrlWithModuleLayer extends Application {
public static final int ERROR_OK = 0;
public static final int ERROR_ASSUMPTION_VIOLATED = 2;
public static final int ERROR_TIMEOUT = 3;
public static final int ERROR_TITLE_NOT_UPDATED = 4;
public static final int ERROR_UNEXPECTED_EXIT = 5;

@Override
public void start(Stage primaryStage) throws Exception {
Module module = Application.class.getModule();

if (module == null) {
System.err.println("Failure: Module for Application not found");
System.exit(ERROR_ASSUMPTION_VIOLATED);
}

if (! module.isNamed()) {
System.err.println("Failure: Expected named module");
System.exit(ERROR_ASSUMPTION_VIOLATED);
}

ModuleDescriptor moduleDesc = module.getDescriptor();

if (moduleDesc.isAutomatic()) {
System.err.println("Failure: Automatic module found");
System.exit(ERROR_ASSUMPTION_VIOLATED);
}

if (moduleDesc.isOpen()) {
System.err.println("Failure: Open module found");
System.exit(ERROR_ASSUMPTION_VIOLATED);
}

BorderPane root = new BorderPane();
Scene scene = new Scene(root);

WebView webview = new WebView();
root.setCenter(webview);

// The title tag value is made available via the getTitle nethod. The
// JS code changes the value, so that the enclosing application can
// detect, that the JS code was run.
String checkJS = "document.getElementsByTagName(\"title\")[0].textContent='Executed'";
String checkJSEncoded = Base64.getEncoder().encodeToString(checkJS.getBytes(StandardCharsets.UTF_8));

String script = "<html>"
+ "<head>"
+ "<title>Armed</title>"
+ "</head>"
+ "<body>"
+ "<h1>Test for loading a data URL</h1>"
+ "<p>The test is successful, if the JVM does not crash with a SEGFAULT.</p>"
+ "<script src=\"data:application/javascript;base64," + checkJSEncoded + "\"></script>"
+ "</body>"
+ "</html>";
webview.getEngine().getLoadWorker().stateProperty().addListener(
new ChangeListener<State>() {
public void changed(ObservableValue ov, State oldState, State newState) {
if (newState == State.SUCCEEDED) {
String title = webview.getEngine().getTitle();
if ("Executed".equals(title)) {
System.exit(ERROR_OK);
} else {
System.exit(ERROR_TITLE_NOT_UPDATED);
}
}
}
});
webview.getEngine().loadContent(script);

primaryStage.setScene(scene);
primaryStage.setWidth(1024);
primaryStage.setHeight(768);
primaryStage.show();
}
}
Loading

0 comments on commit e61b923

Please sign in to comment.