这段代码在memset上写了一个额外的字符,但为什么呢?
int main(int argc, char ∗argv[]) {
char ∗a, ∗b;
a=(char ∗)malloc(12);
b=(char ∗)malloc(12);
if(!a || !b)
err(2, "malloc error.\n");
bzero(a, 12); bzero(b, 12);
printf("%x %x (%i)n", a, b, b−a); // b−a is d.
memset(a, (int)'a', 11);
memset(b, (int)'b', 11);
printf("a: %sn", a);
printf("b: %sn", b);
printf("−−−n");
memset(a, (int)'a', b−a); // Heap−Overflow? Is this happening here ?
printf("a: %sn", a);
free(a);
free(b);
return 0;
}
脆弱性? 如果这是密码,每次打印后都会有一个额外的字符?
不能对指向单独数组的指针使用指针算术。a
和b
是两个不同的数组。
C17 6.5.6强调我的:
如果两个指针 操作数和结果指向同一数组对象的元素,或者指向最后一个之后的元素 元素的数组对象,计算不得产生溢出;否则,行为是未定义的。
因此,b-a
没有明确定义的结果。它可以是任何东西。因此,memset(... , b-a);
调用未定义的行为,并可能写出数组的范围。或者编译器可以将b-a
替换为零。
此外,不能保证a
和b
在内存中相邻分配。动态分配函数的堆管理器部分可以在任何位置添加大小字节和填充字节。堆可能会被分段,并强制在完全不同的位置完成分配。或者b
可以在比a
更低的地址分配,因为没有要求堆使用向上计数的分配。(例如,大多数堆栈使用向下计数。
所以是的,这非常脆弱,因为整个程序充满了错误,并且可能随时崩溃。"堆溢出",如果存在这样的术语,宁愿指正在使用的堆的所有内存,这不是这里发生的事情。
b-a
最小正值可能是 8 个字节的标头,12 个字节的数据,四舍五入到最接近的 8 的倍数,即 24。它也可能是负面的,不能保证b
的地址大于a
。它也可以是其他任何东西或让你的程序做任何事情,因为不相关的(不是相同的数组或分配的缓冲区(指针之间的指针算术是未定义的行为。
但是让我们假设它是有效的值 24(未定义的行为当然允许它是你所期望的,因为它允许任何事情(。您将 24 个字节内存到大小为 12 的缓冲区中。这是缓冲区溢出。