Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replacing JNI Crashes by Exceptions on Android [duplicate]

I have developed an Android application that uses a native C library. I could successfully compile the whole thing using JNI, and everything works smoothly.

However, every now and then the native C library crashes (most frequently with a SIGSEGV). This, in turn, causes my application to crash without any meaningful notice for the user. What I would like to achieve is the following:

  1. Catch the signal in the native code using a signal handler (sigaction) to prevent random crashes
  2. Throw an exception in the C library that Java can catch
  3. Catch the exception in Java, generate a meaningful warning message for the user and keep the app running

In case that this is of any use for you - the JNI code runs in a separate thread (to be more precise inside an AsyncTask).

I already checked http://blog.httrack.com/blog/2013/08/23/catching-posix-signals-on-android/ and https://github.com/xroche/coffeecatch but I cannot get it to compile.

Following the suggestions from Best way to throw exceptions in JNI code? and How to catch JNI Crashes as exceptions using Signal handling based mechanism in Java and https://www.developer.com/java/data/exception-handling-in-jni.html I carried out the following steps:

In my native code I added the following function that (to my understanding) sets up a signal handler:

void initializeSignalHandler(JNIEnv* env){
    int watched_signals[] = { SIGABRT, SIGILL, SIGSEGV, SIGINT, SIGKILL }; // 6, 4, 11, 2, 9
    struct sigaction sighandler;
    memset(&sighandler, 0, sizeof(sighandler));

    sighandler.sa_sigaction = &sighandler_func;
    sighandler.sa_mask = 0;
    sighandler.sa_flags = SA_SIGINFO | SA_ONSTACK;
    for(int ii=0; ii<5; ii++){
        int signal = watched_signals[ii];
        sigaction(signal, &sighandler, NULL);        
    }
    env = env;
}

My function that handles these signals looks like this:

void sighandler_func(int sig, siginfo_t* sig_info, void* ptr){
    printerr("Sighandler: ", sig);
    jclass jcls = (*env)->FindClass(env, "java/lang/Error");
    jboolean flag = (*env)->ExceptionCheck(env);
    if (flag) {
        (*env)->ExceptionClear(env);
        /* code to handle exception */
    }
    if (jcls!=NULL){
        printerr("Throwing exception");
        (*env)->ThrowNew(env, jcls, "error message");
    }
}

My critical JNI function begins by configuring the signal handler:

JNIEXPORT jint JNICALL Java_android_playground_criticalFuction
    (JNIEnv *env, jclass c, jlong handle, jshortArray out_buffer){


    // new signal handler
    struct sigaction sighandler;
    initializeSignalHandler(env);

    // ...here goes the critical code
}

When a SIGILL occurs in my native C code the following happens:

1) On my debug terminal I get the following four messages

  • Sighandler: 4 (that corresponds to SIGILL)
  • Throwing exception
  • Sighandler: 4
  • Sighandler: 11

2) The app window closes but I do not get the Android message "Unfortunately ... was closed" that usually appears when an app crashes

I do not really understand why I get the third and fourth signal message as I would assume that the exception is thrown. In addition, I think that the exception is never really thrown (to Java).

I am lost and any help is greatly appreciated.

like image 771
Martin Avatar asked Dec 18 '17 17:12

Martin


Video Answer


1 Answers

I don't know if what you are trying to do here is technically possible. @Michael's comments imply that it isn't possible.

But if it is possible, it is a bad idea.

By the time your the native library you are using triggers a SIGSEGV, it could well have done untold damage; e.g. overwriting objects in the heap, etcetera. If you attempt to recover, there is a chance that the previous damage will result in unexpected behavior or incorrect results ... or that the GC will crash later due to heap corruption.

This is why it is standard behavior for a JVM to panic when an unexpected SIGSEGV occurs in native code OR in Java code.

While it is nice to give the users a meaningful error, there is not much meaningful that you can tell them ... if you are getting random SIGSEGV errors and other JVM panics.

like image 157
Stephen C Avatar answered Sep 22 '22 23:09

Stephen C