链表错误:AddressSanitizer: heap-use-after-free on address

本文讨论了在实现C++链表时遇到的一个常见错误,即AddressSanitizer报告的heap-use-after-free问题。错误发生在修剪二叉搜索树的代码中,由于在递归过程中错误地删除了节点,导致后续访问已释放的内存。通过移除导致问题的`delete root;`语句,可以避免该错误,但可能会引起内存泄漏。解决方案是在适当的地方正确管理内存,以确保程序的健壮性和效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在写链表时会遇见的错误

ERROR: AddressSanitizer: heap-use-after-free on address 0x603000000258 at pc 0x00000037c6f5 bp 0x7ffea5071f90 sp 0x7ffea5071f88
READ of size 8 at 0x603000000258 thread T0
#3 0x7f3fea5be082 (/lib/x86_64-linux-gnu/libc.so.6+0x24082)
0x603000000258 is located 8 bytes inside of 24-byte region [0x603000000250,0x603000000268)
freed by thread T0 here:
#3 0x7f3fea5be082 (/lib/x86_64-linux-gnu/libc.so.6+0x24082)
previously allocated by thread T0 here:
#4 0x7f3fea5be082 (/lib/x86_64-linux-gnu/libc.so.6+0x24082)
Shadow bytes around the buggy address:
0x0c067fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c067fff8000: fa fa fd fd fd fa fa fa fd fd fd fa fa fa fd fd
0x0c067fff8010: fd fa fa fa 00 00 00 07 fa fa fd fd fd fa fa fa
0x0c067fff8020: fd fd fd fa fa fa fd fd fd fa fa fa fd fd fd fa
0x0c067fff8030: fa fa fd fd fd fa fa fa fd fd fd fa fa fa fd fd
=>0x0c067fff8040: fd fa fa fa fd fd fd fa fa fa fd[fd]fd fa fa fa
0x0c067fff8050: 00 00 00 fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8090: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap:

代码:

class Solution {	//669. 修剪二叉搜索树
public:
	TreeNode* trimBST(TreeNode* root, int low, int high) {
		if (root == nullptr) return root;
		if (root->val < low) {
			TreeNode* tmp = trimBST(root->right, low, high);
			delete root;	//有问题,可以不加这句话,但是可能有内存泄漏问题
			return tmp;
		}
		if (root->val > high) {
			TreeNode* tmp = trimBST(root->left, low, high);
			delete root;	//有问题,可以不加这句话,但是可能有内存泄漏问题
			return tmp;
		}

		root->left = trimBST(root->left, low, high);
		root->right = trimBST(root->right, low, high);
		return root;
	}
};

把delete root;去掉,不会再出现这个问题。

<think>嗯,用户这次的需求很明确,是关于CTF比赛中pwn类题目里的字典管理挑战。从用户使用的关键词"pwn binary security dictionary management challenge CTF"来看,ta应该是个有一定二进制安全基础的参赛者,正面临需要构造特定字典的题目场景。 这类题目通常出现在CTF的中高难度环节,用户可能需要突破字典限制来实现漏洞利用。我想到几个关键点:首先是堆块复用场景下的字典管理,比如通过伪造size字段绕过检查;其次是字符串处理中的字典技巧,像利用strtok等函数的分隔特性;还有更复杂的如House of Spirit这种需要精心构造fake chunk的利用方式。 用户可能正在解一道需要同时控制多个堆块指针的题目,或者遇到需要精确布局内存的题目。考虑到CTF题目的演变趋势,这类题目往往会把字典管理和use-after-free漏洞结合起来,比如通过字典操作维持悬空指针的可用性。 在准备回答时,应该突出实战性。先解释基本概念,再用具体CTF题目案例说明,最后给出可操作的构造模板。要注意提醒常见陷阱,比如glibc版本差异对堆布局的影响,毕竟很多选手会在本地环境调试时踩这个坑。 用户没有提供具体题目信息,所以需要覆盖多种常见场景。重点展示如何通过字典管理控制内存布局,这是这类题目的核心考点。可以引用一些经典CTF赛题作为参考,但要注意避免直接泄露现役赛题的解法。</think>以下是针对CTF中Pwn类"字典管理"挑战的全面解析及实战指南,这类题目通常涉及堆利用、内存破坏和逻辑漏洞的组合: --- ### 核心漏洞场景与利用模式 1. **堆块复用漏洞** - 题目常模拟自定义字典数据结构(如哈希表+链表),在 **删除键值对** 时未清理指针,导致UAF: ```c void dict_delete(dict_t* d, char* key) { entry_t* entry = find_key(d, key); // 仅从链表移除但未释放内存 list_remove(d->entries, entry); // 未置空:entry->key = NULL; entry->value = NULL; } ``` - **利用手段**:通过`add`重新申请相同大小堆块,控制悬空指针写入恶意数据 2. **整数溢出触发堆溢出** - 键值长度校验缺陷: ```c // 错误:用uint8_t存储长度(最大255) void dict_add(dict_t* d, char* key, uint8_t klen, char* val, uint8_t vlen) { entry_t* e = malloc(sizeof(entry_t) + klen + vlen); memcpy(e->data, key, klen); // 若klen+vlen>255则溢出 memcpy(e->data+klen, val, vlen); } ``` - **利用手段**:构造`klen=0xFF, vlen=0xFF`,触发`malloc(0x1FE)`但复制`0x1FF`字节数据 --- ### 经典利用技术实战(附PoC) #### 案例1:通过字典操作实现Fastbin Dup ```python # 步骤1:创建两个相同大小的键值对 add("A", "x"*24) # chunkA add("B", "y"*24) # chunkB # 步骤2:删除并重新申请控制fd指针 delete("A") delete("B") # 此时fastbin: B->A add("C", p64(0x601080)) # 篡改B的fd指向目标地址(如GOT表) # 步骤3:触发恶意指针解引用 get("C") # 程序读取0x601080处数据 ``` #### 案例2:堆布局实现Unlink攻击 ```c // 题目伪代码:字典使用双向链表管理 typedef struct entry { char key[16]; char* value; struct entry* prev; struct entry* next; } entry_t; // 利用步骤: // 1. 创建两个相邻块:entryA和entryB // 2. 伪造entryB的prev/next指针指向恶意结构 // 3. 删除entryB触发unlink:P->next = B->next ``` --- ### 防御绕过技巧 1. **对抗Safe-Linking**(GLIBC 2.32+) - 泄露堆地址:通过打印字典内容泄露残留指针 - 计算XOR掩码:`mask = (leak_addr >> 12) ^ target_addr` ```python # 计算加密后的fd指针 safe_fd = (heap_leak >> 12) ^ target_address ``` 2. **大小限制绕过** - 分块存储:将shellcode拆分为多个键值存储 - 链式触发:通过字典遍历操作(如`dict_print`)拼接执行 --- ### 真题实战参考 1. **0CTF 2021 - dict** - 关键漏洞:UAF + 未初始化指针 - 利用链:`delete->add`控制函数指针 → 劫持`__free_hook` 2. **HITCON CTF 2019 - lazyhouse** - 结合大小限制和off-by-one - 通过字典扩容机制触发堆重叠 --- ### 训练资源推荐 1. **实验环境** ```bash git clone https://github.com/io12/pwninit && cd pwninit ./setup_dict_challenge.py # 自动部署含ASLR的字典题目环境 ``` 2. **调试工具** ```gdb # 在delete操作后自动检查堆状态 b *dict_delete+0x50 command heap bins x/32gx (void*)&dict_entries end ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海螺蜜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值