当前位置: 首页 > news >正文

系统开发面试你会这个native crash的面试题吗?

文章目录

    • 背景:
    • Abort中止
    • Null 指针空指针异常
    • 低地址空指针异常 Null 指针(容易识别为非空指针)
    • FORTIFY 失败
    • -fstack-protector 检测到的堆栈损坏
    • 来自不允许的系统调用的 Seccomp SIGSYS

背景:

近期有学员参加fw相关的面试,回来都和马哥报喜说表现挺好的也通过了技术面,面试官都很满意,也给马哥带回来了一些面试题,也有偶尔一两个是回答的没有非常有底气的,比如关于native crash的一个面试题目:
经常在做native开发时候会有native crash,请问平常开发中遇过哪几种经典native crash请分别列举说出对应情况。
下面针对这个native crash的面试题目,马哥这边收集了几个经典的案例来给大家分享,比较常见有Abort类型,纯空指针类型,低地址空指针类型等.

Abort中止

中止操作很有趣,因为这是刻意而为。执行中止操作可通过多种不同的方法(包括调用 abort(3)、使 assert(3) 失败、使用 Android 特有的严重记录类型之一)来实现,但所有这些方法都涉及到调用 abort。abort 调用会向发起调用的线程发出 SIGABRT 信号,因此为了识别这种情况,您需要在 debuggerd 输出中查找以下两项内容:libc.so 中显示“abort”的帧,以及 SIGABRT 信号。

您可能会看到明确的“中止消息”行。不过,您还应该查看 logcat 输出,了解此线程在刻意终止自身之前所记录的内容,因为与 assert(3) 或高级别的严重记录设备不同的是,abort(3) 不接受任何消息。

当前版本的 Android 内嵌了 tgkill(2) 系统调用,因此它们的堆栈最容易读取,同时对 abort(3) 的调用位于最顶端:

pid:4637, tid:4637, name: crasher>>>crasher<<<signal6(SIGABRT), code-6(SI_TKILL), fault addr -------- Abort message:'some_file.c:123: some_function: assertion "false" failed'r0 00000000 r1 0000121d r2 00000006 r3 00000008 r4 0000121d r5 0000121d r6 ffb44a1c r7 0000010c r8 00000000 r9 00000000 r10 00000000 r11 00000000ipffb44c20 sp ffb44a08 lr eace2b0b pc eace2b16 backtrace:#00 pc 0001cb16 /system/lib/libc.so (abort+57)#01 pc 0001cd8f /system/lib/libc.so (__assert2+22)#02 pc 00001531 /system/bin/crasher (do_action+764)#03 pc 00002301 /system/bin/crasher (main+68)#04 pc 0008a809 /system/lib/libc.so (__libc_init+48)#05 pc 00001097 /system/bin/crasher (_start_main+38)

在原始中止调用(此处为帧 4)与实际发送信号(此处为帧 0)之间,较低版本的 Android 需要遵循复杂的路径。特别是在 32 位 ARM 上运行的 Android,它会将 __libc_android_abort(此处为帧 3)添加到其他平台的 raise/pthread_kill/tgkill 序列:

pid:1656, tid:1656, name: crasher>>>crasher<<<signal6(SIGABRT), code-6(SI_TKILL), fault addr -------- Abort message:'some_file.c:123: some_function: assertion "false" failed'r0 00000000 r1 00000678 r2 00000006 r3 f70b6dc8 r4 f70b6dd0 r5 f70b6d80 r6 00000002 r7 0000010c r8 ffffffed r9 00000000 sl 00000000 fp ff96ae1cip00000006 sp ff96ad18 lr f700ced5 pc f700dc98 cpsr 400b0010 backtrace:#00 pc 00042c98 /system/lib/libc.so (tgkill+12)#01 pc 00041ed1 /system/lib/libc.so (pthread_kill+32)#02 pc 0001bb87 /system/lib/libc.so (raise+10)#03 pc 00018cad /system/lib/libc.so (__libc_android_abort+34)#04 pc 000168e8 /system/lib/libc.so (abort+4)#05 pc 0001a78f /system/lib/libc.so (__libc_fatal+16)#06 pc 00018d35 /system/lib/libc.so (__assert2+20)#07 pc 00000f21 /system/xbin/crasher#08 pc 00016795 /system/lib/libc.so (__libc_init+44)#09 pc 00000abc /system/xbin/crasher您可以使用 crasher abort 重现此类崩溃问题的实例。

Null 指针空指针异常

这是典型的原生代码崩溃问题,虽然它只是下一类崩溃问题的特殊情况,但值得单独说明,因为这类崩溃问题通常无需细细思量。

在以下示例中,尽管崩溃函数在 libc.so 内,但由于字符串函数仅在指定给它们的指针处进行操作,因此您可以推断出在调用 strlen(3) 时指定的是 Null 指针;对于这类崩溃问题,应直接找发起调用的代码的作者加以解决。在这种情况下,帧 #01 是不良调用程序。

pid:25326, tid:25326, name: crasher>>>crasher<<<signal11(SIGSEGV), code1(SEGV_MAPERR), fault addr 0x0 r0 00000000 r1 00000000 r2 00004c00 r3 00000000 r4 ab088071 r5 fff92b34 r6 00000002 r7 fff92b40 r8 00000000 r9 00000000 sl 00000000 fp fff92b2cipab08cfc4 sp fff92a08 lr ab087a93 pc efb78988 cpsr 600d0030 backtrace:#00 pc 00019988 /system/lib/libc.so (strlen+71)#01 pc 00001a8f /system/xbin/crasher (strlen_null+22)#02 pc 000017cd /system/xbin/crasher (do_action+948)#03 pc 000020d5 /system/xbin/crasher (main+100)#04 pc 000177a1 /system/lib/libc.so (__libc_init+48)#05 pc 000010e4 /system/xbin/crasher (_start+96)

您可以使用 crasher strlen-NULL 重现此类崩溃问题的实例。

低地址空指针异常 Null 指针(容易识别为非空指针)

在许多情况下,故障地址不会为 0,而是其他一些小数字。两位或三位地址尤其常见,而六位地址几乎肯定不是 Null 指针解引用(它需要 1MiB 的偏移量)。通常,当您的代码将 Null 指针解引用为看似有效的结构时,就会出现这种情况。常用的函数是 fprintf(3)(或任何其他使用 FILE* 的函数)和 readdir(3),因为代码通常无法检查到底是 fopen(3) 调用先成功,还是 opendir(3) 调用先成功。

下面是一个 readdir 示例:

pid:25405, tid:25405, name: crasher>>>crasher<<<signal11(SIGSEGV), code1(SEGV_MAPERR), fault addr 0xc r0 0000000c r1 00000000 r2 00000000 r3 3d5f0000 r4 00000000 r5 0000000c r6 00000002 r7 ff8618f0 r8 00000000 r9 00000000 sl 00000000 fp ff8618dcipedaa6834 sp ff8617a8 lr eda34a1f pc eda618f6 cpsr 600d0030 backtrace:#00 pc 000478f6 /system/lib/libc.so (pthread_mutex_lock+1)#01 pc 0001aa1b /system/lib/libc.so (readdir+10)#02 pc 00001b35 /system/xbin/crasher (readdir_null+20)#03 pc 00001815 /system/xbin/crasher (do_action+976)#04 pc 000021e5 /system/xbin/crasher (main+100)#05 pc 000177a1 /system/lib/libc.so (__libc_init+48)#06 pc 00001110 /system/xbin/crasher (_start+96)

在此示例中,导致崩溃问题的直接原因是 pthread_mutex_lock(3) 曾尝试访问地址 0xc(第 0 帧)。但是 pthread_mutex_lock 执行的第一项操作是解引用指定给它的 pthread_mutex_t* 的 state 元素。如果您查看源代码,会发现该元素在结构中的偏移量为零,这表示指定给 pthread_mutex_lock 的指针 0xc 无效。从帧 1 可以看出,readdir 会将该指针指定给它,这会从指定的 DIR* 中提取 mutex_ 字段。通过查看该结构,您会发现 struct DIR 中 mutex_ 的偏移量为 sizeof(int) + sizeof(size_t) + sizeof(dirent*),在 32 位设备上表示为 4 + 4 + 4 = 12 = 0xc,这样便可以找到错误所在:调用程序向 readdir 传递了一个 Null 指针。此时,您可以将该堆栈粘贴到堆栈工具中,以找出这个问题在 logcat 中的发生位置。

structDIR{intfd_;size_t available_bytes_;dirent*next_;pthread_mutex_t mutex_;dirent buff_[15];longcurrent_pos_;};

其实在大多数情况下,您可以跳过此分析。一个充分的低位故障地址通常意味着您可以跳过堆栈中的任意 libc.so 帧,并直接归咎于发起调用的代码。不过,情况并非总是如此,这些例外将是您用作展示的绝佳机会。

您可以使用 crasher fprintf-NULL 或 crasher readdir-NULL 重现此类崩溃问题的实例。

FORTIFY 失败

FORTIFY 失败是中止的一种特殊情况,当 C 库检测到可能导致安全漏洞的问题时,就会发生 FORTIFY 失败。很多 C 库函数已得到加强;它们需要一个额外的参数来确定缓冲区的实际大小,并在运行时检查您尝试执行的操作是否真的合理。以下示例显示代码尝试使用 read(fd, buf, 32) 读入实际上只有 10 字节长的缓冲区…

pid:25579, tid:25579, name: crasher>>>crasher<<<signal6(SIGABRT), code-6(SI_TKILL), fault addr -------- Abort message:'FORTIFY: read: prevented 32-byte write into 10-byte buffer'r0 00000000 r1 000063eb r2 00000006 r3 00000008 r4 ff96f350 r5 000063eb r6 000063eb r7 0000010c r8 00000000 r9 00000000 sl 00000000 fp ff96f49cip00000000 sp ff96f340 lr ee83ece3 pc ee86ef0c cpsr 000d0010 backtrace:#00 pc 00049f0c /system/lib/libc.so (tgkill+12)#01 pc 00019cdf /system/lib/libc.so (abort+50)#02 pc 0001e197 /system/lib/libc.so (__fortify_fatal+30)#03 pc 0001baf9 /system/lib/libc.so (__read_chk+48)#04 pc 0000165b /system/xbin/crasher (do_action+534)#05 pc 000021e5 /system/xbin/crasher (main+100)#06 pc 000177a1 /system/lib/libc.so (__libc_init+48)#07 pc 00001110 /system/xbin/crasher (_start+96)

您可以使用 crasher fortify 重现此类崩溃问题的实例。

-fstack-protector 检测到的堆栈损坏

编译器的 -fstack-protector 选项会在具有栈上缓冲区的函数中插入检查机制,以防止缓冲区溢出。默认情况下,系统会为平台代码(而非应用)启用此选项。启用此选项后,编译器会向函数序言添加指令,以在堆栈上写入刚刚超过上一局部值的随机值,并向函数结尾添加指令以进行回读并确认是否发生更改。如果该值已更改,则表示该值已被缓冲区溢出覆盖,因此该结尾会调用 __stack_chk_fail 来记录消息和中止。

pid:26717, tid:26717, name: crasher>>>crasher<<<signal6(SIGABRT), code-6(SI_TKILL), fault addr -------- Abort message:'stack corruption detected'r0 00000000 r1 0000685d r2 00000006 r3 00000008 r4 ffd516d8 r5 0000685d r6 0000685d r7 0000010c r8 00000000 r9 00000000 sl 00000000 fp ffd518bcip00000000 sp ffd516c8 lr ee63ece3 pc ee66ef0c cpsr 000e0010 backtrace:#00 pc 00049f0c /system/lib/libc.so (tgkill+12)#01 pc 00019cdf /system/lib/libc.so (abort+50)#02 pc 0001e07d /system/lib/libc.so (__libc_fatal+24)#03 pc 0004863f /system/lib/libc.so (__stack_chk_fail+6)#04 pc 000013ed /system/xbin/crasher (smash_stack+76)#05 pc 00001591 /system/xbin/crasher (do_action+280)#06 pc 00002219 /system/xbin/crasher (main+100)#07 pc 000177a1 /system/lib/libc.so (__libc_init+48)#08 pc 00001144 /system/xbin/crasher (_start+96)

您可以根据回溯中是否出现 __stack_chk_fail 以及特定中止消息将此中止与其他类型的中止区分开来。

您可以使用 crasher smash-stack 重现此类崩溃问题的实例。

来自不允许的系统调用的 Seccomp SIGSYS

seccomp 系统(具体是指 seccomp-bpf)会限制对系统调用的访问权限。如需详细了解适用于平台开发者的 seccomp,请参阅博文 Android O 中的 Seccomp 过滤器。调用受限系统调用的线程将收到信号 SIGSYS 及代码 SYS_SECCOMP。系统调用编号将与架构一起显示在原因行中。需要注意的是,系统调用编号会因架构而异。例如,readlinkat(2) 系统调用在 x86 系统上的编号为 305,而在 x86-64 系统上的编号则为 267。在 arm 和 arm64 平台上,调用编号也不一样。因为系统调用编号因架构而异,所以在通常情况下,使用堆栈轨迹来找出不允许的系统调用比在标头中寻找系统调用编号更容易。

pid:11046, tid:11046, name: crasher>>>crasher<<<signal31(SIGSYS), code1(SYS_SECCOMP), fault addr -------- Cause: seccomp prevented call to disallowed arm system call99999r0 cfda0444 r1 00000014 r240000000r3 00000000 r4 00000000 r5 00000000 r6 00000000 r7 0001869f r8 00000000 r9 00000000 sl 00000000 fp fffefa58ipfffef898 sp fffef888 lr 00401997 pc f74f3658 cpsr 600f0010 backtrace:#00 pc 00019658 /system/lib/libc.so (syscall+32)#01 pc 00001993 /system/bin/crasher (do_action+1474)#02 pc 00002699 /system/bin/crasher (main+68)#03 pc 0007c60d /system/lib/libc.so (__libc_init+48)#04 pc 000011b0 /system/bin/crasher (_start_main+72)

您可以根据信号行中是否出现 SYS_SECCOMP 以及原因行中的说明,将不允许的系统调用与其他崩溃问题区分开来。

上面native crash只列举最常见的几种,不一定最全面,欢迎各位粉丝朋友进行留言补充。

原文地址:
https://mp.weixin.qq.com/s/IJU_oWFhW4ARtnKCu4arAg

更多framework实战开发案例获取,请关注下面“千里马学框架”dd马哥

http://www.jsqmd.com/news/640801/

相关文章:

  • 怎么评价大模型微调前后的效果
  • Pixel Language Portal实战案例:Hunyuan-MT-7B驱动的微信小程序多语种实时对话翻译插件开发
  • # 005、模型选择:YOLOv5/v8模型结构解析与游戏场景下的选型策略
  • 北京哪家火锅好吃又实惠,怎么找?认准美团火锅人气榜,好吃不贵更省心 - 资讯焦点
  • 2026年重庆儿童绘画领域,哪些企业值得关注?好用之选大揭秘 - 企业推荐官【官方】
  • uni离线打包实现 ios 支付StoreKit 2,其实没有想象中那么复杂,不需要写原生插件,不需要转 uts
  • 详解TCP三次握手与四次挥手
  • Agent - Reflection
  • Chord - Ink Shadow 部署详解:Windows系统下Docker与模型环境配置
  • 成都怎么找最正宗的火锅店?美团火锅人气榜实测好用,新手也能零踩雷 - 资讯焦点
  • 别再焦虑了!小白程序员必备:收藏这份AI大模型学习资源,抢占职场先机
  • 2026乡村全科执医刷题题库深扒:这两款靠谱题库值得推荐! - 医考机构品牌测评专家
  • TranslucentTB:Windows任务栏透明美化终极指南,让你的桌面焕然一新!✨
  • 多模态大模型持续学习不是“加个Adapter”就完事:深度解析Meta新论文《Continual M3AE》中提出的跨模态原型锚定机制与3周内可部署的轻量级实现路径
  • 零基础两周拿下PCTP认证:我的TiDB数据库专家备考笔记与实战避坑指南
  • HTML打包EXE三种加密方式对比:静态密码、离线一机一码、网络验证
  • MATLAB数据清洗实战:从Excel到干净数据的完整流程(附代码)
  • 3分钟让Figma说中文:设计师专属的中文界面插件终极指南
  • AI时代软件工程师的核心能力是什么:斯坦福答案
  • 误码率实战解析:从理论到应用的全方位指南
  • 从博通官网精准获取ESXi与VM虚拟机套件的实战指南
  • 硬件调试革命:3大突破让AMD Ryzen系统稳定性提升5倍
  • Rust 是如何判断对象是否相等的?一起来聊一聊 PartialEq 与 Eq
  • 最大异或和路径
  • 终极指南:如何用缠论量化插件实现通达信精准交易分析
  • AI算法入门:深度学习六周学习计划
  • LifeNet Health|人原代肝细胞3D肝球体标准化培养实操方案【曼博生物】
  • 新手建模常见错误:面反、破面、重叠
  • 用ESP-01S和51单片机做个手机遥控灯:从AT指令配置到代码烧录的保姆级避坑指南
  • 抖音无水印批量下载神器:5分钟搞定创作者素材收集的终极指南