Hi, Thanks for the reply. Yes -- my project uses JFX JARs rather than jmods (as many do). And the clashing application in question, must be the same. But this is a real problem that occurred with a real user, and I only have a handful of users.
Oracle signing the JFX DLLs, while an improvement, would still leave the following problems wide open: - There's no way to stop a developer (or some build tool) from re-signing or removing signature from signed DLLs, in which case this problem can still re-occur - In the event of a genuine difference of a DLLs (under the same cache folder version), if the DLL cannot be deleted (to be replaced), because a running application application is using it -- a completely feasibly scenario -- then we have one application breaking another. Also, the developers shipping apps are in full control of the JFX JARs, their DLLs, and the *reported" javafx.version and javafx.runtime.version. E.g., I could have JFX 21.0.5 in a JFX 23.0.2 cache folder if I wanted simply by changing javafx.runtime.version (or javafx.cachedir). It's far too brittle. Regards, *Cormac Redmond* Software Engineer, Certak Ltd. e: credm...@certak.com | m: +353 (0) 86 268 2152 | w: www.certak.com On Sat, 8 Feb 2025 at 12:49, Christopher Schnick <crschn...@xpipe.io> wrote: > I think that went a bit under the radar as this only occurs when using the > maven dependencies. The SDK and jmod downloads do not have that issue as > they don't copy DLLs into any temp directory. Only the maven jars have to > do this. > > About signing any JavaFX DLLs, that would indeed be a good addition > considering all other JDK DLLs are signed. > > Best > Christopher Schnick > On 08/02/2025 13:31, Cormac Redmond wrote: > > Hi, > > I am surprised nobody else sees this bug as a higher-priority conversation > point. > > It's troubling to see how running one self-contained application can break > another self-contained application (because of a cache that most JFX devs > wouldn't even know exist). > > If one well-behaved JFX application cannot delete/replace a file JFX cache > on start-up, because another well-behaved JFX application is using that > cached file (it will be if built on the same JFX version) -- then the > application will not run, at all. > > And as I explained earlier, this is a likely occurring scenario in the > wild -- the only reason this bug isn't more prevalent /reported / > noticeable, is that it's not too likely for a user to have two JFX > applications using the same JavaFX version, on their machine. But that's > down to pure coincidence / chance. I wouldn't say the same for Electron or > Flutter, etc. If they had this bug, it would be noticed and it would be > news. > > Also, the creators of these applications would have no idea that their > application is not starting, nor would the users know why. > > By the way, the problem is not just about *signed* DLLs + checksums, > obviously. It would occur if the DLLs are different for any other > reason too, obviously, and the authors of NativeLibLoader thought this > possibility is high enough to do the checksum + delete + replace check. The > application shouldn't silently fail because of a cache management bug. > > > > > Regards, > > *Cormac Redmond* > Software Engineer, Certak Ltd. > > e: credm...@certak.com | m: +353 (0) 86 268 2152 | w: www.certak.com > > > > On Thu, 6 Feb 2025 at 19:56, Cormac Redmond <credm...@certak.com> wrote: > >> Hi, >> >> I have found a "serious" bug, where two completely independent JFX >> applications, both with their own embedded runtime (built with jlink & >> jpackage) & using the same JavaFX version, are unable to run >> simultaneously, because of the JFX cache -- at least on Windows. >> >> When trying to run any application, NativeLibLoader does a checksum on >> DLLs in the cache (e.g.: >> C:\Users\xyz\.openjfx\cache\23.0.2+3\amd64\prism_d3d.dll); >> and tries to delete any files that exist where the checksums do not match >> (in order to replace them): >> >> if (!Arrays.equals(isHash, fileHash)) { >>> Files.delete(f.toPath()); >>> } >> >> >> But a second application *fails* to start, as it is unable to delete >> these files because they're in use by the first application (see stacktrace >> below). >> >> But why are the checksums different, shouldn't they be the same? They are >> different because the DLLs are signed by the builder of the applications -- >> different authors and different timestamps. So the DLL checksums will be >> different despite the DLLs being the same, in terms of the JFX version. >> >> Why are these JFX DLLs signed by the authors? Because for some reason, >> they come *unsigned*, and all DLLs when packaged *should* be signed, >> including any embedded in JARs, to avoid alarming Windows Defender warnings >> and mistrust, etc. There's no point spending a small fortune on Windows >> code-signing certs and shipping with any unsigned third-party DLLs >> (including any embedded in JARs). You might sign your own binaries and any >> unsigned third-party binaries. Similarly for MacOS, everything, including >> embedded native libs in JARs, etc., needs to be signed for gatekeeper & >> notarization to allow it. >> >> So it would be better if JFX DLLs came signed, to avoid forcing >> developers to do it (which would also avoid this checksum mishap). Or, if >> developers need to sign Oracle's DLLs, then this checksum approach isn't >> suitable. Also, there should really be a proper fallback in place -- a >> cache bug should not have such a catastrophic outcome. >> >> FYI: JLink also removes signatures from a bunch of JDK DLLs when >> assembling the runtime, but leaves a bunch of Windows DLLs >> untouched. Personally, I'd prefer all DLLs to come signed, and let the >> developers re-sign if they want. Removing the signatures is adding >> unnecessary hurdles for folks, for no benefit I can see. >> >> Anyway, example stacktrace: >> >> Loading D3D native library ... >>> WARNING: java.lang.UnsatisfiedLinkError: Can't load library: C:\Program >>> Files\KafkIO\bin\prism_d3d.dll >>> Loading library prism_d3d from resource failed: >>> java.nio.file.AccessDeniedException: C:\Users\xyz >>> \.openjfx\cache\23.0.2+3\amd64\prism_d3d.dll >>> java.nio.file.AccessDeniedException: C:\Users\xyz >>> \.openjfx\cache\23.0.2+3\amd64\prism_d3d.dll >>> at >>> java.base/sun.nio.fs.WindowsException.translateToIOException(Unknown Source) >>> at >>> java.base/sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source) >>> at >>> java.base/sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source) >>> at >>> java.base/sun.nio.fs.WindowsFileSystemProvider.implDelete(Unknown Source) >>> at >>> java.base/sun.nio.fs.AbstractFileSystemProvider.delete(Unknown Source) >>> at java.base/java.nio.file.Files.delete(Unknown Source) >>> at >>> com.sun.glass.utils.NativeLibLoader.cacheLibrary(NativeLibLoader.java:300) >>> at >>> com.sun.glass.utils.NativeLibLoader.installLibraryFromResource(NativeLibLoader.java:218) >>> at >>> com.sun.glass.utils.NativeLibLoader.loadLibraryFromResource(NativeLibLoader.java:200) >>> at >>> com.sun.glass.utils.NativeLibLoader.loadLibraryInternal(NativeLibLoader.java:142) >>> at >>> com.sun.glass.utils.NativeLibLoader.loadLibrary(NativeLibLoader.java:58) >>> at >>> com.sun.prism.d3d.D3DPipeline.lambda$static$0(D3DPipeline.java:54) >>> at java.base/java.security.AccessController.doPrivileged(Unknown >>> Source) >>> at com.sun.prism.d3d.D3DPipeline.<clinit>(D3DPipeline.java:50) >>> at java.base/java.lang.Class.forName0(Native Method) >>> at java.base/java.lang.Class.forName(Unknown Source) >>> at java.base/java.lang.Class.forName(Unknown Source) >>> at >>> com.sun.prism.GraphicsPipeline.createPipeline(GraphicsPipeline.java:218) >>> at >>> com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.init(QuantumRenderer.java:92) >>> at >>> com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:125) >>> at java.base/java.lang.Thread.run(Unknown Source) >>> GraphicsPipeline.createPipeline failed for com.sun.prism.d3d.D3DPipeline >>> java.lang.UnsatisfiedLinkError: no prism_d3d in java.library.path: >>> C:\Program >>> Files\KafkIO;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\Program >>> Files\Oculus\Support\oculus-runtime;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;c:\dev\apps\apache-maven-3.9.9\bin;C:\dev\apps\cygwin64\bin;C:\Program >>> Files >>> (x86)\Windows Kits\10\Windows Performance Toolkit\;C:\Program >>> Files\dotnet\;C:\Program Files\Git\cmd;C:\Program Files\7-Zip;C:\Program >>> Files\SafeNet\Authentication\SAC\x64;C:\Program >>> Files\SafeNet\Authentication\SAC\x32;C:\Program >>> Files\Docker\Docker\resources\bin;%JAVA_HOME%\bin;;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\System32\OpenSSH\;C:\Users\ >>> xyz\scoop\apps\zulu-jdk\current\bin;C:\Users\xyz >>> \scoop\apps\zulu22-jdk\current\bin;C:\Users\xyz >>> \scoop\apps\zulu21-jdk\current\bin;C:\Users\xyz \scoop\shims;C:\Users\x >>> yz \AppData\Local\Microsoft\WindowsApps;C:\dev\scripts;C:\Program >>> Files\JetBrains\IntelliJ IDEA 2024.2\bin;C:\Program Files\KafkIO\app;. >>> at java.base/java.lang.ClassLoader.loadLibrary(Unknown Source) >>> at java.base/java.lang.Runtime.loadLibrary0(Unknown Source) >>> at java.base/java.lang.System.loadLibrary(Unknown Source) >>> at >>> com.sun.glass.utils.NativeLibLoader.loadLibraryInternal(NativeLibLoader.java:170) >>> at >>> com.sun.glass.utils.NativeLibLoader.loadLibrary(NativeLibLoader.java:58) >>> at >>> com.sun.prism.d3d.D3DPipeline.lambda$static$0(D3DPipeline.java:54) >>> at java.base/java.security.AccessController.doPrivileged(Unknown >>> Source) >>> at com.sun.prism.d3d.D3DPipeline.<clinit>(D3DPipeline.java:50) >>> at java.base/java.lang.Class.forName0(Native Method) >>> at java.base/java.lang.Class.forName(Unknown Source) >>> at java.base/java.lang.Class.forName(Unknown Source) >>> at >>> com.sun.prism.GraphicsPipeline.createPipeline(GraphicsPipeline.java:218) >>> at >>> com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.init(QuantumRenderer.java:92) >>> at >>> com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:125) >>> at java.base/java.lang.Thread.run(Unknown Source) >> >> >> >> Kind Regards, >> Cormac >> >> >>