惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
C
CXSECURITY Database RSS Feed - CXSecurity.com
L
LINUX DO - 热门话题
S
Secure Thoughts
TaoSecurity Blog
TaoSecurity Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
T
Threat Research - Cisco Blogs
AI
AI
B
Blog RSS Feed
S
Schneier on Security
雷峰网
雷峰网
Schneier on Security
Schneier on Security
Help Net Security
Help Net Security
Cloudbric
Cloudbric
L
LINUX DO - 最新话题
罗磊的独立博客
有赞技术团队
有赞技术团队
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Apple Machine Learning Research
Apple Machine Learning Research
P
Proofpoint News Feed
酷 壳 – CoolShell
酷 壳 – CoolShell
The Hacker News
The Hacker News
博客园 - Franky
Attack and Defense Labs
Attack and Defense Labs
The Cloudflare Blog
Webroot Blog
Webroot Blog
Last Week in AI
Last Week in AI
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
博客园 - 叶小钗
美团技术团队
L
Lohrmann on Cybersecurity
T
The Blog of Author Tim Ferriss
The Last Watchdog
The Last Watchdog
T
Troy Hunt's Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Vercel News
Vercel News
Know Your Adversary
Know Your Adversary
O
OpenAI News
博客园 - 【当耐特】
Hacker News - Newest:
Hacker News - Newest: "LLM"
C
Cybersecurity and Infrastructure Security Agency CISA
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
www.infosecurity-magazine.com
www.infosecurity-magazine.com
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
PCI Perspectives
PCI Perspectives
H
Heimdal Security Blog
I
InfoQ
GbyAI
GbyAI
T
Threatpost
C
Cisco Blogs

土土哥的Blog

好久没有更新博客=。= 阿里巴巴国际无线技术部 - 招人啦~求iOS、Android、Java 转-阿里巴巴国际无线技术部 - 在这里遇见最好的自己 反编译分析并模拟实现methodSignatureForSelector方法 有趣的Autolayout示例5-Masonry实现 在对象dealloc的后期执行Task-开源库TTGDeallocTaskHelper 用QuartzCode快速实现一个收藏动画 开源项目-拼图验证控件TTGPuzzleVerify的实现 Swift开源Mac App - BingWallPaper 有趣的Autolayout示例4-Masonry实现 翻译-为什么objc_msgSend必须用汇编实现 API返回结果设计经验与总结 结合访问Out Parameters出现EXC_BAD_ACCESS的例子,反编译汇编解读__autoreleasing 总结一些iOS项目中组织代码的方法 对组件化与模块化的思考与总结 开源项目-TTGTagCollectionView 有趣的Autolayout示例3-Masonry实现 Swift开源项目: TTGEmojiRate的实现 Swift写的库-TTGSnackbar 有趣的Autolayout示例2-Masonry实现 解决iOS项目的版本兼容问题-结合宏、Category和Runtime 用Runtime的手段填充任意NSObject对象的nil属性 有趣的Autolayout示例-Masonry实现 UITextView编辑时插入自定义表情-续-自定义表情图片的大小 RPC框架Thrift例子-PHP调用C++后端程序 GCD使用经验与技巧浅谈 为GCD队列绑定NSObject类型上下文数据-利用__bridge_retained(transfer)转移内存管理权 Enum-枚举的正确使用-Effective-Objective-C-读书笔记-Item-5 @autoreleasepool-内存的分配与释放 有关宏定义的经验与技巧-简化代码-增强Log Effective-Objective-C-读书笔记-Item-4-如何正确定义常量 UITextView编辑时插入自定义表情-简单的图文混编 关于评论不见了=。= Entity和Model的不同-关于代码的数据层 一次审核被拒的经历-关于iCloud到底应该备份什么数据 Block类型变量-缓存Http请求与回调 提升UITableView性能-复杂页面的优化 NSString的Copy与内存分配 利用NSProxy实现消息转发-模块化的网络接口层设计-原创 Effective-Objective-C-读书笔记-Item-3 Effective-Objective-C-读书笔记-Item-2 Effective-Objective-C-读书笔记-Item-1 iOS项目的目录结构-原创 Android开源库-LinkTextView-原创 第一篇Blog
反编译分析Xcode8的Bug, release下连续两次调用有二级指针参数的空方法会Crash
土土哥 · 2017-03-14 · via 土土哥的Blog

更新

  • 在最新的Xcode 9 Beta版本中,此Bug已被苹果修复:Bug ID 30530580: Crash on “Release” Build Configuration scheme setting for Out Parameters code

二级指针

二级指针,也叫指针的指针,或者Out Parameters,可以用来改变一个指针的地址值,由于在Objective-C里面方法、函数不支持返回多个值,所以经常用二级指针实现这个功能,比如NSFileManager- (BOOL)removeItemAtURL:(NSURL *)URL error:(NSError **)error方法,就可以让方法在内部创建error后传出。

问题

最近在Debug代码的时候,注释掉了一个带有二级指针参数的方法内部所有代码,然后在Release环境下安装运行,结果居然Crash了,猛然想起好像以前同事也遇到过,仔细检查了下,感觉代码是没有问题的,所以继续深究,新建了一个空的工程,重现了这个EXC_BAD_ACCESS的Crash,代码非常简单,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

#import <Foundation/Foundation.h>
#import <CoreFoundation/CoreFoundation.h>

@interface TestClass : NSObject
@end

@implementation TestClass

+ (void)runTest {
NSMutableArray *array = [NSMutableArray new];
[TestClass testFunc:&array];
[TestClass testFunc:&array];
NSLog(@"array: %@", array);
}

+ (void)testFunc:(NSMutableArray **)array {
}

@end

int main(int argc, const char * argv[]) {
@autoreleasepool {
[TestClass runTest];
}
return 0;
}

经测试,在Xcode 8.0、以及最新的8.2.1稳定版下,Release环境中,都会Crash,放到iOS工程里面,模拟器、真机也会。但是,在Xcode7下就没有问题。

分析

源码分析

首先,从代码本身上来看,是没有问题的,带有二级指针的方法testFunc:是空的方法,虽然传进来了NSMutableArray **array,但是没有对其赋值。

runTest方法也只是创建了一个NSMutableArray对象,然后取了指针的指针,调用了两次testFunc:,然后打印,按理来说是没有任何副作用的。

所以,直接从代码上看,发现不了问题的原因。

clang -rewrite-objc分析

直接看代码没有用,那就用clang重写出C++代码分析,命令行对main.m执行clang -rewrite-objc main.m,得到C++重写后的代码,在文件的最后,就能找到testFunc:runTest的C++代码,如下:

1
2
3
4
5
6
7
8
9
10
11

static void _C_TestClass_runTest(Class self, SEL _cmd) {
NSMutableArray *array = ((NSMutableArray *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("new"));
((void (*)(id, SEL, NSMutableArray **))(void *)objc_msgSend)((id)objc_getClass("TestClass"), sel_registerName("testFunc:"), &array);
((void (*)(id, SEL, NSMutableArray **))(void *)objc_msgSend)((id)objc_getClass("TestClass"), sel_registerName("testFunc:"), &array);
NSLog((NSString *)&__NSConstantStringImpl__var_folders__5_6lqsnn195jj6pw61j66npcnm0000gn_T_main_5a7341_mi_0, array);
}


static void _C_TestClass_testFunc_(Class self, SEL _cmd, NSMutableArray **array) {
}

从C++代码来看,也没有可能会导致EXC_BAD_ACCESS问题的释放、dealloc之类的代码,继续!

Hopper Disassembler反编译分析

重写出C++代码无法解决,就只能让Hopper出马了,毕竟“汇编面前,没有秘密”=。=,以x86_64架构二进制为例。

先来看testFunc:的:

跟预想的一致,没有任何“有效代码”。

再看runTest方法:

为了方便分析,把反编译出来的汇编代码分了段:

  1. 调用NSMutableArray new创建array变量,保存在r15寄存器上
  2. 第一次调用testFunc:
  3. retain,然后release,rbx保存array,r15释放
  4. 第二次调用testFunc:
  5. release,再retain,即释放了rbx,然后又retain,这里明显有问题
  6. 调用NSlog打印
  7. 最后release释放array变量

为了进一步验证,用Hopper的反汇编生成Objective-C代码功能进行验证,生成的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

void +[TestClass runTest](void * self, void * _cmd) {

r15 = [NSMutableArray new];

[TestClass testFunc:r15];

rbx = [r15 retain];
[r15 release];

[TestClass testFunc:rbx];

[rbx release];

rbx = [rbx retain];

NSLog(@"array: %@", rbx);

[rbx release];
return;
}

release和retain的顺序错误
Hopper生成的代码跟汇编分析基本一致,至此,可以看出,唯一“不正常”的代码就是第5段的“先release,再retain”,这会导致array变量提前释放,以至于后面再次retain无效,NSLog的时候,出现访问已被释放的变量,于是出现EXC_BAD_ACCESS崩溃。

对比Xcode7

现在已经基本可以确定问题了,但是为什么Xcode7就不会Crash?反编译一探究竟。

相同的代码,Xcode7 Release环境编译出可执行二进制文件,反编译后,用Hopper生成Objective-C代码,然后跟Xcode8的做对比,如下:

可以看出,Xcode8编译后,release和retain的顺序错了,导致了array变量提前被释放,继而EXC_BAD_ACCESS错误。而Xcode7编译的一切正常。

问题原因小结

现在可以大胆猜测,EXC_BAD_ACCESS的原因就是Xcode8的编译错误
在编译上面的代码时,Xcode8不能正确的处理array变量作为二级指针传入testFunc空函数后的release、retain顺序,导致array被提前释放,最终产生EXC_BAD_ACCESS错误。

目前已经给Xcode提了Bug report,等待苹果的回复~

所有的代码、Xcode7和8编译出的二进制可执行文件、Hopper的文件,打包放在了Github上:https://github.com/zekunyan/OutParameterPointerCrashOnXcode8

总结

汇编面前,没有秘密!=。=