【AICon】 如何构建高效的 RAG 系统?RAG 技术在实际应用中遇到的挑战及应对策略?>>> 了解详情
写点什么

手把手教你 Debug — iOS 14 ImageIO Crash 分析

  • 2021-05-31
  • 本文字数:2394 字

    阅读完需:约 8 分钟

手把手教你 Debug — iOS 14 ImageIO Crash 分析

背景

去年 9 月份开始,许多用户升级到 iOS 14 之后,线上出现很多 ImageIO 相关堆栈的 Crash 问题,而且公司内几乎所有的 APP 上都有出现,在部分 APP 上甚至达到了 Top 3  Crash。


得益于 APM 平台精准数据采集机制和丰富的异常信息现场,我们通过收集到详细的 Crash 日志信息进行分析解决。

问题定位

堆栈信息

从堆栈信息看,是在 ImageIO 解析图片信息的时候 Crash ,并且最后调用的方法都是看起来都是和 INameSpacePrefixMap 相关,推测 Crash 应该是和这个方法 CGImageSourceCopyPropertiesAtIndex 的实现有关。





问题聚合特点

机型集中在 iOS14 以上的版本,同时是在后台出现

分析

从 CrashLog 做一个初步分析


  • 从堆栈信息看,这段代码是图片库在子线程通过 CGImageSourceCopyPropertiesAtIndex 解析 imageSource 中的图片相关信息,然后发生了野指针的 Crash。

  • CGImageSourceCopyPropertiesAtIndex 的输入只有一个 imageSourceimageSource 由图片的 data 生成,调用栈并没有多线程操作,可以排除是多线程操作 imageSource、data 导致的 Crash。

  • 看堆栈是在解析 PNG 图片,通过将下发的图片格式换成 JPG 格式,发现量级并没有降低。推测 Crash 不是某种特定图片格式引起的。

反汇编分析

反汇编准备

  • iOS 14.3 的 iPhone 8

  • ImageIO 系统库:~/Library/Developer/Xcode/iOS DeviceSupport 目录下找到对应 iOS 14.3 的 ImageIO

  • 一份 iOS 14.3、iPhone 8 上发生的  CrashLog

  • Hopper

反汇编

1、从 CrashLog 上找到 Crash 对应的指令偏移地址 2555072



2、通过 Hopper 打开 ImageIO,跳转到指令偏移地址 2555072

Navigate => Go To File Offset 2555072



3、Crash 对应的指令应该是0000000181b09cc0 ldr x8, [x8, #0x10],可以看到应该是访问 [x8, #0x10]指向的内存出错



4、查看 Crashlog 中对应寄存器的值,错误地址 far: 0x000021a1ee2fa271,而且 x8 寄存器已经是一个错误的值 0x000021a1ee2fa261



5、向上回溯查看 x8 的来源

  • 0000000181b09cbc ldr x8, [x20] x8 是存在 x20 指向的内存中(即 x8 = *x20

  • 0000000181b09c98 ldr x20, [x21, #0x8] x20 又存在[x21, #0x8] 指向的内存中

  • 0000000181b09c8c adrp x21, #0x1da0ed0000000000181b09c90 add x21, x21, #0xe10x21 指向的是一个 data 段,推测 x21 应该是一个全局变量,所以,可能是这个全局变量野了,或者是这个全局变量引用的某些内存(x20)野了


6、运行时 debug 查看 x8、x20、x21 对应寄存器的值是什么

  • x21 从内存地址的名字看,应该是一个全局的 Map


ImageIO`AdobeXMPCore_Int::ManageDefaultNameSpacePrefixMap(bool)::sDefaultNameSpacePrefixMap
复制代码




7、从 Hopper 上看,这个sDefaultNameSpacePrefixMap只在

AdobeXMPCore_Int::ManageDefaultNameSpacePrefixMap(bool) 这个函数中调用。可能会在多线程下调用这个函数,而导致这个全局变量的出现 data race 导致了野指针。


__ZZN16AdobeXMPCore_IntL31ManageDefaultNameSpacePrefixMapEbE26sDefaultNameSpacePrefixMap:        // AdobeXMPCore_Int::ManageDefaultNameSpacePrefixMap(bool)::sDefaultNameSpacePrefixMap
00000001da0ede10 dq 0x0000000000000000 ; DATA XREF=__ZN16AdobeXMPCore_IntL31ManageDefaultNameSpacePrefixMapEb+44, __ZN16AdobeXMPCore_IntL31ManageDefaultNameSpacePrefixMapEb+120, __ZN16AdobeXMPCore_IntL31ManageDefaultNameSpacePrefixMapEb+392
复制代码


8、经过在运行时反复调试,这个

AdobeXMPCore_Int::ManageDefaultNameSpacePrefixMap(bool) 会在多个方法中调用(并且调用时都加了锁,不太可能会出现 data race):

  • AdobeXMPCore_Int::INameSpacePrefixMap_I::CreateDefaultNameSpacePrefixMap()

  • AdobeXMPCore_Int::INameSpacePrefixMap_I::InsertInDefaultNameSpacePrefixMap(char const*, unsigned long long, char const*, unsigned long long)

  • AdobeXMPCore_Int::INameSpacePrefixMap_I::DestroyDefaultNameSapcePrefixMap()





9、在后台线程访问访问全局变量 sDefaultNameSpacePrefixMap 时 Crash,推测可能是用户手动杀进程后,全局变量在主线程已经被析构,后台线程还会继续访问这个全局变量,从而出现野指针访问异常。发现 Crash 日志的主线程堆栈也出现 _exit 的调用,可以确定是全局变量析构导致。


Crash 发生的原因:

在用户手动杀进程后,主线程将这个全局变量析构了,这时候子线程再访问这个全局变量就出现了野指针。

复现问题

尝试在子线程不断调用 CFDictionaryRef CGImageSourceCopyPropertiesAtIndex(CGImageSourceRef isrc, size_t index, CFDictionaryRef options);,并且手动杀掉进程触发这个 crash

成功复现


可以证明上述的推理是正确的。

总结

  • CFDictionaryRef CGImageSourceCopyPropertiesAtIndex(CGImageSourceRef isrc, size_t index, CFDictionaryRef options); 这个方法在解析部分图片的时候最终会访问全局变量


mageIO`AdobeXMPCore_Int::ManageDefaultNameSpacePrefixMap(bool)::sDefaultNameSpacePrefixMap
复制代码


  • 在用户手动杀进程后,这个sDefaultNameSpacePrefixMap被析构,如果这时候在子线程再被访问就可能出现野指针的问题

修复 ImageIO Crash 方案

因为sDefaultNameSpacePrefixMap 是在系统库内部的全局变量,没办法对其进行修改,只能避免在子线程调用 CGImageSourceCopyPropertiesAtIndex 方法


  • 方法一:CGImageSourceCopyPropertiesAtIndex 是用来获取图片的宽高、imageOrientation、动图帧等信息,选择用其他方法来替换,e.g. 宽高用 CGImageRef 来获取

  • 方法二:将 CGImageSourceCopyPropertiesAtIndex 被调用的线程收敛起来,调用 atexit 函数来注册一个进程结束回调函数,进程结束的时候将终止线程


作者:字节移动技术——陈奕


本文转载自:字节跳动技术团队(ID:BytedanceTechBlog)

原文链接:手把手教你 Debug — iOS 14 ImageIO Crash 分析

2021-05-31 07:001239

评论

发布
暂无评论
发现更多内容

如何实现文件高速传输,推荐镭速高速文件传输解决方案

镭速

IoT企业物联网平台,从设备端到云端业务系统全链路开发实战——实践类

阿里云AIoT

数据库 监控 物联网 存储 消息中间件

建议收藏!数据可视化大屏设计必备步骤

葡萄城技术团队

阿里云云通信风控系统的架构与实践

阿里云视频云

云计算 云通信

2023年Java面试正确姿势(1000+面试题附答案解析)

Java编程日记

Java 架构 后端 java程序员 java面试

经常会采坑的javascript原型应试题

loveX001

JavaScript 前端

深入理解JS作用域链与执行上下文

loveX001

JavaScript

百度前端一面高频react面试题指南

beifeng1996

前端 React

逃离同质化,OPPO折叠屏正在笃定远一点的未来

脑极体

OPPO 折叠屏

美团前端常见面试题整理

loveX001

JavaScript 前端

有哪些做的不错的国产化企业协同办公软件?14个企业协同软件对比

PingCode

团队管理 团队协作 团队协作工具

低代码选型,论协同开发的重要性

葡萄城技术团队

和狂飙的 ChatGPT 聊聊软件开发的现在与未来

极狐GitLab

DevOps 研发效能 DevSecOps 极狐GitLab ChatGPT

用户属性-MQTT 5.0新特性

EMQ映云科技

物联网 IoT mqtt 企业号 2 月 PK 榜 用户属性

2023前端二面vue面试题

bb_xiaxia1998

Vue 前端

社招前端二面面试题总结

loveX001

JavaScript 前端

用ChatGPT优化AI绘画提示词的探索

Baihai IDP

人工智能 AI AIGC ChatGPT

每日一题之请描述Vue组件渲染流程

bb_xiaxia1998

Vue 前端

工业数字孪生:西门子工业网络与设备虚拟调试案例(TIA+MCD+SINETPLAN)

工赋开发者社区

每日一题之Vue的异步更新实现原理是怎样的?

bb_xiaxia1998

Vue 前端

LeetCode题解:89.格雷编码,归纳法,详细注释

Lee Chen

JavaScript LeetCode

如何使用 Kubernetes 实现应用程序的弹性伸缩

API7.ai 技术团队

Prometheus api 网关 APISIX Serverless Kubernetes

前端react面试题(边面边更)

beifeng1996

前端 React

ChatGPT专题 | 万字长文解析!复现和使用GPT-3/ChatGPT,你所应该知道的

工赋开发者社区

NFTScan x TiDB丨一栈式 HTAP 数据库为 Web3 数据服务提供毫秒级多维查询

PingCAP

TiDB

React的useLayoutEffect和useEffect执行时机有什么不同

beifeng1996

前端 React

React循环DOM时为什么需要添加key

beifeng1996

前端 React

最佳的18个JAVASCRIPT前端开发框架和库

2D3D前端可视化开发

web前端 Javascript框架 前端开发框架 webgl库 javascript库

IoT物联网设备端硬件上云技术方案详解——实践类

阿里云AIoT

物联网 存储 开发工具 数据采集 传感器

测试开发 | Dubbo 接口测试技术,测试开发进阶必备(附源码)

霍格沃兹测试开发学社

0经验拿下大厂年薪30万Offer,我的面试求职之路(含面试题)~

霍格沃兹测试开发学社

手把手教你 Debug — iOS 14 ImageIO Crash 分析_架构_字节跳动技术团队_InfoQ精选文章