首页 > 解决方案 > ReleasePrimitiveArrayElements 期间的 LogHeapCorruption

问题描述

当我们的应用程序通过 JNI(数百个元素)获得大量流量时,我们似乎遇到了很多堆损坏错误(似乎更大的元素更容易发生这种情况)。

abort 0x0000007e32cdf360
art::Runtime::Abort(char const*) 0x0000007daf4c22ac
android::base::LogMessage::~LogMessage() 0x0000007e33a6a654
art::gc::Verification::LogHeapCorruption(art::ObjPtr<art::mirror::Object>, art::MemberOffset, art::mirror::Object*, bool) const 0x0000007daf298318
art::gc::collector::ConcurrentCopying::MarkNonMoving(art::Thread*, art::mirror::Object*, art::mirror::Object*, art::MemberOffset) 0x0000007daf226b98
art::gc::collector::ConcurrentCopying::ThreadFlipVisitor::VisitRoots(art::mirror::CompressedReference<art::mirror::Object>**, unsigned long, art::RootInfo const&) 0x0000007daf22909c
art::Thread::HandleScopeVisitRoots(art::RootVisitor*, int) 0x0000007daf50af7c
void art::Thread::VisitRoots<false>(art::RootVisitor*) 0x0000007daf50e840
art::gc::collector::ConcurrentCopying::ThreadFlipVisitor::Run(art::Thread*) 0x0000007daf22870c
art::(anonymous namespace)::CheckJNI::ReleasePrimitiveArrayElements(char const*, art::Primitive::Type, _JNIEnv*, _jarray*, void*, int) 0x0000007daf37c680
Java_org_libsodium_jni_SodiumJNI_crypto_1aead_1xchacha20poly1305_1ietf_1decrypt sodium-jni.c:156
art_quick_generic_jni_trampoline 0x0000007daf148354
<unknown> 0x000000009d05bbe8

似乎导致的行位于此处(我们的代码是开源的)https://github.com/standardnotes/react-native-sodium/blob/367b61a90180fe75ddef5b599e01c47cb4761b1f/android/src/main/cpp/sodium-jni.c# L156。我尝试对此进行更多调试,但我的 JNI + CPP 知识有限。对于以更好的方式将数据从 Java 交换到 C++,您有什么建议吗?

代码片段:

JNIEXPORT jint JNICALL
Java_org_libsodium_jni_SodiumJNI_crypto_1aead_1xchacha20poly1305_1ietf_1decrypt(JNIEnv *jenv,
                                                                                jclass clazz,
                                                                                jbyteArray j_m,
                                                                                jintArray j_mlen_p,
                                                                                jbyteArray j_nsec,
                                                                                jbyteArray j_c,
                                                                                jint j_clen,
                                                                                jbyteArray j_ad,
                                                                                jint j_adlen,
                                                                                jbyteArray j_npub,
                                                                                jbyteArray j_k) {
    unsigned char *c = as_unsigned_char_array(jenv, j_c);
    unsigned char *m = (unsigned char *) (*jenv)->GetByteArrayElements(jenv, j_m, 0);
    unsigned char *npub = as_unsigned_char_array(jenv, j_npub);
    unsigned char *ad = as_unsigned_char_array(jenv, j_ad);
    unsigned char *nsec = as_unsigned_char_array(jenv, j_nsec);
    unsigned char *k = as_unsigned_char_array(jenv, j_k);

    int result = crypto_aead_xchacha20poly1305_ietf_decrypt(m, j_mlen_p, nsec, c, j_clen, ad, j_adlen, npub, k);
    (*jenv)->ReleaseByteArrayElements(jenv, j_m, (jbyte *) m, 0);
    return (jint)result;
}

从java调用:

@ReactMethod
  public void crypto_aead_xchacha20poly1305_ietf_decrypt(final String cipherText, final String public_nonce, final String key, final String additionalData, final Promise p) {
    try {
      byte[] c = this.base64ToBin(cipherText, Sodium.base64_variant_ORIGINAL());
      byte[] npub = this.hexToBin(public_nonce);
      byte[] k = this.hexToBin(key);
      if (c == null || c.length <= 0)
        p.reject(ESODIUM,ERR_FAILURE);
      else if (npub.length != Sodium.crypto_aead_xchacha20poly1305_IETF_NPUBBYTES())
        p.reject(ESODIUM,ERR_BAD_NONCE);
      else if (k.length != Sodium.crypto_aead_xchacha20poly1305_IETF_KEYBYTES())
        p.reject(ESODIUM,ERR_BAD_KEY);
      else {
        byte[] ad = additionalData != null ? additionalData.getBytes(StandardCharsets.UTF_8) : null;
        int adlen = additionalData != null ? ad.length : 0;
        int[] decrypted_len = new int[1];
        byte[] decrypted = new byte[c.length - Sodium.crypto_aead_chacha20poly1305_IETF_ABYTES()];

        int result = Sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(decrypted, decrypted_len, null, c, c.length, ad, adlen, npub, k);
        if (result != 0)
          p.reject(ESODIUM,ERR_FAILURE);
        else
          p.resolve(new String(decrypted, StandardCharsets.UTF_8));
      }
    }
    catch (Throwable t) {
      p.reject(ESODIUM,ERR_FAILURE,t);
    }
  }

似乎大多数时候它发生在更大的元素上,但并非总是如此。也发生在crypto_1aead_1xchacha20poly1305_1ietf_1encrypt.

标签: androidc++java-native-interface

解决方案


ReleasePrimitiveArrayElements意味着->ReleaseByteArrayElements()

这个问题很可能是您所指的,JNIEnv*并且在某些时候它与线程分离(处理时间会相当有趣)。你需要以JNIEnv*不同的方式获得,例如。类似AttachCurrentThreadIfNeeded()。另请参阅 JNI线程


推荐阅读