/* We overlay this structure on the user-data portion of a chunk when
the chunk is stored in the per-thread cache. */typedefstructtcache_entry{structtcache_entry*next;}tcache_entry;/* There is one of these for each thread, which contains the
per-thread cache (hence "tcache_perthread_struct"). Keeping
overall size low is mildly important. Note that COUNTS and ENTRIES
are redundant (we could have just counted the linked list each
time), this is for performance reasons. */# define TCACHE_MAX_BINS 64
typedefstructtcache_perthread_struct{charcounts[TCACHE_MAX_BINS];tcache_entry*entries[TCACHE_MAX_BINS];}tcache_perthread_struct;
void*__libc_malloc(size_tbytes){...#if USE_TCACHE
/* int_free also calls request2size, be careful to not pad twice. */size_ttbytes;checked_request2size(bytes,tbytes);size_ttc_idx=csize2tidx(tbytes);if(tc_idx<mp_.tcache_bins/*&& tc_idx < TCACHE_MAX_BINS*//* to appease gcc */&&tcache&&tcache->entries[tc_idx]!=NULL){returntcache_get(tc_idx);}#endif
...}
很简单就是看 tcache 对应位置有没有 entry, 有就调用 tcache_get 并返回.
1
2
3
4
5
6
7
8
9
10
11
12
/* Caller must ensure that we know tc_idx is valid and there's
available chunks to remove. */static__always_inlinevoid*tcache_get(size_ttc_idx){tcache_entry*e=tcache->entries[tc_idx];assert(tc_idx<TCACHE_MAX_BINS);assert(tcache->entries[tc_idx]>0);tcache->entries[tc_idx]=e->next;--(tcache->counts[tc_idx]);return(void*)e;}
void__libc_free(void*mem){mstatear_ptr;mchunkptrp;/* chunk corresponding to mem */void(*hook)(void*,constvoid*)=atomic_forced_read(__free_hook);if(__builtin_expect(hook!=NULL,0)){(*hook)(mem,RETURN_ADDRESS(0));return;}if(mem==0)/* free(0) has no effect */return;p=mem2chunk(mem);if(chunk_is_mmapped(p))/* release mmapped memory. */{/* See if the dynamic brk/mmap threshold needs adjusting.
Dumped fake mmapped chunks do not affect the threshold. */if(!mp_.no_dyn_threshold&&chunksize_nomask(p)>mp_.mmap_threshold&&chunksize_nomask(p)<=DEFAULT_MMAP_THRESHOLD_MAX&&!DUMPED_MAIN_ARENA_CHUNK(p)){mp_.mmap_threshold=chunksize(p);mp_.trim_threshold=2*mp_.mmap_threshold;LIBC_PROBE(memory_mallopt_free_dyn_thresholds,2,mp_.mmap_threshold,mp_.trim_threshold);}munmap_chunk(p);return;}MAYBE_INIT_TCACHE();ar_ptr=arena_for_chunk(p);_int_free(ar_ptr,p,0);}
unsigned__int64delete(){intidx;// [rsp+Ch] [rbp-24h]
charbuf[24];// [rsp+10h] [rbp-20h] BYREF
unsigned__int64v3;// [rsp+28h] [rbp-8h]
v3=__readfsqword(0x28u);if(num<=0){puts("There is no message in system");}else{puts("Please input index of message you want to delete:");read(0,buf,8uLL);idx=atoi(buf);if(idx<0||idx>9){puts("Index is invalid!");}else{free(msglist[idx].str);msglist[idx].len=0;--num;}}return__readfsqword(0x28u)^v3;}
unsigned__int64edit(){intidx;// [rsp+Ch] [rbp-24h]
charbuf[24];// [rsp+10h] [rbp-20h] BYREF
unsigned__int64v3;// [rsp+28h] [rbp-8h]
v3=__readfsqword(0x28u);if(num<=0){puts("No message can you edit");}else{puts("Please input index of message you want to edit:");read(0,buf,8uLL);idx=atoi(buf);if(msglist[idx].len&&idx>=0&&idx<=9){puts("Now you can edit the message:");read(0,msglist[idx].str,(int)msglist[idx].len);}else{puts("Index is invalid!");}}return__readfsqword(0x28u)^v3;}
unsigned__int64display(){intidx;// [rsp+Ch] [rbp-24h]
charbuf[24];// [rsp+10h] [rbp-20h] BYREF
unsigned__int64v3;// [rsp+28h] [rbp-8h]
v3=__readfsqword(0x28u);if(num<=0){puts("No message in system");}else{puts("Please input index of message you want to display:");read(0,buf,8uLL);idx=atoi(buf);if(msglist[idx].len&&idx>=0&&idx<=9)printf("The message: %s\n",msglist[idx].str);elseputs("Index is invalid!");}return__readfsqword(0x28u)^v3;}
漏洞主要出现在删除部分. 可以看到, free 后指针没有清空, 仅仅是设置了 len 为 0. edit 和 display 的时候是通过 len 来判断的, 而不是通过指针是否为空. 而 delete 仅仅是判断了下标是否正确.