Heap-Overflow还是Buffer-Overflow?代码memset写入额外字符的原因及漏洞问询
关于这段C代码的内存操作问题分析
咱们一步一步拆解你的问题,先理清代码里的关键细节,再逐个解答:
1. 为什么memset操作会写入额外字符?
你给a和b各用malloc(12)分配了12字节堆内存,但堆上两个内存块的地址差b-a远大于12。这是因为堆分配器(比如常见的glibc ptmalloc)会给每个堆块附加元数据(比如记录块大小、空闲状态的头部),而且内存分配会按固定字节数对齐(比如16字节),所以a和b的实际地址间隔d会比12大很多。
看代码里的执行流程:
- 初始用
bzero把a和b的12字节都清为'\0' - 接着用
memset(a, 'a', 11),让a的前11位是'a',第12位保留'\0'(作为字符串终止符) - 但当你执行
memset(a, 'a', b−a)时,写入长度是d(远大于12),这会把a的12字节全部覆盖成'a'(包括原本的终止符),还会继续写入a之后的内存区域——可能是b的内存块,也可能是堆的元数据区。
而printf("%s", a)会一直读取直到遇到'\0',现在a本身已经没有终止符了,所以会继续读取后面越界覆盖的内存内容(比如b里的'b'或者其他堆数据),最终就出现了额外字符。
2. 这段代码是否存在漏洞?
是的,存在明确的堆溢出(Heap-Overflow)漏洞。
堆溢出指的是写入的数据超出了堆内存块的合法分配大小,破坏了堆的元数据或其他内存区域。这段代码里的memset(a, 'a', b−a)完全无视a的12字节限制,越界写入会篡改堆分配器维护的关键数据(比如空闲链表指针、块大小标记)。这会导致后续的free(a)或free(b)操作直接崩溃,在更复杂的场景下,攻击者甚至可以利用这种漏洞篡改堆结构,执行任意恶意代码。
3. 如果是密码存储场景,每次打印是否会出现额外字符?
结果是不确定的,不一定每次都有额外字符,原因有两点:
- 堆地址随机化(ASLR):现代系统默认开启ASLR,每次程序运行时堆的起始地址都会随机变化,
b-a的差值也会跟着变。极端情况下如果某次差值刚好等于12(几乎不可能,因为堆元数据存在),那不会越界;但大多数时候差值会大于12,触发越界。 - 越界区域的内容不确定:即使触发越界,覆盖的内存区域里是否有
'\0'终止符是随机的。有时候可能很快遇到'\0',额外字符很少;有时候可能覆盖到没有终止符的区域,打印大量乱码,甚至直接导致程序崩溃。
另外要注意:用这种方式存储密码本身就极度不安全——明文存在堆里,越界操作还可能导致密码泄露到其他内存区域,被后续的内存分配读取到。
内容的提问来源于stack exchange,提问作者Tsolakidis Konstantinos
相关产品推荐
相关产品推荐

