Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5e2a8849 authored by Andreas Gampe's avatar Andreas Gampe
Browse files

LockAgent: Add ability to generate a native crash

Use the crasher to create a tombstone with the violation as the abort
message.

Test: m
Test: manual
Change-Id: I8c5041a4fbffedb11b6b9c564ab1e214551896bf
parent a6874fcc
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@ cc_library {
    include_dirs: [
        // NDK headers aren't available in platform NDK builds.
        "libnativehelper/include_jni",
        // Use ScopedUtfChars.
        "libnativehelper/header_only_include",
    ],
    header_libs: [
        "libopenjdkjvmti_headers",
@@ -33,6 +35,8 @@ cc_binary_host {
    include_dirs: [
        // NDK headers aren't available in platform NDK builds.
        "libnativehelper/include_jni",
        // Use ScopedUtfChars.
        "libnativehelper/header_only_include",
    ],
    header_libs: [
        "libopenjdkjvmti_headers",
+58 −1
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@
#include <memory>
#include <sstream>

#include <unistd.h>

#include <jni.h>

#include <jvmti.h>
@@ -26,10 +28,14 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>

#include <nativehelper/scoped_utf_chars.h>

// We need dladdr.
#if !defined(__APPLE__) && !defined(_WIN32)
@@ -61,6 +67,7 @@
namespace {

JavaVM* gJavaVM = nullptr;
bool gForkCrash = false;

// Converts a class name to a type descriptor
// (ex. "java.lang.String" to "Ljava/lang/String;")
@@ -372,7 +379,7 @@ void prepareHook(jvmtiEnv* env) {
    }
}

jint attach(JavaVM* vm, char* options ATTRIBUTE_UNUSED, void* reserved ATTRIBUTE_UNUSED) {
jint attach(JavaVM* vm, char* options, void* reserved ATTRIBUTE_UNUSED) {
    gJavaVM = vm;

    jvmtiEnv* env;
@@ -383,9 +390,59 @@ jint attach(JavaVM* vm, char* options ATTRIBUTE_UNUSED, void* reserved ATTRIBUTE

    prepareHook(env);

    std::vector<std::string> config = android::base::Split(options, ",");
    for (const std::string& c : config) {
        if (c == "native_crash") {
            gForkCrash = true;
        }
    }

    return JVMTI_ERROR_NONE;
}

extern "C" JNIEXPORT
jboolean JNICALL Java_com_android_lock_1checker_LockHook_getNativeHandlingConfig(JNIEnv*, jclass) {
    return gForkCrash ? JNI_TRUE : JNI_FALSE;
}

extern "C" JNIEXPORT void JNICALL Java_com_android_lock_1checker_LockHook_nWtf(JNIEnv* env, jclass,
        jstring msg) {
    if (!gForkCrash || msg == nullptr) {
        return;
    }

    // Create a native crash with the given message. Decouple from the current crash to create a
    // tombstone but continue on.
    //
    // TODO: Once there are not so many reports, consider making this fatal for the calling process.
    ScopedUtfChars utf(env, msg);
    if (utf.c_str() == nullptr) {
        return;
    }
    const char* args[] = {
        "/system/bin/lockagent_crasher",
        utf.c_str(),
        nullptr
    };
    pid_t pid = fork();
    if (pid < 0) {
        return;
    }
    if (pid == 0) {
        // Double fork so we return quickly. Leave init to deal with the zombie.
        pid_t pid2 = fork();
        if (pid2 == 0) {
            execv(args[0], const_cast<char* const*>(args));
            _exit(1);
            __builtin_unreachable();
        }
        _exit(0);
        __builtin_unreachable();
    }
    int status;
    waitpid(pid, &status, 0);  // Ignore any results.
}

extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) {
    return attach(vm, options, reserved);
}
+11 −0
Original line number Diff line number Diff line
@@ -72,14 +72,20 @@ public class LockHook {

    private static final LockChecker[] sCheckers;

    private static boolean sNativeHandling = false;

    static {
        sHandlerThread = new HandlerThread("LockHook:wtf", Process.THREAD_PRIORITY_BACKGROUND);
        sHandlerThread.start();
        sHandler = new WtfHandler(sHandlerThread.getLooper());

        sCheckers = new LockChecker[] { new OnThreadLockChecker() };

        sNativeHandling = getNativeHandlingConfig();
    }

    private static native boolean getNativeHandlingConfig();

    static <T> boolean shouldDumpStacktrace(StacktraceHasher hasher, Map<String, T> dumpedSet,
            T val, AnnotatedStackTraceElement[] st, int from, int to) {
        final String stacktraceHash = hasher.stacktraceHash(st, from, to);
@@ -175,7 +181,12 @@ public class LockHook {
    private static void handleViolation(Violation v) {
        String msg = v.toString();
        Log.wtf(TAG, msg);
        if (sNativeHandling) {
            nWtf(msg);  // Also send to native.
        }
    }

    private static native void nWtf(String msg);

    /**
     * Generates a hash for a given stacktrace of a {@link Throwable}.