-
-
PWN入门-23-LargeBin托梦
-
发表于: 2025-5-18 22:08 1080
-
众所周知,程序申请堆内存一般可以分成两步,首先是通过GLibC封装的malloc
等接口函数向GLibC提出堆内存的申请,最后GLibC会通过brk
等系统调用向实际的物理资源管理者内核申请内存。
拿到的内存会落入top chunk
中,然后GLibC再从top chunk
中切割chunk给程序使用,至于程序释放chunk时,chunk会有两个出路,它们分别是进入空闲chunk的管理链表和重新合并进入top chunk
。
是否与top chunk
合并,取决于被释放的chunk是否与top chunk
相邻。
你不想将堆内存交给GLibC管理也不是不行,GLibC针对接口函数在内存申请其实也是依赖brk
与mmap
等系统调用实现的,你可以直接通过系统调用去获取堆内存,避免堆内存落在GLibC的手中。
GLibC对于释放时不合入top chunk
的chunk,会将它存入对应的链表中,进入链表需要分成两个阶段,在不考虑tcache机制的影响下,第一阶段GLibC会根据chunk的大小选择进入fastbinsY
或unsorted bin
。
第二阶段指的是chunk的迁移,迁移一般发生在_int_malloc
中,即程序向GLibC申请堆内存的时候。
对于fast bin
来讲,它只会在链表不为空且可用chunk空间紧张的时候,才会通过函数malloc_consolidate
合并fast bin
中的空闲chunk。
空闲chunk紧张的时机有两个,一是程序向GLibC提出属于large chunk
的堆内存大小申请,二是top chunk
空间不够,这时候GLibC都会检查have_fastchunks
标志位,看是否有可以合并的fast chunk
存在。
另一种迁移发生在函数_int_malloc
通过遍历unsorted bin
链表查找chunk的时候,当unsorted bin
链表无法提供与申请要求匹配的chunk时,_int_malloc
会将chunk从unsorted bin
中取出,然后存入small bin
或large bin
中。
从上面可以看出,unsorted bin
、small bin
以及large bin
三大链表的主要区别还是十分明显的。
unsorted bin
是small chunk
和large chunk
在释放时的第一去处,只有当链表unsorted bin
中chunk不能满足申请者的要求时,_int_malloc
函数才会将空闲的chunk迁入small bin
或large bin
中。
而unsorted bin
、small bin
以及large bin
虽然使用共同使用bins数组,但是它们在使用区域上却有着严格的划分。
其中unsorted bin
链表固定占用bin_at(M, 1)
的位置。
你应该知道small chunk
和large chunk
之间的界限是由MIN_LARGE_SIZE
的大小进行划分的,GLibC在bins数组使用区域上的划分上依旧延续了这个套路。
bin_at(M, 2)
到bin_at(M, 63)
的区域是给small bin
使用的,剩余的区域是留给large bin
使用的。
large bin
与small bin
和unsorted bin
还有一些差别。
unsorted bin
中存储chunk的大小并不固定,small chunk
和large chunk
都是可能的,而bin_at(M, 2)
到bin_at(M, 63)
所对应的small bin
链表,同一链表中存储的chunk大小都是固定的,只有small bin
链表不同时,链表中所存储的chunk大小才会有差异。
至于bin_at(M, 64)
开始对应的large bin
链表,如果仔细largebin_index
宏就会发现,该宏会将某个大小范围内的chunk归类到bin_at(M, x)
中,所以呢,单个链表large bin
中所存放的chunk大小并不一定是相同的。
这也是malloc_chunk
结构体中fd_nextsize
和bk_nextsize
两个字段存在的原因,这两个字段专门帮助large bin
链表链接同链表中不同大小的chunk。
一旦你对操作系统和GLibC的机制不熟悉,误认为通过free
接口释放后,再读写被释放的内存就会出现SIGEGV
(无效的内存访问)崩溃,那就大错特错了。
归还到GLibC手中的堆内存,GLibC并不会立即向内核提出抛弃内存的请求,所以这些内存仍然位于程序的内存布局中,对于程序来讲,释放堆内存后将对应的缓冲区变量设置成空指针才是正道。
针对UAF产生的攻击,最为常见的就是信息泄露了,由于GLibC对malloc_chunk
结构体中fdxx
和bkxx
在使用阶段和释放阶段的功能划分,导致了这些字段会在释放时写入新的内存地址数据。
假如程序并没有将缓冲区变量设置成空指针,那么fdxx
和bkxx
在使用阶段位于数据区的特性,就会导致内存地址数据被泄露。
原来的数据区释放后摇身一变就成了可以影响chunk的功能区,一旦程序仍在操作缓冲区变量时,就相当于改写了GLibC管理空闲chunk的规则,那么造成的安全隐患当然不会止于信息泄露这一条了。
除了信息泄露之外,再比较常见的攻击方式就是任意地址读写了。
这种任意地址地址读写的漏洞,该问题也是因为fdxx
和bkxx
四个在使用期和释放期中作用不明确的字段导致的。
当我们手握一个已释放chunk的读写权限,那么完成fd
位置上内存地址的泄露当然不成问题,但想要完成任意地址读写环境的创建,还需要依赖GLibC的链表管理规则。
在GLibC的空闲chunk链表的规则中,最少会用一个fd
字段链接其余的chunk,最多就如同large bin
一样,会用完fdxx
和bkxx
四个字段。
在GLibC的概念中,fdxx
和bkxx
用于链接其他的chunk,程序发出堆内存的申请之后,GLibC会考虑从链表中取出chunk返回给程序使用,漏洞在这个时候会显现出来,当chunk被取出之后,GLibC有个很重要的任务就是更新链表。
更新链表的依据就是malloc_chunk
中的fdxx
和bkxx
四个字段,它会将根据字段上存放的地址A更新链表。当程序再次申请堆内存时,申请到的chunk就是地址A所对应的内存区域了。
根据链表的特性和可以修改已释放chunk的异常现象,我们可以控制fdxx
和bkxx
四个字段,让它们指向特定的内存区域,当GLibC根据fdxx
或bkxx
取出新地址并读写时,就完成了向我们指定的任意内存区域进行读写数据的操作。
对于GLibC来讲,就像是头上带了绿帽,孩子(被篡改的chunk)已经不是自己的了,还蒙在鼓中,尽心尽力的为了这个孩子提供服务,等孩子长大了(被移出链表)才发现,原来这个孩子是跟自己没有关系的。
并且这个孩子还可能会被刺自己,协助黑客破坏系统。
从上面可以看到修改fdxx
和bkxx
指向任意的地址,使得取出的chunk指向特定的内存区域,然后再在该区域进行读写,就是任意地址读写形成的原因。
GLibC针对malloc_chunk
中fdxx
和bkxx
四个字段的策略,为我们带来的任意地址读写的可能,这种可能性需要依赖UAF,毕竟要是程序员知道free
接口的内存释放只是伪释放,那就一定会将变量设置成空指针,进而丧失对已释放chunk的操控能力。
GLibC早就注意到了任意地址读写的问题,这一问题产生源自于GLibC早前永远默认结构体malloc_chunk
上fdxx
和bkxx
中保存的数值都是正确的。
所以现在GLibC不再保持这种默认正确的规则,它会在使用前对这些数据进行检查,只要发现数据有异常就会通过malloc_printerr
抛出错误。
比较典型的就是unlink_chunk
函数中的检查,该函数会根据当前释放chunk的内存地址p
的fd
前一个入链chunk,以及bk
获取后一个入链chunk。
按照GLibC的规则,p->fd
的后一个入链chunk和p->bk
的前一个入链chunk都应该指向新释放chunk的内存地址p
,如果不是就说明p
上的数据是有问题的。
这种依赖相邻chunk的检查,在一定程度上确保了安全性,但如果你连相邻chunk都能伪造或控制,那就不好说了。
这种任意地址读写的情况当然不止unlink_chunk
这一处,只要fdxx
和bkxx
可以被恶意控制的操作都是符合要求的。
比如程序通过_int_malloc
申请chunk的时候,会尝试遍历unsorted bin
链表,当_int_malloc
发现申请内存的大小不属于small chunk
,且最近没有被切割的chunk的时候,就会将unsorted bin
链表中的bk
保存的最早入链chunk的再晚一个入链的chunk取出,因为该victim->bk
之后要么与申请大小相等直接返回给使用,要么就是移入small bin
或large bin
中,反正就是不在unsorted bin
里面了。
在这个时候victim
肯定是处于空闲状态的,假如这个时候victim
还是可以被程序写入数据的,那么bck = victim->bk
中的bck
被赋值后,bck
就会指向我们可以控制的内存区域。
后面将victim
移除链表,并将bck
更新成unsorted bin
的bk
链表的头成员时,就代表GLibC将我们设置的恶意地址看作成了新的chunk。
后面取出chunk时,取出的就是恶意地址对应的内存区域,程序拿到恶意地址后,就可以对该区域进行读写。
只不过可惜,现在已经是公元2025年了,GLibC早就针对这块进行了加固,加固的方案比较简单,仍然是根据chunk相连的特性进行检查。
在正常情况下bck
上fd
保存的是victim
,但如果bck
是被篡改过的,那么它的fd
上保存的就很难是victim
,所以当bck->fd != victim
时就会抛出错误。
针对large bin
进行的攻击与上方介绍的任意地址读写颇有相似之处。
在GLibC对堆的管理概念中,程序申请的chunk不能通过unsorted bin
链表匹配时,会将原来bk
链表上的头成员victim
取出,并将victim->bk
更新为bk
链表的头成员,被移出unsorted bin
链表的victim
,如果不能和申请大小nb
完全匹配,那么victim
进入small bin
或large bin
链表中。
进入small bin
链表时的操作相对简单,可以自行阅读源代码,但当victim
进入链表large bin
中时,操作就会变得复杂起来。
变得复杂是因为large bin
链表可以存储不同大小chunk的特殊性导致的。
当victim
的大小小于large bin
链表最早入链的bck->bk
时,GLibC会选择直接将victim
插入large bin
链表,作为最晚入链的chunk存在。
但当victim
的大小大于等于large bin
链表中原最早入链chunk时,处理操作就会变得复杂起来,主要原因是_int_malloc
函数需要遍历large bin
链表,找到一个比victim
更小的chunk。
从while
循环语句中可以看到,fwd
不断的根据fd_nextsize
去更新,而循环中止的判断条件只是fwd
小于等于victim
的size
时才会停止,那么GLibC就不怕找不到合适的chunk,导致这个循环一直进行产生死循环吗?
当然不用考虑!因为if ((size) < chunksize_nomask (bck->bk))
已经告诉我们一个事情,那就是victim
比large bin
链表中最小的chunk要大,所以在遍历字段fd_nextsize
的过程中至少也会找到一个chunk小于victim
。
当根据fd_nextsize
遍历large bin
链表找到的fwd
大小刚好等于victim
的时候,GLibC会直接让fwd
等于fwd->fd
,fwd->fd
相当于找到比fwd
更晚入链的chunk,这么做是因为GLibC会固定将victim
插入第二的位置。
直接往fwd
的前面插入不行吗,为什么是fwd
维护的fd
链表中第二的位置呢?
GLibC主要是为了避免给victim
更新xx_nextsize
,这是因为相同大小的chunk所在的链表中,只有头成员的xx_nextsize
是起作用的。
如果fwd
小于victim
,由于前面也没有找到和victim
相等chunk,所以可以知道victim
对应的大小在链表中还是唯一的,所以接下来GLibC会更新fwd
和victim
的xx_nextsize
。
从上面可以看到,当程序还握有已释放fwd
的内存地址且可以修改它时,一共会产生三处风险代码,而这些风险代码就是与fwd->xx
相关的赋值语句。
上面风险代码诞生于unsorted bin
中的large chunk
移动到large bin
链表的时候,当我们篡改fwd
上malloc_chunk
结构体占用字段的数值为恶意地址时,会导致GLibC向恶意地址上写入victim
的地址。
这个恶意地址当然是可以随意指定的,只要这片内存区域是可读可写的就可以,只不过与任意地址申请为chunk的场景有所不同,这里只能写入特定的内容。
前面提到过large bin
链表和unsorted bin
链表与其他链表有一个显著的区别,那就是large bin
和unsorted bin
允许链表中存放大小不相同的chunk。
unsorted bin
是small chunk
和large chunk
被释放时的第一去处,该链表并不会对chunk按照大小进行排序。
那么large bin
也是这样的吗?
当然不是,large bin
会将入链的chunk按照大小进行排序。
large chunk
从unsorted bin
链表移动到large bin
链表的时机,其实就发生在函数_int_malloc
的内部,当函数遍历unsroted bin
链表,发现chunk不能匹配申请者要求的时候,就会将chunk移入small bin
或large bin
中。
下面列出了large chunk
进入large bin
链表时的全部分支,从分支的判断条件上,我们可以窥探出GLibC是如何排列large bin
中的chunk的。
最先面临的判断是fwd != bck
,因为fd
和bk
两个字段所维护的都是循环链表,所以GLibC用了一个特殊的数据作为链表头,即&bins[i] - offset(fd)
,这个特殊的数据可以保证xx->fd
和xx->bk
分别指向fd
的链表头成员bins[i]
和bk
的链表头成员bins[i + 1]
,所以当fwd == bck
时,就说明链表是空的。
large bin
链表为空时,直接将chunk插入就可以,但当链表不为空时,GLibC会检查bck->bk
指向的链表头chunk是否大于待移入victim
,如果是,那就更新fwd
为bck
,此时的bck
等于&bins[i] - offset(fd)
,后面插入victim
时,会通过fwd->bk
将victim
插入bk
链表头的位置。
从这里可以看出,fd
链表和bk
链表已经不止根据chunk入链时间维护链表了,还会根据chunk的大小进行维护,并且bk
链表头上保存的永远都是链表中的最小chunk。
如果victim
的大小比bk
链表头成员要大,那就好遍历fd_nextsize
字段,在链表中找到小于等于victim
的chunk,然后完成插入。
至于bins数组中fd
字段所在的位置,它与bk
位置保存最小chunk的属性有所不同,该字段会保存链表中最大的chunk。
从if (size == chunksize_nomask(fwd))
对应的else
分支可以知道,找到一个小于待插入的victim
时就会进入else
分支,如果victim
是链表中最大的一个,那么bck = fwd->bk
语句运行完后,bck
就等于&bins[i] - offset(fd)
,从而使得最后插入时bck->fd
的值等于bins[i]
,使得fd
所在的位置被插入整个链表中最大的chunk。
向任意地址写入特定chunk地址的攻击,依赖于fd
、bk
、bk_nextsize
三个属于结构体malloc_chunk
的字段,将它们篡改后,_int_malloc
函数的流程内部,就可能将它们改写。
不管是修改哪一个字段,都要求黑客迫使_int_malloc
函数的执行流程进入chunk从链表unsorted bin
中迁移到large bin
链表的分支中。
当unsorted bin
中无法与申请大小匹配的victim
的超过large bin
链表中的空闲chunk时,就会开启基于bk
和bk_nextsize
的利用,而当大小是相等时,则会轮到fd
字段发挥可利用的作用。
在任意地址写入特定数据的攻击方式是一种较为古老的攻击方式,既然是古老的,那就代表它已经被GLibC修复掉了。
下面是GLibC的修复手法,依旧是我们非常熟悉的根据相邻chunk确认数据有效性。
上面介绍的向任意地址写入特定数据的漏洞,其实是忽略了GLibC的部分的代码的,只注意关注了else
的部分,而忽略了if
分支下的代码。
那么if
分支下的代码是不是极其稳固呢?
答案是否定的。
在下方代码中可以看到,fwd->fd
对应large bin
链表中的最晚入链chunk,另一个fwd->fd->bk_nextsize
则对应最晚入链chunk的bk_nextsize
字段。
当程序可以控制large bin
链表中最晚入链chunk的bk_nextsize
字段时,赋值语句victim->bk_nextsize
就会指向我们所设置的恶意地址。
当victim->bk_nextsize->fd_nextsize = victim
语句运行完后,恶意地址所指向内存区域偏移offset(fd_nextsize)
的地址就会被写上victim
存储的数据。
至于怎么样才能进入if
所在的分支,那就需要做到以下几点。
首先unsorted bin
链表中必须存在一个large chunk
,且这个large chunk
需要比large bin
链表中的最早入链chunk小,其次就是程序发出的堆内存申请的大小不能和unsorted bin
链表中large chunk
相等。
做到上面的两点,就可以顺利的进入if
分支了。
那么问题就又来了,类似的问题之前并非没有先例,作为业界顶级项目GLibC的维护者,难道他们就没有注意到这个问题吗?
是难以加固代码,还是根本就没有察觉呢?
fwd->fd->bk_nextsize
指向比fwd->fd
更小的chunk,而fd_nextsize
则会指向比它更大的chunk,按照道理来讲,fwd->fd->bk_nextsize->fd_nextsize
会重新指向fwd->fd
,只要检查这个结果是否成立,就可以验证数据的正确性了。
但是GLibC为什么不进行检查呢?
而且GLibC中也不是没有这样的检查啊!
负责维护链表的fd_xxxx
和bk_xxxx
的字段被篡改后,按照道理来讲,是可以影响程序申请chunk的流程的,但是事情真的能那么顺利吗?
程序想要从large bin
中获取chunk,只有一个要求,那就是unsorted bin
链表不能提供给申请者提供chunk,在结束unsorted bin
链表的遍历之后,只要GLibC通过in_smallbin_range
宏判断出申请大小不属于small chunk
的范围,就会把它视为针对large chunk
的申请。
此时会正式开始从large bin
链表中获取large chunk
。
能不能从large bin
链表中取出chunk,可不是申请大小符合large chunk
的范围就可以了,GLibC有两点要求,一是large bin
链表不为空,二是根据first
取出链表中最大的victim
跟申请大小nb
进行判断,如果申请大小小于victim
,才会从large bin
链表中取出chunk,否则的话,也找不到能匹配的chunk。
在可以从large bin
链表申请large chunk
的时候,GLibC会获取当前链表中最小的chunk,如果该chunk大于申请大小就会拿来使用,否则则会继续按照从小到大的顺序遍历链表,直到找到大于申请大小nb
的chunk。
找到chunk后,会先通过last
宏判断victim
是不是链表中最小的chunk,如果不是且victim
的大小等于victim->fd
的大小,那就会把victim->fd
取出使用。
要记得前面插入时,也是将chunk插入第二的位置,这里是与之匹配的取出操作。
接下来,GLibC会通过unlink_chunk
接口将victim
移出链表。
最后GlibC会计算victim
大小和申请大小的差值remainder_size
,当发现差值小于chunk的最小要求值时,会将整个chunk返回给用户使用,反之则会先对chunk进行分割,然后将分割出来的部分返回,剩余的部分会插入unsorted bin
链表中。
修改已释放large chunk
的bk_nextsize
字段为恶意地址后,有几率填写恶意地址偏移offset(fd_nextsize)
处的数值为chunk的地址当然是一种漏洞,但当GLibC进入函数_int_malloc
的申请chunk流程时,则又会爆发任意地址申请的漏洞。
想要让任意地址申请顺利的通过,首先要避免victim
获取到恶意地址后,GLibC检查当前chunk的mchunk_size
比申请大小还要的情况,这里需要我们修改恶意地址所在内存区域偏移offset(mchunk_size)
上的数值。
上面的循环绕过还算比较方便,但是接下来unlink_chunk
函数的绕过可就困难了,因为不仅需要给伪造的chunk构造数据,还需要给伪造chunk的fdxx
和bkxx
字段所链接的chunk构造数据,才能顺利的通过unlink_chunk
函数的检查。
至于绕过unlink_chunk
函数检查的详情,在OffByOne
那节中已经有过详细的介绍,这里就不再进行过多的介绍了。
上面介绍了针对unsroted bin
链表和large bin
链表所产生的的攻击方式,虽然unsroted bin
攻击已经被加固措施干掉了,但好在large bin
攻击依然存在。
large bin
攻击得以发生的原因,是因为victim->bk_nextsize
会被写入当前链表中最大的fwd->fd
成员的偏移offset(bk_nextsize)
处保存的数值,当这个数值被篡改后,后面的victim->bk_nextsize->fd_nextsize = victim
语句会将向恶意地址所在内存区域偏移offset(fd_nextsize)
的地方写入victim
的值。
large bin
攻击得以存在的原因,是因为被插入的victim
属于large bin
链表中最小的chunk时,缺少针对fwd->fd->bk_nextsize
的检查。
bins数组存储的三大了链表只剩下了small bin
链表,那么small bin
链表存在被攻击的可能性吗?
当然也是可以的,不过针对small bin
产生的攻击需要留到下回在进行解析。
下面直接给出了程序的源代码。
从源代码可以看到漏洞发生在函数vuln
中,vuln
主动帮我们构造了一个large bin
攻击的场景,在这里我们通过改写指针p1
上偏移offset(bk_nextsize)
的位置,让其指向变量vuln_addr
,tmp2
完成申请后,vuln_addr
会变到指针p2 - offset(fd)
的位置上,此时就实现了向任意地址写入特定地址的操作。
由于vuln
函数最后会调用system
,在原本的流程中,system
会调用msg
上填充的ls
,且msg
上的数据也不会被改写,但有了large bin
的漏洞,和read
接口写p2
指针的操作,就让改写vuln_addr->msg
成了可能。
通过上面的分析构造出下面的exploit。
运行exploit成功获得Shell。
struct malloc_chumalloc_statenk {
......
mfastbinptr fastbinsY[NFASTBINS];
......
mchunkptr bins[NBINS
*
2
-
2
];
......
}
struct malloc_chumalloc_statenk {
......
mfastbinptr fastbinsY[NFASTBINS];
......
mchunkptr bins[NBINS
*
2
-
2
];
......
}
_int_free
-
>
if
((size) <
=
get_max_fast())
-
> atomic_store_relaxed (&av
-
>have_fastchunks, true);
-
> unsigned
int
idx
=
fastbin_index(size);
-
> fb
=
&fastbin (av, idx);
-
> p
-
>fd
=
PROTECT_PTR (&p
-
>fd, old);
-
>
*
fb
=
p;
-
>
else
if
(!chunk_is_mmapped(p))
-
> _int_free_merge_chunk
-
> _int_free_create_chunk
-
>
if
(nextchunk !
=
av
-
>top)
-
> mchunkptr bck
=
unsorted_chunks (av);
-
> mchunkptr fwd
=
bck
-
>fd;
-
> p
-
>fd
=
fwd;
-
> p
-
>bk
=
bck;
-
> bck
-
>fd
=
p;
-
> fwd
-
>bk
=
p;
_int_free
-
>
if
((size) <
=
get_max_fast())
-
> atomic_store_relaxed (&av
-
>have_fastchunks, true);
-
> unsigned
int
idx
=
fastbin_index(size);
-
> fb
=
&fastbin (av, idx);
-
> p
-
>fd
=
PROTECT_PTR (&p
-
>fd, old);
-
>
*
fb
=
p;
-
>
else
if
(!chunk_is_mmapped(p))
-
> _int_free_merge_chunk
-
> _int_free_create_chunk
-
>
if
(nextchunk !
=
av
-
>top)
-
> mchunkptr bck
=
unsorted_chunks (av);
-
> mchunkptr fwd
=
bck
-
>fd;
-
> p
-
>fd
=
fwd;
-
> p
-
>bk
=
bck;
-
> bck
-
>fd
=
p;
-
> fwd
-
>bk
=
p;
_int_malloc
-
>
if
(in_smallbin_range (nb))
-
>
else
-
>
if
(atomic_load_relaxed (&av
-
>have_fastchunks))
-
> malloc_consolidate (av);
-
>
while
((victim
=
unsorted_chunks (av)
-
>bk) !
=
unsorted_chunks (av))
-
>
if
(in_smallbin_range (size))
-
> bck
=
bin_at (av, victim_index);
-
> fwd
=
bck
-
>fd;
-
>
else
-
> bck
=
bin_at (av, victim_index);
-
> fwd
=
bck
-
>fd;
-
> xx
-
>fd_nextsize
=
xxx
-
> xx
-
>bk_nextsize
=
xxx
-
> victim
-
>bk
=
bck;
-
> victim
-
>fd
=
fwd;
-
> fwd
-
>bk
=
victim;
-
> bck
-
>fd
=
victim;
-
> use_top:
-
>
if
((size) >
=
(nb
+
MINSIZE))
-
>
else
if
(atomic_load_relaxed (&av
-
>have_fastchunks))
-
> malloc_consolidate (av);
_int_malloc
-
>
if
(in_smallbin_range (nb))
-
>
else
-
>
if
(atomic_load_relaxed (&av
-
>have_fastchunks))
-
> malloc_consolidate (av);
-
>
while
((victim
=
unsorted_chunks (av)
-
>bk) !
=
unsorted_chunks (av))
-
>
if
(in_smallbin_range (size))
-
> bck
=
bin_at (av, victim_index);
-
> fwd
=
bck
-
>fd;
-
>
else
-
> bck
=
bin_at (av, victim_index);
-
> fwd
=
bck
-
>fd;
-
> xx
-
>fd_nextsize
=
xxx
-
> xx
-
>bk_nextsize
=
xxx
-
> victim
-
>bk
=
bck;
-
> victim
-
>fd
=
fwd;
-
> fwd
-
>bk
=
victim;
-
> bck
-
>fd
=
victim;
-
> use_top:
-
>
if
((size) >
=
(nb
+
MINSIZE))
-
>
else
if
(atomic_load_relaxed (&av
-
>have_fastchunks))
-
> malloc_consolidate (av);
#define unsorted_chunks(M) (bin_at (M, 1))
#define unsorted_chunks(M) (bin_at (M, 1))
#define NSMALLBINS 64
#define SMALLBIN_WIDTH MALLOC_ALIGNMENT
#define MIN_LARGE_SIZE (NSMALLBINS * SMALLBIN_WIDTH)
#define in_smallbin_range(sz) \
((unsigned
long
) (sz) < (unsigned
long
) MIN_LARGE_SIZE)
#define smallbin_index(sz) (((unsigned) (sz)) >> 4)
#define largebin_index(sz) largebin_index_64
#define NSMALLBINS 64
#define SMALLBIN_WIDTH MALLOC_ALIGNMENT
#define MIN_LARGE_SIZE (NSMALLBINS * SMALLBIN_WIDTH)
#define in_smallbin_range(sz) \
((unsigned
long
) (sz) < (unsigned
long
) MIN_LARGE_SIZE)
#define smallbin_index(sz) (((unsigned) (sz)) >> 4)
#define largebin_index(sz) largebin_index_64
#define largebin_index_64(sz) \
(((((unsigned
long
) (sz)) >>
6
) <
=
48
) ?
48
+
(((unsigned
long
) (sz)) >>
6
) :\
((((unsigned
long
) (sz)) >>
9
) <
=
20
) ?
91
+
(((unsigned
long
) (sz)) >>
9
) :\
((((unsigned
long
) (sz)) >>
12
) <
=
10
) ?
110
+
(((unsigned
long
) (sz)) >>
12
) :\
((((unsigned
long
) (sz)) >>
15
) <
=
4
) ?
119
+
(((unsigned
long
) (sz)) >>
15
) :\
((((unsigned
long
) (sz)) >>
18
) <
=
2
) ?
124
+
(((unsigned
long
) (sz)) >>
18
) :\
126
)
#define largebin_index_64(sz) \
(((((unsigned
long
) (sz)) >>
6
) <
=
48
) ?
48
+
(((unsigned
long
) (sz)) >>
6
) :\
((((unsigned
long
) (sz)) >>
9
) <
=
20
) ?
91
+
(((unsigned
long
) (sz)) >>
9
) :\
((((unsigned
long
) (sz)) >>
12
) <
=
10
) ?
110
+
(((unsigned
long
) (sz)) >>
12
) :\
((((unsigned
long
) (sz)) >>
15
) <
=
4
) ?
119
+
(((unsigned
long
) (sz)) >>
15
) :\
((((unsigned
long
) (sz)) >>
18
) <
=
2
) ?
124
+
(((unsigned
long
) (sz)) >>
18
) :\
126
)
unlink_chunk
-
> mchunkptr fd
=
p
-
>fd;
-
> mchunkptr bk
=
p
-
>bk;
-
>
if
(__builtin_expect (fd
-
>bk !
=
p || bk
-
>fd !
=
p,
0
))
-
> malloc_printerr (
"corrupted double-linked list"
);
unlink_chunk
-
> mchunkptr fd
=
p
-
>fd;
-
> mchunkptr bk
=
p
-
>bk;
-
>
if
(__builtin_expect (fd
-
>bk !
=
p || bk
-
>fd !
=
p,
0
))
-
> malloc_printerr (
"corrupted double-linked list"
);
_int_malloc
-
>
for
(;;)
-
>
while
((victim
=
unsorted_chunks (av)
-
>bk) !
=
unsorted_chunks (av))
-
> bck
=
victim
-
>bk;
-
>
if
(in_smallbin_range (nb) && bck
=
=
unsorted_chunks (av) && victim
=
=
av
-
>last_remainder && (unsigned
long
) (size) > (unsigned
long
) (nb
+
MINSIZE))
-
> ......
-
>
return
p;
-
> unsorted_chunks (av)
-
>bk
=
bck;
-
> bck
-
>fd
=
unsorted_chunks (av);
_int_malloc
-
>
for
(;;)
-
>
while
((victim
=
unsorted_chunks (av)
-
>bk) !
=
unsorted_chunks (av))
-
> bck
=
victim
-
>bk;
-
>
if
(in_smallbin_range (nb) && bck
=
=
unsorted_chunks (av) && victim
=
=
av
-
>last_remainder && (unsigned
long
) (size) > (unsigned
long
) (nb
+
MINSIZE))
-
> ......
-
>
return
p;
-
> unsorted_chunks (av)
-
>bk
=
bck;
-
> bck
-
>fd
=
unsorted_chunks (av);
_int_malloc
-
>
for
(;;)
-
>
while
((victim
=
unsorted_chunks (av)
-
>bk) !
=
unsorted_chunks (av))
-
> bck
=
victim
-
>bk;
-
>
if
(__glibc_unlikely (bck
-
>fd !
=
victim) || __glibc_unlikely (victim
-
>fd !
=
unsorted_chunks (av)))
-
> malloc_printerr (
"malloc(): unsorted double linked list corrupted"
);
_int_malloc
-
>
for
(;;)
-
>
while
((victim
=
unsorted_chunks (av)
-
>bk) !
=
unsorted_chunks (av))
-
> bck
=
victim
-
>bk;
-
>
if
(__glibc_unlikely (bck
-
>fd !
=
victim) || __glibc_unlikely (victim
-
>fd !
=
unsorted_chunks (av)))
-
> malloc_printerr (
"malloc(): unsorted double linked list corrupted"
);
_int_malloc
-
>
for
(;;)
-
>
while
((victim
=
unsorted_chunks (av)
-
>bk) !
=
unsorted_chunks (av))
-
> bck
=
victim
-
>bk;
-
> size
=
chunksize (victim);
-
> ......
-
> unsorted_chunks (av)
-
>bk
=
bck;
-
> bck
-
>fd
=
unsorted_chunks (av);
-
>
if
(size
=
=
nb)
-
> ......
-
>
return
p;
-
>
if
(in_smallbin_range (size))
-
> ......
-
>
else
-
> ......
_int_malloc
-
>
for
(;;)
-
>
while
((victim
=
unsorted_chunks (av)
-
>bk) !
=
unsorted_chunks (av))
-
> bck
=
victim
-
>bk;
-
> size
=
chunksize (victim);
-
> ......
-
> unsorted_chunks (av)
-
>bk
=
bck;
-
> bck
-
>fd
=
unsorted_chunks (av);
-
>
if
(size
=
=
nb)
-
> ......
-
>
return
p;
-
>
if
(in_smallbin_range (size))
-
> ......
-
>
else
-
> ......
if
(in_smallbin_range (size))
-
> ......
else
-
> victim_index
=
largebin_index (size);
-
> bck
=
bin_at (av, victim_index);
-
> fwd
=
bck
-
>fd;
-
>
if
(fwd !
=
bck)
-
>
if
((size) < chunksize_nomask (bck
-
>bk))
-
> fwd
=
bck;
-
> bck
=
bck
-
>bk;
-
> xxx
-
>xx_nextsize
=
xxxx
-
>
else
-
>
while
(size < chunksize_nomask (fwd))
-
> fwd
=
fwd
-
>fd_nextsize;
-
>
if
(size
=
=
chunksize_nomask (fwd))
-
> fwd
=
fwd
-
>fd;
-
>
else
-
> victim
-
>fd_nextsize
=
fwd;
-
> victim
-
>bk_nextsize
=
fwd
-
>bk_nextsize;
-
> fwd
-
>bk_nextsize
=
victim;
-
> victim
-
>bk_nextsize
-
>fd_nextsize
=
victim;
-
> bck
=
fwd
-
>bk;
-
>
else
-
> victim
-
>fd_nextsize
=
victim
-
>bk_nextsize
=
victim;
-
> victim
-
>bk
=
bck;
-
> victim
-
>fd
=
fwd;
-
> fwd
-
>bk
=
victim;
-
> bck
-
>fd
=
victim;
if
(in_smallbin_range (size))
-
> ......
else
-
> victim_index
=
largebin_index (size);
-
> bck
=
bin_at (av, victim_index);
-
> fwd
=
bck
-
>fd;
-
>
if
(fwd !
=
bck)
-
>
if
((size) < chunksize_nomask (bck
-
>bk))
-
> fwd
=
bck;
-
> bck
=
bck
-
>bk;
-
> xxx
-
>xx_nextsize
=
xxxx
-
>
else
-
>
while
(size < chunksize_nomask (fwd))
-
> fwd
=
fwd
-
>fd_nextsize;
-
>
if
(size
=
=
chunksize_nomask (fwd))
-
> fwd
=
fwd
-
>fd;
-
>
else
-
> victim
-
>fd_nextsize
=
fwd;
-
> victim
-
>bk_nextsize
=
fwd
-
>bk_nextsize;
-
> fwd
-
>bk_nextsize
=
victim;
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课