JNI开发使用HWAddress检测内存错误


相比于Asan和Valgrind繁琐的配置,HWAddress Sanitizer使用起来更加的简单,但前提是你有pixel3及以上型号的手机。

摘自官方文档

与传统的 ASan 相比,HWASan 具有如下特征:

  • 类似的 CPU 开销(约为 2 倍)
  • 类似的代码大小开销 (40 – 50%)
  • 更小的 RAM 开销 (10% – 35%)

HWASan 能检测到 ASan 所能检测到的同一系列错误:

  • 堆栈和堆缓冲区上溢/下溢
  • 释放之后的堆使用情况
  • 超出范围的堆栈使用情况
  • 重复释放/错误释放

此外,HWASan 还可以检测:

  • 返回之后的堆栈使用情况
示例:
#include <jni.h>
#include <string>
#include <stdlib.h>
#include <thread>

extern "C" JNIEXPORT void JNICALL
Java_com_example_sanitizertest_MainActivity_doUseAfterFree(
        JNIEnv *env, jobject /* this */) {
    char * volatile p = new char[10];
    delete[] p;
    p[5] = 42;
}

extern "C" JNIEXPORT void JNICALL
Java_com_example_sanitizertest_MainActivity_doHeapBufferOverflow(
        JNIEnv *env, jobject /* this */) {
    char * volatile p = new char[16];
    p[16] = 42;
    delete[] p;
}

extern "C" JNIEXPORT void JNICALL
Java_com_example_sanitizertest_MainActivity_doHeapBufferOverflowReadLoop(
        JNIEnv *env, jobject /* this */) {
    for (int i = 0; i < 0x10000; ++i) {
        char * volatile p = new char[16];
        volatile char x = p[32];
        x++;
        delete[] p;
    }
}

extern "C" JNIEXPORT void JNICALL
Java_com_example_sanitizertest_MainActivity_doDoubleFree(
        JNIEnv *env, jobject /* this */) {
    char * volatile p = new char[16];
    delete[] p;
    delete[] p;
}

extern "C" JNIEXPORT void JNICALL
Java_com_example_sanitizertest_MainActivity_doNullDeref(
        JNIEnv *env, jobject /* this */) {
    char * volatile p = (char *)nullptr;
    p[42] = 1;
}

static void RunUAFLoop() {
    constexpr int kLoopCount = 100;
    constexpr int kAllocCount = 1000;
    volatile char sink;
    char **p = new char*[kAllocCount];
    for (int j = 0; j < kLoopCount; ++j) {
        for (int i = 0; i < kAllocCount; ++i)
            p[i] = new char[128];
        for (int i = 0; i < kAllocCount; ++i)
            delete[] p[i];
        for (int i = 0; i < kAllocCount; ++i)
            sink = p[i][42];
    }
    delete[] p;
}

extern "C" JNIEXPORT void JNICALL
Java_com_example_sanitizertest_MainActivity_doUseAfterFreeLoop(
        JNIEnv *env, jobject /* this */) {
    std::thread t(RunUAFLoop);
    t.detach();
}

1)接入教程

第一步:

根据你的pixel手机型号进行刷机,可以直接通过chrome刷机,比起命令行操作,简单不少,全部都是自动化操作;

第二步:

你可以在cmake中添加一个宏或者不加,通过flavor传递宏来控制是否进行内存检测,比如:

externalNativeBuild.cmake { arguments "-DHWASAN=1" }
第三步:

在cmakelist的target_link_libraries之后设置:

if (ANDROID_ABI STREQUAL "arm64-v8a" AND HWASAN)
        target_compile_options(yourTarget PUBLIC -fsanitize=hwaddress -fno-omit-frame-pointer)
        set_target_properties(yourTarget PROPERTIES LINK_FLAGS -fsanitize=hwaddress)
    endif ()

yourTarget是你的库的名称,HWASan仅支持Android10(29 )以上和64位系统,并且要求NDK>=21;

第四步:

在刷好Rom的pixel手机上运行你的app即可,当检测到内存错误时,可以在 /data/tombstones 文件夹中找到对应的文件。


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注