FBRetainCycleDetector不能扫描__block变量的问题分析和解决方案

问题描述

这还得从使用Aspects这个库说起,如下图:

上图中的id token 的类型如下图:

AspectIdentifier 的block属性对应的是图一的传参usingBlock,很明显token引用了usingblock,usingblock引用了token,构成循环引用,这个是一个常规的循环引用内存泄漏。可是FBRetainCycleDetector没有扫描出环,一开始我怀疑是我自己改造过的AllocationTracker没有跟踪到AspectIdentifier实例(闲鱼改造了FBAllocationTracker),经排查AllocationTracker是有记录的,可是为什么没有扫描出环呢?

FBRetainCycleDetector的原理简介

1.记录下运行过程中创建和释放的实例
2.分析未释放的实例retain了哪些实例(对应深度优先遍历的临接表),逐层展开,形成一个有向图
3.使用深度优先遍历图的过程中,查看是否有成环

一般的oc对象获取retain的实例通过运行时接口很容易就可以获取到,block就比较特殊,下面介绍一下FBRetainCycleDetector 如何获取block捕获的变量,并确定哪些变量是被block强引用的:
对应的接口 static NSIndexSet _GetBlockStrongLayout(void block)

0x01:转换block对应的ABI 结构,判断是否存在block强引用的变量

判断block是否强引用其他变量并拷贝至堆上:

Block引用了 1) C++ 栈上对象 2)OC对象 3) 其他block对象 4) __block修饰的变量,并被拷贝至堆上时则需要copy/dispose辅助函数,辅助函数由编译器生成
除了case 1,其他三种case都会分别调用下面的函数:
void _Block_object_assign(void destAddr, const void object, const int flags);
void _Block_object_dispose(const void *object, const int flags);
_Block_object_assign(或者_Block_object_dispose)会根据flags的值来决定调用相应类型的copy helper(或者dispose helper)

0x02:根据block size确定捕获了多少个变量,创建出一个同样大小的block,并且给捕获的变量指针指向一个伪装的oc对象FBBlockStrongRelationDetector

0x03:调用block的dispose_helper()释放伪装的block

0x04:查看dispose_helper()调用了哪几个FBBlockStrongRelationDetector的 release接口

0x05:根据前面记录的索引下标,记录下真正被block捕获并强引用的实例

回归问题

现在回归最开始那个问题,从图一可以知道usingblock捕获了3个变量,

0x01看看fb的分析结果:


只有disposal、guideHandler,没有__block id token
对比一下三者的区别,只有token是__block声明过(这里已经确定token没有被释放,查看AllocationTracker记录是否有token变量即可),说明fb获取block捕获变量的方案对__block无效

0x02对比一下差别:

这是我声明的一个block:

对其执行clang -rewrite-objc编译转换成C++实现


从上图可以确定block捕获的变量就存放在block的ABI struct 的末尾;__block声明的变量要复杂一些,isa、forwarding、flags、size是必有,disposer、copy是obj-c对象才有

来看看block的disposer和copy函数实现:

分别对捕获的变量做了相应的disposer和copy处理,第二个参数传人的值是不一样的,估计就是这个参数有关联

解决方案

0x01 按原有的方式先把block捕获的非__block声明的strong变量找出来


看一下红色框:这里直接跳过了block的头部,做了一个小优化,直接从存放变量的索引开始

0x02 除去第一步找到的变量,对剩余的变量在做两次次过滤:

1.是否是malloc指针:不做这步过滤,会导致指针的强转取值crash,因为block可能捕获了一个inter数据,这时如果当成指针来处理,那么基本上会被当作访问非法指针而崩溃
2.去掉__block变量没有disposer和copy 函数的变量,这个使用size大小来判断即可

0x03 构造__block变量


上面每一个构造赋值都是必不可少的

0x04 再一次调用block的disposer函数,对构造的block进行释放操作,获取最后的结果

  1. diposer() 释放了 struct BlockByref 下的void *refObj对象(即FBBlockStrongRelationDetector对象)
    2.这里注意一下,为什么没有对malloc出来的的__block变量进行free?这里调用free的话,会导致crash,因为block的disposer函数调用到了__block变量自己的disposer接口的时候,已经释放了这段内存,我是通过打开malloc scribble 这个调试开关,发现这段内存有部分被置为了0x55确定的。

查看结果

从图片可以看出来__block变量已然被解析出来了

git路径

我把修改提交到FBRetainCycleDetector的github上的一个个人分支,等待fb的省核,附上git地址:
https://github.com/chaokongzwp/FBRetainCycleDetector.git

时间: 2024-10-27 00:46:29

FBRetainCycleDetector不能扫描__block变量的问题分析和解决方案的相关文章

javascript变量声明实例分析

  javascript变量声明实例分析          这篇文章主要介绍了javascript变量声明,实例分析了javascript变量声明的相关使用技巧,需要的朋友可以参考下 本文实例讲述了javascript变量声明的方法.分享给大家供大家参考.具体分析如下: js中使用一个变量之前应当先声明.变量使用关键字var来声明. 如果未在var声明语句中给变量指定初始值,则该变量值为undefined. 不用在声明变量时指定变量类型,js变量可以是任意数据类型. 使用var语句重复声明变量是

js函数内变量的作用域分析

 这篇文章主要介绍了js函数内变量的作用域分析,以实例形式简单分析了js函数的变量调用顺序,具有一定参考借鉴价值,需要的朋友可以参考下     本文实例分析了js函数内变量的作用域.分享给大家供大家参考.具体分析如下: 先看一个函数实例: 代码如下: <html> <head> </head> <body> <script type="text/javascript"> var a = 5; var c = 3; functi

JQuery EasyUI 加载两次url的原因分析及解决方案_jquery

1.传统方式 <span style="font-size:18px;">$(function () { var url = "../Source/Query/jhDataQry.ashx?action=query"; $(dg).datagrid({ url: url, queryParams: { qsrq: qsrq, zzrq: zzrq } }); }) <table id="DataGrid" class="

手板葫芦问题分析及解决方案

本文导读: 手扳葫芦可以进行提升.牵引.下降.校准等作业,已经被越来越多的用户使用,应用范围特别广泛,其常见的问题也越来越被关注,非技术型的常见问题,用户可以做出合理的调整就可以快捷解决,节约成本,提高工作效率,以下是金雕整理关于手板葫芦常见问题分析及解决方案,以供用户参考! 核心要点: 用户在使用手板葫芦碰到问题是,不要着急,应先确认问题的根源,不管是技术型问题还是非技术型问题,待解决之后,都应做好详细的记录,日后碰到同类问题是,方便及时的做出解决方案,以节约时间! 一.用户长时间的在潮湿的环

ubuntu16.04下vim安装失败的原因分析及解决方案_Linux

先给大家说下问题描述? 重装了ubuntu系统,安装vim出现了以下问题: sudo apt-get install vim 正在读取软件包列表... 完成 正在分析软件包的依赖关系树 正在读取状态信息... 完成 有一些软件包无法被安装.如果您用的是 unstable 发行版,这也许是 因为系统无法达到您要求的状态造成的.该版本中可能会有一些您需要的软件 包尚未被创建或是它们已被从新到(Incoming)目录移出. 下列信息可能会对解决问题有所帮助: 下列软件包有未满足的依赖关系: vim :

分享下今天研究的流量上限DDos攻击分析和解决方案

分享下今天研究的流量上限DDos攻击分析和解决方案   经常听到或者碰到某个网站被攻击,一般都是流量攻击.今天自己写了个程序测下相关的上限,程序只简单做了个get html操作(不包含图片等资源文件). 用一台双核CPU机器A,启100个线程,连续发送服务器B,统计出的结果是每秒钟发173个请求,机器A的发送带宽450Kbps,机器A的接收带宽2.8Mbps,机器B的发送带宽2.8Mbps,机器B的接收带宽450Kbps. 用一台双核CPU机器A,启1000个线程,连续发送服务器B,统计出的结果

Android中ListView异步加载图片错位、重复、闪烁问题分析及解决方案

Android ListView异步加载图片错位.重复.闪烁分析以及解决方案,具体问题分析以及解决方案请看下文. 我们在使用ListView异步加载图片的时候,在快速滑动或者网络不好的情况下,会出现图片错位.重复.闪烁等问题,其实这些问题总结起来就是一个问题,我们需要对这些问题进行ListView的优化. 比如ListView上有100个Item,一屏只显示10个Item,我们知道getView()中convertView是用来复用View对象的,因为一个Item的对应一个View对象,而Ima

HTTPOXY -- CGI 环境变量劫持漏洞分析

0x00 前言         昨晚,一个名为 HTTPOXY 的漏洞在安全圈内广泛传播.云盾攻防对抗团队第一时间对此漏洞进行了深入分析,发现其本质是一个 CGI 环境变量劫持漏洞,对 CGI 的环境变量 HTTP_PROXY 变量进行劫持.如果 CGI 在运行过程中依赖 HTTP_PROXY,那么攻击者将能够获取到程序敏感数据,甚至伪造返回包对 CGI 程序实现欺骗. 0x01 漏洞分析          这个漏洞实际上 CGI 程序对变量命名不规范导致的.CGI 程序在接收到 HTTP He

jQuery ajax时间差导致的变量赋值问题分析_jquery

本文实例分析了jQuery ajax时间差导致的变量赋值问题.分享给大家供大家参考,具体如下: ajax异步请求,在各种特效方面,做出了不少的贡献,有了它让用户体验更好.下面说一下曾今遇到过的一个问题,今天又遇到了,又花了我一点时间,小问题,但是特别容易忽视,并且不容易想到是什么原因产生的.废话不多说,举个例子大家就明白了. 一.准备测试文件test.php和test.html 1. test.php <?php echo "1"; ?> 2. test.html <