Search code examples
androidexceptionjava-native-interfacenative-code

ART prevents any Java calls from JNI during native signal handling


My project use a module of capturing crash and send in android system. When handling the native crash, native code will do something and then do java calls from JNI. It works well on the Dalvik. But it fails in android version above 5.0 using ART. Because ART prevents any Java calls from JNI during native signal handling. It says that ART signal handling uses alternate signal stack, so during the process of signal handing, can't call java methods? Is there any other ways??
Flow:
1. Java call native method, but native method crashes.
2. Native crash handler catches the signal to handle the crash.
3. During the process of crash handling, calling JNI method but failed

12-31 20:36:02.516 7845-7957 A/art: art/runtime/check_jni.cc:65] JNI DETECTED ERROR IN APPLICATION: JNI IsSameObject called with pending exception 'java.lang.StackOverflowError' thrown in...
in call to IsSameObject
stack=0x9fff8000-0x9fffa000 stackSize=1036KB

Reference:https://code.google.com/p/android/issues/detail?id=162663
Would be nice if someone working with ART and/or Bionic could look into that.


Solution

  • Your project relies on undefined behavior.

    Any signal handler can safely make only calls to async-signal-safe functions. Any other function call invokes undefined behavior. Relying on the Java/Dalvik/ART virtual machine to limit itself to async-signal-safe function calls in any situation is unrealistic at best and quite likely just plain incorrect.

    Your handler for an arbitrary signal can be called at any time, leaving the VM in any possible state. There is no way to safely make Java calls from a JNI signal handler, and it's unreasonable to expect anyone to even try supporting such calls - how could a VM's designers allow a signal to interrupt any synchronized method if the signal handler were allowed to make calls synchronized on the same object? If they did that, they'd break the language by violating the very meaning of synchronized. But if they didn't do that, they'd allow deadlocks as such calls would try to lock an object that can never be unlocked because the signal interrupted processing.

    In short, Java calls via JNI from a signal handler are fundamentally unsupportable.

    The fact that they used to work for you only gave you an expectation that they would continue to do so.

    You were lucky in the past.

    It no longer works, and you can't expect it to work in the future.

    And even if you hack it into working for you somehow, it's still fundamentally unsound. The only calls that are safe to make from within a signal handler, per the POSIX standard, are:

    The following table defines a set of functions that shall be either reentrant or non-interruptible by signals and shall be async-signal-safe. Therefore applications may invoke them, without restriction, from signal-catching functions:

    _Exit()
    _exit()
    abort()
    accept()
    access()
    aio_error()
    aio_return()
    aio_suspend()
    alarm()
    bind()
    cfgetispeed()
    cfgetospeed()
    cfsetispeed()
    cfsetospeed()
    chdir()
    chmod()
    chown()
    clock_gettime()
    close()
    connect()
    creat()
    dup()
    dup2()
    execle()
    execve()
    fchmod()
    fchown()
    fcntl()
    fdatasync()
    fork()
    fpathconf()
    fstat()
    fsync()
    ftruncate()
    getegid()
    geteuid()
    getgid()
    getgroups()
    getpeername()
    getpgrp()
    getpid()
    getppid()
    getsockname()
    getsockopt()
    getuid()
    kill()
    link()
    listen()
    lseek()
    lstat()
    mkdir()
    mkfifo()
    open()
    pathconf()
    pause()
    pipe()
    poll()
    posix_trace_event()
    pselect()
    raise()
    read()
    readlink()
    recv()
    recvfrom()
    recvmsg()
    rename()
    rmdir()
    select()
    sem_post()
    send()
    sendmsg()
    sendto()
    setgid()
    setpgid()
    setsid()
    setsockopt()
    setuid()
    shutdown()
    sigaction()
    sigaddset()
    sigdelset()
    sigemptyset()
    sigfillset()
    sigismember()
    sleep()
    signal()
    sigpause()
    sigpending()
    sigprocmask()
    sigqueue()
    sigset()
    sigsuspend()
    sockatmark()
    socket()
    socketpair()
    stat()
    symlink()
    sysconf()
    tcdrain()
    tcflow()
    tcflush()
    tcgetattr()
    tcgetpgrp()
    tcsendbreak()
    tcsetattr()
    tcsetpgrp()
    time()
    timer_getoverrun()
    timer_gettime()
    timer_settime()
    times()
    umask()
    uname()
    unlink()
    utime()
    wait()
    waitpid()
    write()
    

    All functions not in the above table are considered to be unsafe with respect to signals. In the presence of signals, all functions defined by this volume of IEEE Std 1003.1-2001 shall behave as defined when called from or interrupted by a signal-catching function, with a single exception: when a signal interrupts an unsafe function and the signal-catching function calls an unsafe function, the behavior is undefined.

    Since there is no way you can ever guarantee that making a Java call via JNI from within a signal handler will only make calls to async-signal-safe functions, there's no way you can ever expect anything but undefined behavior.