JDK-8223377 : JavaFX can crash due to loading the wrong native libraries if system libraries are installed
  • Type: Bug
  • Component: javafx
  • Sub-Component: other
  • Affected Version: openjfx11
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: linux_ubuntu
  • CPU: x86_64
  • Submitted: 2019-05-05
  • Updated: 2019-05-17
  • Resolved: 2019-05-17
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
openjfx13Fixed
Related Reports
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
Ubuntu 19.04 (Disco Dingo)

openjdk version "11.0.3" 2019-04-16
OpenJDK Runtime Environment (build 11.0.3+7-Ubuntu-1ubuntu1)
OpenJDK 64-Bit Server VM (build 11.0.3+7-Ubuntu-1ubuntu1, mixed mode, sharing)

A DESCRIPTION OF THE PROBLEM :
When the HTML code, that the webengine is tasked to render contains japanese/sundanese punctuation code points, the code is not correctly rendered,  but crashes the WebEngine.

Sample characters: \uA9C1\u1CC0\uA9C2


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
I used netbeans to execute the souce code attached. 

1. The project is created by placing the pom.xml into a new subdirectory. 
2. Under that directory the secondfile needs to be created as: src/main/java/eu/doppel_helix/dev/jdk/reproducecrash/TestBrowser.java.
3. Open the project with netbeans
4. Run clean and build on it (to initialize dependencies from maven central)
5. Open TestBrowser and run it by invoking "Run file" from the context menu

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Page showing the rendered unicode symbols or "missing character" symbols.
ACTUAL -
Segfault from the JVM. In the source code are two variants. I ran both variants, the hs_err log can be found here (as I could not upload it here):
https://www.doppel-helix.eu/hs_err_unicode_characters.log
https://www.doppel-helix.eu/hs_err_unicode_entity.log

---------- BEGIN SOURCE ----------
============ pom.xml ===========
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>eu.doppel_helix.dev.jdk</groupId>
    <artifactId>reproducecrash</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>13-ea+6</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-web</artifactId>
            <version>13-ea+6</version>
        </dependency>
    </dependencies>
</project>

============ TestBrowser ===========
public class TestBrowser implements Runnable {

    public static void main(String[] args) {
        Platform.startup(new TestBrowser());
    }

    public void run() {
        Stage primaryStage = new Stage();

	primaryStage.setTitle("Hello World!");
	WebView view = new WebView();
	BorderPane bp = new BorderPane();
	bp.setCenter(view);
	primaryStage.setScene(new Scene(bp, 800, 600));
	primaryStage.show();

        StringBuilder sb = new StringBuilder();
        sb.append("<!DOCTYPE html>\n");
        sb.append("<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"de\">\n");
        sb.append("<head>\n");
        sb.append("<title>Demo Site</title>\n");
        sb.append("</head>\n");
        sb.append("<body>\n");
        sb.append("<h1>Test</h1>");
// Variant 1: Unicode characters
        sb.append("\uA9C1\u1CC0\uA9C2");
// Variant 2: Unicode entities
//        sb.append("&#xA9C1;&#x1CC0;&#xA9C2;");
        sb.append("</body>");
        sb.append("</html>");

        view.getEngine().loadContent(sb.toString());
    }
}
---------- END SOURCE ----------

FREQUENCY : always



Comments
Changeset: 74bdac7b31e3 Author: kcr Date: 2019-05-17 06:11 -0700 URL: http://hg.openjdk.java.net/openjfx/jfx-dev/rt/rev/74bdac7b31e3 8223377: JavaFX can crash due to loading the wrong native libraries if system libraries are installed Reviewed-by: kcr, jvos Contributed-by: mblaesing@doppel-helix.eu
17-05-2019

Pull request sent by submitter of bug: https://github.com/javafxports/openjdk-jfx/pull/468
13-05-2019

Workaround: Set the java.library.path system property to $HOME/.openjfx/cache/13-ea $ java -Djava.library.path=$HOME/.openjfx/cache/13-ea ... (this may not be effective, since it presupposes that you have populated the cache)
11-05-2019

I filed JDK-8223746 to track the enhancement to check the version of the native library at load time.
11-05-2019

The above three comments are from this email thread on openjfx-dev: https://mail.openjdk.java.net/pipermail/openjfx-dev/2019-May/023295.html Based on the additional information, I raised the priority to P3 and targeted it for openjfx13.
11-05-2019

From: Matthias Bl��sing I experimented with OpenJFX once again and noticed, that even simple programms crashed for me. I saw the crashes being introduced in maven release: <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-controls</artifactId> <version>12-ea+7</version> </dependency> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-web</artifactId> <version>12-ea+7</version> </dependency> Until that version this stack trace is generated: java.lang.ArrayIndexOutOfBoundsException: Index -17 out of bounds for length 32 at com.sun.prism.impl.GlyphCache.getCachedGlyph(GlyphCache.java:332) at com.sun.prism.impl.GlyphCache.render(GlyphCache.java:148) at com.sun.prism.impl.ps.BaseShaderGraphics.drawString(BaseShaderGraphics.java:2101) at com.sun.javafx.webkit.prism.WCGraphicsPrismContext$10.doPaint(WCGraphicsPrismContext.java:939) at com.sun.javafx.webkit.prism.WCGraphicsPrismContext$Composite.paint(WCGraphicsPrismContext.java:1524) at com.sun.javafx.webkit.prism.WCGraphicsPrismContext$Composite.paint(WCGraphicsPrismContext.java:1509) at com.sun.javafx.webkit.prism.WCGraphicsPrismContext.drawString(WCGraphicsPrismContext.java:951) at com.sun.webkit.graphics.GraphicsDecoder.decode(GraphicsDecoder.java:301) at com.sun.webkit.graphics.WCRenderQueue.decode(WCRenderQueue.java:92) at com.sun.webkit.WebPage.paint2GC(WebPage.java:736) at com.sun.webkit.WebPage.paint(WebPage.java:703) at com.sun.javafx.sg.prism.web.NGWebView.renderContent(NGWebView.java:95) at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2072) at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1964) at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:270) at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:578) at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2072) at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1964) at com.sun.javafx.tk.quantum.ViewPainter.doPaint(ViewPainter.java:479) at com.sun.javafx.tk.quantum.ViewPainter.paintImpl(ViewPainter.java:328) at com.sun.javafx.tk.quantum.PresentingPainter.run(PresentingPainter.java:91) at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305) at com.sun.javafx.tk.RenderJob.run(RenderJob.java:58) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:125) at java.base/java.lang.Thread.run(Thread.java:834) I tried to corner the problem and noticed, that the crash is _not_ reproducible with the java binary from jdk.java.net. The crash is also not reproducible with a Ubuntu Live CD with only the default-jre installed. Then I tried to align the live environment (does not crash) with my desktop system (OpenJFX crashes). And finally I found the problem. 1. Get the Xubuntu 19.04 live CD: http://torrent.ubuntu.com/xubuntu/releases/disco/release/desktop/xubuntu-19.04-desktop-amd64.iso.torrent 2. Start the Image (Try Xubuntu) in VirtualBox 3. Install the default JDK (that will be 11.0.3) and maven: sudo apt install default-jdk maven git 4. Clone the reproducer repository: git clone https://github.com/matthiasblaesing/reproduce-openjfx-crash.git 5. Build it: cd reproduce-openjfx-crash mvn package 6. Run with: java -jar target/reproduce-openjfx-crash.jar => Window with the title "Hello World!", the text "Test" and japanese/sudanese punctuation symbols are shown => In the console, you see, that the native libraries are loaded from resource 7. Close windows 8. Install openjfx JNI libraries: apt install libopenjfx-jni 9. Run again with: java -jar target/reproduce-openjfx-crash.jar => Window is briefly displayed => On the console a SEGFAULS is logged (and hs_err_pid... is written) => You can read, that the native libraries were loaded via System#loadLibrary -------------------------------------------------------------------- This result also explains why the problem is not visible with the binaries from jdk.java.net: The java executables use different java.library.paths: Ubuntu: java.library.path = /usr/java/packages/lib /usr/lib/x86_64-linux-gnu/jni /lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu /usr/lib/jni /lib /usr/lib OpenJDK: java.library.path = /usr/java/packages/lib /usr/lib64 /lib64 /lib /usr/lib As contents of the libopenjfx-jni package is installed to /usr/lib/x86_64-linux-gnu/jni/, only the Ubuntu java launcher finds the binaries. -------------------------------------------------------------------- It is not too surprising, that the native libraries and the java implementations are tightly coupled. For the JNA project we are faced with the same situation. However, there are differences: - the JNA project checks, that the native libraries are of a compatible version - there is a system property, that lets the user choose whether system libraries should be used or the bundled native libraries be extracted - the system property was changed to default to use the bundled native libraries I had a quick look at the NativeLibraryLoader and don't see a similar mechanism. The only work around I found was overriding the java.library.path, but that requires changes to the launch sequence of the VM. For a managed language, I don't think a segfault is a valid result for loading HTML and thus should not be just a P4. It is not uncommon to have the distribution libraries installed, so I expect the problem to be present on more systems.
11-05-2019

I was not aware that Ubuntu distributed a standalone JavaFX library, so yes that explains the problem. I will file an RFE to add the native / class file versioning checks that you mentioned, but that's likely to be a bit of work, since I think it would be worth doing only if done as part of the initial load library in such a way that when it fails, it considers it a failed load (and moves on to the next method of finding the libraries (although there is still some value in early detection and a thrown exception with a reasonable error message versus the crash that happens today). I think the best short-term solution is your suggestion of changing the order of precedence such that System.loadLibrary is last, which is more in keeping with what we do when running the SDK: the libraries associated with the class files should be used in preference to the system libraries.
11-05-2019

From: Matthias Bl��sing the problem on Ubuntu is this: When you install a package, that requires OpenJFX (for example mediathekview), the package libopenjfx-jni is installed as a dependency. The package libopenjfx-jni installs the OpenJFX native libraries into the folder /usr/lib/x86_64-linux-gnu/jni. This is all good if you are only using distribution libraries, but when using a third-party application, that requires JavaFX, it breaks. In my case this is Apache NetBeans, that can't even bundle a JVM (ASF requirement), so using the system VM is the logical choice. The problem is in com.sun.glass.utils.NativeLibLoader#loadLibraryInternal. The native libraries are loaded: - from filesstem in the same folder as the jar - via System#loadLibrary - extracted from the resources of the jar The options are tried in that order and the first successful wins. In my case instead of loading the working native libraries from the maven jars, the system ones are picked up via System#loadLibrary. This means, I get the OpenJFX native libraries for 11.0.2 with the OpenJFX java classes of 13-ea+7 (for the newest variant). This is obvisually a bad idea (the crash shows that clearly). For JNA two thinks are done: The native libraries are versioned independently from the java classes and after loading the library the java part checks if a compatible native library was loaded (same major, same or higher minor version). The java classes embedd the version of the native library they expect and the native library embeds its real version, so mismatches can be detected before the JVM blows. Another difference: Today JNA prefers its bundled native library if not requested differently via system property. For desktop systems JNA now tries to load the library first from the JAR and only falls back to system libraries, if that fails.
11-05-2019