我正在阅读Robert C Seaford的C和C ++书中的安全编码。
int main(int argv, char* argv[]) {
char a[16];
char b[16];
char c[32];
strcpy(a, "0123456789abcdef");
strcpy(b, "0123456789abcdef");
strcpy(c, a);
strcat(c, b);
printf("a = %s\n", a);
retrun 0;
}
三个字符数组的静态声明无法为空终止字符分配存储空间。
结果,第一个strcpy()
在数组末尾写入一个空字符。
根据编译器分配存储的方式,下一行的strcpy()
可能会覆盖此空字节。如果发生这种情况,a
现在指向一个包含20个字符的数组,而b
指向一个包含10个字符的数组。
我的问题是作者的意思是数组a
包含20个字符而数组b
指向10个字符?
答案 0 :(得分:4)
我必须假设“10”我们谈论的是十六进制0x10(16),而“20”我们谈论的是十六进制0x20(32)。
无论如何,关键是字符串操作代码通过空字符的位置确定字符串的长度,而不是数组中实际分配的字符数。如果内存分配如下:
<-------a------><-------b------><--------------c-------------->
然后在第一个strcpy
后,内存看起来像这样。 (我将使用!来表示null)。
<-------a------><-------b------><--------------c-------------->
0123456789abcdef!
然后在第二个strcpy
后,内存看起来像这样。
<-------a------><-------b------><--------------c-------------->
0123456789abcdef0123456789abcdef!
每个strcpy
操作都会溢出它应该写入并使用相邻内存的数组,这是一种非常糟糕的做法。任何操纵a
的函数都是一个字符串,它会看到32个(0x20)字符(不计算空值),任何操纵b
的函数就像它的字符串一样会看到16(0x10)个字符(不是尽管你只在每个数组中为15个字符分配了足够的内存(不计算空值)。
任何这种情况都不会崩溃的事实是内存如何布局的意外。如果你在一个数组中写入的字符比你有空格要多,并且写入的相邻内存不可写,那么你就会崩溃。
当然,既然您正在阅读安全编码,那么您覆盖其他内容的事实可能是一个严重的安全问题,具体取决于输入的可信度以及被覆盖的确切内容。