如果我尝试访问malloc()区域以外的内存会发生什么?

时间:2009-10-31 23:58:20

标签: c memory-management malloc

我已经用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

5 个答案:

答案 0 :(得分:16)

  

是什么阻止我写入超过81个单元的内存位置?

无。但是,这样做会导致未定义的行为。这意味着任何都可能发生,你不应该依赖它做两次相同的事情。 99.999%的时间这是一个错误。

  

我该怎么做才能防止这种情况?

在访问(读取或写入)它们之前,请务必检查指针是否在范围内。在传递给字符串函数时,始终确保字符串以\0结尾。

您可以使用valgrind等调试工具来帮助您找到与越界指针和数组访问相关的错误。

stdlib的方法

对于您的代码,您可以使utstrncat的行为类似于utstrcat,但需要最大大小(即缓冲区的大小)。

stdc ++的方法

您还可以在C ++中创建数组结构/类或使用std::string。例如:

typedef struct UtString {
    size_t buffer_size;
    char *buffer;
} UtString;

然后让您的功能对其进行操作。您甚至可以使用此技术进行动态重新分配(但这似乎不是您想要的)。

缓冲区结束标记方法

另一种方法是使用结束缓冲区标记,类似于字符串标记的结尾。当你遇到标记时,不要写入那个地方或者它之前的那个地方(对于字符串标记的结尾)(或者你可以重新分配缓冲区以便有更多的空间)。

例如,如果您将"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_sizeutset_buffer_size,以便您可以更轻松地进行更改。

答案 1 :(得分:3)

没有什么能阻止你这样做。如果你这样做,任何事情都可能发生:程序可以继续它的快乐方式,好像什么也没发生,它可能会崩溃,它可能会崩溃,它甚至可能会擦掉你的硬盘。这是未定义行为的领域

有许多工具试图检测或缓解这些类型的问题,但没有什么是万无一失的。其中一个工具是valgrind。 valgrind监视程序的内存访问模式,并通知您这类问题。它通过在各种虚拟机中运行程序来实现这一点,因此它会显着损害程序的性能,但它可以帮助您在正确使用时捕获大量错误。

答案 2 :(得分:0)

没有什么能阻止你超越那个界限,发生的事情取决于超出界限的东西。黑客程序的标准黑客技巧(缓冲区溢出),不检查并确保它们不会覆盖缓冲区限制。

正如其他海报所提到的,你只需要仔细编程。不要使用像strlen,strcpy这样的调用 - 使用像strncpy等长度限制的反面词。

答案 3 :(得分:0)

Carl建议 strncpy(),这是一个正确方向的开始。主要思想是通过采用特定的做法养成避免缓冲区溢出的习惯。 strlcpy and strlcat--Consistent, Safe, String Copy and Concatenation中介绍了一个更为深思熟虑的图书馆。

答案 4 :(得分:-4)

会发生什么:没有,或者你的程序会让SIGSEGV被抛出。 你应该做什么:仔细编写你的程序。使用像valgrind这样的工具。