在Surefire中使用AddressSanitizer遇SEGV错误的解决办法问询
你遇到的这个SEGV崩溃确实是AddressSanitizer(ASAN)和JVM自身信号机制冲突导致的——JVM会主动触发并处理SEGV信号来实现一些核心功能(比如线程栈遍历、即时编译的边界检查),但ASAN默认会拦截所有SEGV信号并将其判定为致命错误,这就导致了误报崩溃。
直接忽略所有SEGV信号显然不可行,那样会漏掉JNI代码中真正的内存错误。正确的做法是让ASAN允许JVM的信号处理器正常工作,同时保留对JNI代码的检测能力,具体可以通过以下步骤调整:
1. 调整ASAN的环境变量配置
修改你的maven-surefire-plugin配置,添加ASAN_OPTIONS环境变量,关键设置两个参数:
allow_user_segv_handler=1:允许用户自定义的SEGV信号处理器(这里就是JVM的)优先运行,而不是被ASAN直接拦截detect_leaks=0:关闭ASAN的内存泄漏检测(可选但推荐,因为JVM自身的内存管理会产生大量误报,也可能引发额外的兼容性问题)
修改后的配置如下:
<configuration> <forkMode>always</forkMode> <environmentVariables> <LD_PRELOAD>/usr/lib/x86_64-linux-gnu/libasan.so.4.0.0</LD_PRELOAD> <ASAN_OPTIONS>allow_user_segv_handler=1:detect_leaks=0</ASAN_OPTIONS> </environmentVariables> </configuration>
2. 确保ASAN版本与编译环境匹配
要注意libasan.so.4.0.0对应的是GCC 7.x版本,如果你的JNI库是用更高版本的GCC编译的,需要替换成对应版本的libasan(比如GCC 8对应libasan.so.5,GCC 9+对应libasan.so.6等)。路径错误或版本不匹配也可能导致奇怪的崩溃。
3. 额外的调试选项(如果仍有问题)
如果上述配置还是有问题,可以尝试添加handle_segv=0到ASAN_OPTIONS中,这个参数会让ASAN完全不处理SEGV信号,完全交给JVM处理。但要注意,这会导致ASAN无法检测JNI代码中真正的SEGV错误,所以仅作为最后的调试手段。
原理说明
JVM的信号处理是其正常运行的一部分,ASAN默认的信号拦截逻辑会打断这个流程。通过allow_user_segv_handler=1,ASAN会在检测到SEGV时先调用JVM的信号处理器,只有当JVM不处理这个信号时,ASAN才会将其判定为错误——这样既保留了对JNI代码的内存错误检测,又不会干扰JVM的正常运行。
内容的提问来源于stack exchange,提问作者St.Antario

