如果我尝试访问malloc()区域以外的内存会发生什么?
我用char* memoryChunk = malloc ( 80* sizeof(char) + 1);
分配了一个内存char* memoryChunk = malloc ( 80* sizeof(char) + 1);
是什么阻止我写入超过81个单元的内存位置? 我该怎么做才能防止这种情况发生?
void testStage2(void) { char c_str1[20] = "hello"; char* ut_str1; char* ut_str2; printf("Starting stage 2 tests\n"); strcat(c_str1, " world"); printf("%s\n", c_str1); // nothing exciting, prints "hello world" ut_str1 = utstrdup("hello "); ut_str1 = utstrrealloc(ut_str1, 20); utstrcat(ut_str1, c_str1); printf("%s\n", ut_str1); // slightly more exciting, prints "hello hello world" utstrcat(ut_str1, " world"); printf("%s\n", ut_str1); // exciting, should print "hello hello world wo", 'cause there's not enough room for the second world } char* utstrcat(char* s, char* suffix){ int i = strlen(s),j; int capacity = *(s - sizeof(unsigned) - sizeof(int)); for ( j =0; suffix[j] != '\0'; j++){ if ((i+j-1) == 20) return s; s[i+j] = suffix[j]; } //strcpy(s, suffix); s[i + j] = '\0'; return s; }// append the suffix to s
是什么阻止我写入超过81个单元的内存位置?
没有。 但是,这样做会导致未定义的行为 。 这意味着任何事情都可能发生,你不应该依赖它做两次相同的事情。 99.999%的时间这是一个错误。
我该怎么做才能防止这种情况发生?
在访问(读取或写入)它们之前,请务必检查指针是否在边界内。 在传递给字符串函数时,始终确保字符串以\0
结尾。
您可以使用诸如valgrind之类的调试工具来帮助您找到与越界指针和数组访问相关的错误。
stdlib的方法
对于您的代码,您可以使用utstrncat
,它的作用类似于utstrcat
但需要最大大小(即缓冲区的大小)。
stdc ++的方法
您还可以创建数组结构/类或在C ++中使用std::string
。 例如:
typedef struct UtString { size_t buffer_size; char *buffer; } UtString;
然后让你的function对它进行操作。 您甚至可以使用此技术进行动态重新分配(但这似乎不是您想要的)。
缓冲结束标记方法
另一种方法是使缓冲区标记结束 ,类似于字符串标记的结尾 。 当您遇到标记时,不要写入该位置或其前面的位置(对于字符串标记的结尾)(或者您可以重新分配缓冲区以便有更多空间)。
例如,如果您将"hello world\0xxxxxx\1"
作为字符串(其中\0
是字符串标记的结尾, \1
是缓冲区标记的结尾, x
是随机数据)。 附加" this is fun"
将如下所示:
hello world\0xxxxxx\1 hello world \0xxxxx\1 hello world t\0xxxx\1 hello world th\0xxx\1 hello world thi\0xx\1 hello world this\0x\1 hello world this \0\1 *STOP WRITING* (next bytes are end of string then end of buffer)
你的问题
您的代码存在问题:
if ((i+j-1) == 20) return s;
虽然您在超越缓冲区之前停止,但您没有标记字符串的结尾。
您可以使用break
来提前结束for
循环,而不是返回。 这将导致for
循环后的代码运行。 这将设置字符串标记的结束并返回字符串,这是您想要的。
另外,我担心你的分配可能存在错误。 你有+ 1
来分配字符串之前的大小,对吗? 有一个问题: unsigned
通常不是1个字符; 你需要+ sizeof(unsigned)
。 我还会编写utget_buffer_size
和utset_buffer_size
以便您可以更轻松地进行更改。
没有什么能阻止你这样做。 如果你这样做,任何事情都可能发生:程序可以继续它的快乐方式,好像什么也没发生,它可能会崩溃,它可能会崩溃,它甚至可能会擦掉你的硬盘。 这是未定义行为的领域。
有许多工具试图检测或缓解这些类型的问题,但没有什么是万无一失的。 一个这样的工具是valgrind 。 valgrind监视程序的内存访问模式,并通知您这类问题。 它通过在各种虚拟机中运行程序来实现这一点,因此它会显着损害程序的性能,但它可以帮助您在正确使用时捕获大量错误。
没有什么可以阻止你超越那个界限,发生的事情取决于超出界限的东西。 黑客程序的标准黑客技巧(缓冲区溢出),不检查并确保它们不会覆盖缓冲区限制。
正如其他海报所提到的,你只需要仔细编程。 不要使用像strlen,strcpy这样的调用 – 使用像strncpy这样的长度有限的反面词。
卡尔建议使用strncpy() ,这是一个正确方向的开端。 主要思想是通过采用特定的做法养成避免缓冲区溢出的习惯。 strlcpy和strlcat涵盖了一个更为深思熟虑的库- 一致,安全,字符串复制和连接 。
会发生什么:没什么,或者你的程序会得到SIGSEGV。 你应该做什么:仔细编写你的程序。 使用像valgrind这样的工具。