野指针 Crash

野指针是指指向一个已删除的对象或未申请访问受限内存区域的指针。本文说的Obj-C野指针,说的是Obj-C对象释放之后指针未置空,导致的野指针(Obj-C里面一般不会出现未初始化对象的常识性错误)。

既然是访问已经释放的对象为什么不是必现Crash呢?

因为dealloc执行后只是告诉系统,这片内存我不用了,而系统并没有就让这片内存不能访问。

现实大概是下面几种可能的情况:

1.对象释放后内存没被改动过,原来的内存保存完好,可能不Crash或者出现逻辑错误(随机Crash)。

2.对象释放后内存没被改动过,但是它自己析构的时候已经删掉某些必要的东西,可能不Crash、Crash在访问依赖的对象比如类成员上、出现逻辑错误(随机Crash)。

3.对象释放后内存被改动过,写上了不可访问的数据,直接就出错了很可能Crash在objc_msgSend上面(必现Crash,常见)。

4.对象释放后内存被改动过,写上了可以访问的数据,可能不Crash、出现逻辑错误、间接访问到不可访问的数据(随机Crash)。

5.对象释放后内存被改动过,写上了可以访问的数据,但是再次访问的时候执行的代码把别的数据写坏了,遇到这种Crash只能哭了(随机Crash,难度大,概率低)!!

6.对象释放后再次release(几乎是必现Crash,但也有例外,很常见)。


1

仔细看看上面的关键路径只有出现被随机填入的数据是不可访问的时候才会必现Crash。

所以把这一随机的过程变成不随机的过程。对象释放后在内存上填上不可访问的数据,其实这种技术其实一直都有,xcode的Enable Scribble就是这个作用。


2

但是有个问题:这个方法不能放在测试那边用!因为总不能让测试装了xcode来测试吧?

于是我们自己动手实现一个,这个过程中我们要解决几个问题:

1.怎么在内存释放后填上不可访问的数据?

内存释放很可能不在我们的代码中。为此我们需要hook对象释放的接口,内存时候之后马上执行我们的破坏工作。

2.我们要重写对象释放的接口,重写哪个呢?

NSObject的dealloc、runtime的 object_dispose,C的free应该都是可以,但是各有优点,我选择的是覆盖面最广的free,free是C的函数,重写了它之后还可以顺带解决一部分C的野指针问题。

3.怎么重写?

重写C的接口场景的有两种:

a.替换系统动态库

b.hook

替换动态库太麻烦,还不知道行不行得通;hook我们就找现成的fishhook,github里面找的,但现成的代码需要防止代码冲突。

4.填充的不可访问的数据的长度怎么确定?

获取内存长度的接口不在标准库中,好在在Mac和iOS中可以用malloc_size就可以。

5.填什么?             和xcode一样,填0x55。

上hook后的free代码:

[size=0.85em]void safe_free(void* p){    size_t memSiziee=malloc_size(p);    memset(p, 0x55, memSiziee);    orig_free(p);    return;}

测试一下,出现了和Enable Scribble一样的Crash!

以上就是一种在内存释放后填充0x55使野指针后数据不能访问,从而使某些野指针从不必现Crash变成了必现;



3

其实这就是上一篇文中留下了几个问题之一,如果我们填充0x55后内存又被别的内存覆盖了,最终还是会出现随机Crash。而在真实环境中,这种情况是非常常见的。

我们再梳理一下这个过程:

1.我们在即将要释放的填了0x55,之后调用了free真正释放,内存被系统回收。

2.这个时候系统随时可能把这片内存给别的代码使用,也就是说我们的0x55被再次写上随机的数据(在这里再强调一下,访问野指针是不会Crash的,只有野指针指向的地址被写上了有问题的数据才会引发Crash)。

3.假如释放的内存上又填上了另一个对象的指针,而那个对象也有同样的一个方法,那很可能只是逻辑上有问题,并不会直接Crash,甚至悄无声息地像什么事情都没发生一样。(这个地方可能会发生多种情况,可以参考之上一篇文章中的图)

没有发生Crash可不是好事,因为这种情况如果后续再Crash,问题就非常难查,因为你看到的Crash栈很可能和出错的代码完全没有关联。既然这个问题这么棘手,最好还是和之前一样,让这个Crash提前暴露。

首先,我们要解决的问题就是怎么让系统不再往这片释放的内存上乱放东西。

要控制底层内存管理机制让它不使用这些内存可能很困难。但是,我们变通一下,简单粗暴地,我们干脆就不释放这片内存了。也就是当free被调用的时候我们不真的调用free,而是自己保留着内存,这样系统不知道这片内存已经不需要用了,自然就不会被再次写上别的数据

为了防止系统内存过快耗尽,还需要额外多做几件事:

1.自己保留的内存大于一定值的时候就释放一部分,防止被系统杀死。

2.系统内存警告的时候,也要释放一部分内存。

4

在safe_free以及它调用的函数里面尽量不要再用带锁的函数,不然很容易导致死锁。

加上这个代码之后APP的内存占用会增大不少,拿过来测试可以,但万万不能放在正式的发布版本中

关于性能问题,我的机器是iPhone5,跑在App里面运行,还算流畅(不同App性能可能会有些不同)。

可能由于锁的存在,会使cpu线程切换变得频繁,这样多线程的问题Crash率也可能会提升(最近遇到一个多线程引起的Crash很难重现,但我加了这个代码后就变成了必现Crash)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容