Repository navigation
Conversation
CryptoServicesRegistrar's static initializer also stores its default DSA and DH parameters in a thread-local of the thread that initializes it (bcgit/bc-java#2536). That thread then kept BouncyCastle's class loader alive, and with it the class loader of an embedded JRuby. Lookups fall back to the global values, so clear the thread's copies.
|
Isn't this a bit futile, there's no guarantee it will be clear by the time runtime is being cleared down? There's a lot of explanation, but the interesting part would be the use-case and the actual setup, when this happens. I'll follow the upstream response, not sure we should be messing with the explicit registrar configuration... |
|
The setup is the one in jruby/jruby#9781: a host runs Ruby code in a JRuby runtime in its own class loader, tears it down and drops the loader, while its threads live on. The Gradle daemon does this in asciidoctor/asciidoctor-gradle-plugin#810. Anything there that loads openssl, like Apart from the registrar's static initializer, only BC 1.86 has seven other classes with thread-locals. I checked them: each one holds only JDK objects, is removed in a Following upstream sounds good to me. A fix there would also cover a case this change can't tell apart: if the application set a DSA or DH thread property on the loading thread itself, this would clear that too. |
|
@kares This is part of ongoing exploration to eliminate as many lingering roots as possible. This started out as jruby/jruby#9781 but once the original use case was solved there I requested that @matrei move further exploration to the discussion at jruby/jruby#9950. This is one of the additional cases discovered along the way. I know this is a very long tail, since many (most?) Java libraries abuse thread-locals and static caches and we can't fix them all. It's just a best effort to try to find as many fixable scenarios as possible, to avoid future reports of classloader leaks after runtime teardown. |
|
Thanks, I've been following those - just wasn't sure if it's driven by actual production use-cases. We might still need to do a detailed audit of thread-locals within BC, some of the JDK stuff could be extended (by BC). An actual interesting use-case would be having JOSSL do all kinds of operations, hitting BC thread-locals, and checking if anything else leaks. |
BouncyCastle's
CryptoServicesRegistrarstores its default DSA and DH parameters globally, and also in a thread-local of the thread that initializes it, see bcgit/bc-java#2536. That thread keeps them, and through their classes BouncyCastle's class loader, for as long as it lives. When JRuby is embedded in a class loader that's thrown away later, as in jruby/jruby#9781, JRuby's class loader stays alive with it.This change clears the thread's copies in
OpenSSL.doLoad. Lookups fall back to the global values, which hold the same parameters, so nothing else changes. CallingclearThreadPropertyalso initializes the registrar on the loading thread, so that's always the thread whose copies get cleared.Class loaders still alive after four runtimes did
require 'openssl', a digest, AES, an RSA signature andSSLContext#setup, with the reproducer from jruby/jruby#9950 and-XX:SoftRefLRUPolicyMSPerMB=0, on JDK 21 and 25:master, loaded fromlibOn
master, the last one is held by theSecureRandomthatRandom.ThreadLocalHolderkeeps per thread since c08aa18, whose provider is BouncyCastle's. With-Djruby.openssl.random=shared, it's 0 of 4. I'd look at that one separately.With JRuby 10.0,
./mvnw test,rake test,rake test:provider:bc_allandrake test:provider:bc_wout_jssegive the same results as onmaster: 135 JUnit tests pass, and each rake target runs 892 tests with 0 failures and 0 errors.JRuby 10.0 bundles 0.16.3. The same change for the
0.16branch is in matrei:fix/clear-bc-thread-properties-0.16, if you'd like it there too.bc-fips has the same API, so the call works in the FIPS variant too. But bc-fips also registers a shutdown hook that keeps its class loader alive regardless.