Index: net/android/keystore_openssl.cc |
diff --git a/net/android/keystore_openssl.cc b/net/android/keystore_openssl.cc |
index afdca30de0d772d51261d9dfa7dbda242374e0bf..ec08d70236a84018adcda576bac38b7c17990edf 100644 |
--- a/net/android/keystore_openssl.cc |
+++ b/net/android/keystore_openssl.cc |
@@ -310,6 +310,44 @@ bool GetRsaPkeyWrapper(jobject private_key, EVP_PKEY* pkey) { |
return true; |
} |
+// On Android < 4.2, the libkeystore.so ENGINE uses CRYPTO_EX_DATA and is not |
+// added to the global engine list. If all references to it are dropped, OpenSSL |
+// will dlclose the module, leaving a dangling function pointer in the RSA |
+// CRYPTO_EX_DATA class. To work around this, leak an extra reference to the |
+// ENGINE we extract in GetRsaLegacyKey. |
+// |
+// In 4.2, this change avoids the problem: |
+// https://android.googlesource.com/platform/libcore/+/106a8928fb4249f2f3d4dba1dddbe73ca5cb3d61 |
+// |
+// https://crbug.com/381465 |
+class KeystoreEngineWorkaround { |
+ public: |
+ KeystoreEngineWorkaround() : leaked_engine_(false) {} |
+ |
+ void LeakRsaEngine(EVP_PKEY* pkey) { |
+ if (leaked_engine_) |
+ return; |
+ ScopedRSA rsa(EVP_PKEY_get1_RSA(pkey)); |
+ if (!rsa.get() || |
+ !rsa.get()->engine || |
+ strcmp(ENGINE_get_id(rsa.get()->engine), "keystore") || |
+ !ENGINE_init(rsa.get()->engine)) { |
+ NOTREACHED(); |
+ return; |
+ } |
+ leaked_engine_ = true; |
+ } |
+ |
+ private: |
+ bool leaked_engine_; |
+}; |
+ |
+void LeakRsaEngine(EVP_PKEY* pkey) { |
+ static base::LazyInstance<KeystoreEngineWorkaround>::Leaky s_instance = |
+ LAZY_INSTANCE_INITIALIZER; |
+ s_instance.Get().LeakRsaEngine(pkey); |
+} |
+ |
// Setup an EVP_PKEY to wrap an existing platform RSA PrivateKey object |
// for Android 4.0 to 4.1.x. Must only be used on Android < 4.2. |
// |private_key| is a JNI reference (local or global) to the object. |
@@ -320,6 +358,7 @@ EVP_PKEY* GetRsaLegacyKey(jobject private_key) { |
GetOpenSSLSystemHandleForPrivateKey(private_key); |
if (sys_pkey != NULL) { |
CRYPTO_add(&sys_pkey->references, 1, CRYPTO_LOCK_EVP_PKEY); |
+ LeakRsaEngine(sys_pkey); |
} else { |
// GetOpenSSLSystemHandleForPrivateKey() will fail on Android |
// 4.0.3 and earlier. However, it is possible to get the key |