Android 上面关于不受控制3RD原生代码崩溃问题

10 篇文章 0 订阅

本文主要提供在 NDK Android 上面,由于无法避免调用某些 3RD的原生代码,而该程序存在潜在BUG,意思就是会导致NATIVE上面发生错误,崩溃整个程序的一个解决办法。

本方法是基于 LINUX 系统的异常信号捕获来实现的,该方法仍旧可以使用在 Android 平台上面正确工作。

但捕获异常信号确保程序不崩溃,它不意味着不会发生其它不可预料或控制的问题,例如非托管内存资源泄露问题。

需要注意一点该函数必须是在 STAThread (单线程)下运行,否则我们需要为调用该 JNI Native 函数增加代码临界保护。

另外值得一提,请不要随意修改 JNI C/C++ 层面对于该函数的实现,否则可能遇到 JVM 检查到错误导致 JAVA 进程崩溃的问题,理论上 JAVA 是不允许从 JAVA JIT 函数内因为调用到 NATIVE,然后因为NATIVE报错,然后跳回到本NATIVE函数调用堆栈的,但这个东西实现可以说是在卡 BUG,但是卡这个BUG,Android 四个指令集多个发行版,经过验证都可以正确就绪工作。

补充:该方法仅在函数执行期间捕获到崩溃信号,例如35(FD_SCAN,Android特有多次关闭描述符崩溃信号)、SIGSEGV(段错误)才会执行异常跳转回该函数堆栈,这部分是绕过 JAVA 层面调用堆栈【多层函数平衡堆栈逻辑都不被处理】,算是个解决问题的偏方,除非真的没得办法,必须要处理,可以临时考虑这个方案,但不建议长久使用。

例子:

libcor32.c.call(() -> wv.evaluateJavascript(code, cb))

源实现: 

JAVA

    public interface ICall {
        void handle();
    }
    
    public Throwable call(ICall call) {
        if (call == null) {
            return new IllegalArgumentException("call");
        }
        final Throwable[] throwable = {null};
        synchronized (libcor32.this) {
            try {
                ccall(new ICall() {
                    @Override
                    public void handle() {
                        try {
                            call.handle();
                        } catch (Throwable ex) {
                            throwable[0] = ex;
                        }
                    }
                });
            } catch (Throwable ex) {
                throwable[0] = ex;
            }
        }
        return throwable[0];
    }

    private native void ccall(ICall call);

JNI C/C++(NDK)

inline static int signal_watch(int signo, struct sigaction *osas, sighandler_t _call_jmp_handler) {
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));

    /*init new _call_jmp_handler struct*/
    sa.sa_handler = _call_jmp_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;

    return sigaction(signo, &sa, osas);
}

inline static int signal_unwatch(int signo, struct sigaction *osas) {
    return sigaction(signo, osas, NULL);
}

__LIBCOR32__(void) Java_com_android_myapp_c_libcor32_ccall(
        JNIEnv *env,
        jobject this_,
        jobject handler_) {
    static sigjmp_buf _call_jmp_buf;
    static sighandler_t _call_jmp_handler = [](int signo) {
        siglongjmp(_call_jmp_buf, signo);
    };
    static struct sigaction __call_osas_chunks[15];

    signal_watch(35, __call_osas_chunks + 0, _call_jmp_handler); // FDSCAN(SI_QUEUE)
    signal_watch(SIGBUS, __call_osas_chunks + 1, _call_jmp_handler);
    signal_watch(SIGPIPE, __call_osas_chunks + 2, _call_jmp_handler);
    signal_watch(SIGFPE, __call_osas_chunks + 3, _call_jmp_handler);
    signal_watch(SIGSEGV, __call_osas_chunks + 4, _call_jmp_handler);
    signal_watch(SIGILL, __call_osas_chunks + 5, _call_jmp_handler);
    signal_watch(SIGTRAP, __call_osas_chunks + 6, _call_jmp_handler);
    signal_watch(SIGSYS, __call_osas_chunks + 7, _call_jmp_handler);
    signal_watch(SIGQUIT, __call_osas_chunks + 8, _call_jmp_handler);
    signal_watch(SIGIOT, __call_osas_chunks + 9, _call_jmp_handler);
    signal_watch(SIGUSR1, __call_osas_chunks + 10, _call_jmp_handler);
    signal_watch(SIGUSR2, __call_osas_chunks + 11, _call_jmp_handler);
    signal_watch(SIGXCPU, __call_osas_chunks + 12, _call_jmp_handler);
    signal_watch(SIGXFSZ, __call_osas_chunks + 13, _call_jmp_handler);
    signal_watch(SIGSTKFLT, __call_osas_chunks + 14, _call_jmp_handler);

    jint err_ = sigsetjmp(_call_jmp_buf, 1);
    if (err_ == 0) {
        jclass clazz_ = env->GetObjectClass(handler_);
        jmethodID method_ = env->GetMethodID(clazz_, "handle", "()V");
        env->CallVoidMethod(handler_, method_);
    }

    signal_unwatch(35, __call_osas_chunks + 0); // FDSCAN(SI_QUEUE)
    signal_unwatch(SIGBUS, __call_osas_chunks + 1);
    signal_unwatch(SIGPIPE, __call_osas_chunks + 2);
    signal_unwatch(SIGFPE, __call_osas_chunks + 3);
    signal_unwatch(SIGSEGV, __call_osas_chunks + 4);
    signal_unwatch(SIGILL, __call_osas_chunks + 5);
    signal_unwatch(SIGTRAP, __call_osas_chunks + 6);
    signal_unwatch(SIGSYS, __call_osas_chunks + 7);
    signal_unwatch(SIGQUIT, __call_osas_chunks + 8);
    signal_unwatch(SIGIOT, __call_osas_chunks + 9);
    signal_unwatch(SIGUSR1, __call_osas_chunks + 10);
    signal_unwatch(SIGUSR2, __call_osas_chunks + 11);
    signal_unwatch(SIGXCPU, __call_osas_chunks + 12);
    signal_unwatch(SIGXFSZ, __call_osas_chunks + 13);
    signal_unwatch(SIGSTKFLT, __call_osas_chunks + 14);

    if (err_ != 0) {
        LOG_ERROR("Android native call crash error, specific signal: %d, has been processing.",
                  err_);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值