1.源码分析
/*
If a small request, check regular bin. Since these "smallbins"
hold one size each, no searching within bins is necessary.
(For a large request, we need to wait until unsorted chunks are
processed to find best fit. But for small ones, fits are exact
anyway, so we can check now, which is faster.)
*/
if (in_smallbin_range (nb))
{
idx = smallbin_index (nb);
bin = bin_at (av, idx);
if ((victim = last (bin)) != bin)
{
bck = victim->bk;
if (__glibc_unlikely (bck->fd != victim))
malloc_printerr ("malloc(): smallbin double linked list corrupted");
set_inuse_bit_at_offset (victim, nb);
bin->bk = bck;
bck->fd = bin;
if (av != &main_arena)
set_non_main_arena (victim);
check_malloced_chunk (av, victim, nb);
#if USE_TCACHE
/* While we're here, if we see other chunks of the same size,
stash them in the tcache. */
size_t tc_idx = csize2tidx (nb);
if (tcache && tc_idx < mp_.tcache_bins)
{
mchunkptr tc_victim;
/* While bin not empty and tcache not full, copy chunks over. */
while (tcache->counts[tc_idx] < mp_.tcache_count
&& (tc_victim = last (bin)) != bin)
{
if (tc_victim != 0)
{
bck = tc_victim->bk;
set_inuse_bit_at_offset (tc_victim, nb);
if (av != &main_arena)
set_non_main_arena (tc_victim);
bin->bk = bck;
bck->fd = bin;
tcache_put (tc_victim, tc_idx);
}
}
}
#endif
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
}
}
情景:
1.堆中有空闲的small bin
(两个)
2.对应的tcache bins的数量小于最大数量(剩余的small bin将会被放入tcache
,这时候放入的话没有完整性检查)
3.smallbin的fd可控。
效果:
bck->fd = bin ,即
将bin的值写到了*(bck+0x10)
,我们可以将bck伪造为target_addr-0x10
,bin为libc
相关地址,则可以向target_addr
写入bin
,攻击结果和unsored bin attack
的结果类似。
原因:
当申请大小为布置好的smallbin的chunk时,会对取出的chunk进行链表的完整性检查【__glibc_unlikely (bck->fd != victim)】
,取出后若此时对应的tcache未存满,则将剩余的smallbin放入tcache中,这时候放入的话没有完整性检查,即不会检查这些small bin的fd
和bk
。每个chunk放入之前会有一次unlink操作,此时无链表的完整性检查,如果能控制chunk的fd,就能在bck->fd = bin时
将bin(libc相关地址)的值写到*(bck+0x10)这里。