不久前,我正在搜寻我正在写的大数字库中的一个错误,这使我花了很多时间。问题在于我违反了某些结构成员的内存范围,但是它不再是segmentation fault
或仅仅发生了崩溃,而是做出了意外的事情(至少我没想到)。让我介绍一个例子:
Semengeation_fault.c
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#define N 100 /* arbitrary large number */
typedef unsigned char byte;
void exitError(char *);
void segmentationFaultSignalHandler(int);
sig_atomic_t segmentationFaultFlag = 0;
int main(void)
{
int i, memorySize = 0;
byte *memory;
if (setvbuf(stdout, NULL, _IONBF, 0))
exitError("setvbuf() failed");
if (signal(SIGSEGV, segmentationFaultSignalHandler) == SIG_ERR)
exitError("signal() failed");
for (i = 0; i < N; ++i)
{
printf("Before malloc()n");
if ((memory = malloc(++memorySize * sizeof(byte))) == NULL)
exitError("allocation failed");
printf("After malloc()n");
printf("Before segmentation faultn");
memory[memorySize] = 0x0D; /* segmentation fault */
if (segmentationFaultFlag)
exitError("detected segmentation fault");
printf("After segmentation faultn");
printf("Before free()n");
free(memory);
printf("After free()n");
}
return 0;
}
void segmentationFaultSignalHandler(int signal)
{
segmentationFaultFlag = 1;
}
void exitError(char *errorMessage)
{
printf("ERROR: %s, errno=%d.n", errorMessage, errno);
exit(1);
}
我们可以看到,memory[memorySize] = 0x0D;
线显然违反了malloc()
给出的内存界限,但不会崩溃或引起信号(我知道根据ISO C99/ISO C11,信号处理已定义,并且不必定义。违反记忆范围时,根本可以提高。它在打印线After segmentation fault
,Before free()
和After free()
时移动,但是经过几次迭代后,它始终在free()
(打印After segmentation fault
和Before free()
,但不在After free()
)上崩溃。我想知道是什么原因导致这种行为,什么是检测记忆访问访问的最佳方法(我感到羞耻,但我总是使用 printf
s来确定程序崩溃的位置,但确保必须有更好的工具来做到这一点)很难检测到(大多数情况下,它不会在违规守则中崩溃,但是,如示例,在代码后期尝试再次使用此内存做某事时)。当然,我应该能够释放此内存,因为我对其进行了正确的分配并没有修改指针。
您只能在伪造的环境中检测到违规行为。在这种情况下,您违反了从系统中获得的记忆,您将不再相信任何东西。由于现在发生的一切都是不确定的行为,您不能期望会发生什么,因为没有任何规则。
因此,如果您想检查程序是否有内存leacks或一些读/写违规行为。您必须编写一个程序,该程序获取属于它的内存区域,然后将部分区域的一部分放在"待检查"程序中。您必须检查过程,并跟踪其在我们的内存中写入和阅读的位置,并且必须使用内存的另一部分进行检查是否允许在此处读取或不读取(即,通过设置在您的伪造环境中一些标志和检查它们是否已更改)。
因为如果程序离开了您拥有的区域。您无法确定自己是否会发现这种行为。因此,您必须自己进行记忆管理以检查这种行为。
在记忆中阅读或写作时,您没有任何不确定的行为。
这并不总是会导致分割故障。实际上,代码更有可能破坏其他数据,您的程序将在其他地方崩溃,这使得很难调试。
在此示例中,您写入无效的堆地址。您可能会破坏一些内部堆结构,这可能会使该程序可能会在以下任何Malloc或免费电话上崩溃。
有一些工具可以检查您的堆使用情况,并可以告诉您是否写出界限。我喜欢并且会推荐Valgrind用于Windows的Linux和Gflags。
当malloc返回指针到一块内存时,它使用有关此指针的一些其他信息(例如分配空间的大小)。此信息通常在返回指针之前就存储在地址上。同样,Malloc通常可以将指针返回到比您要求的更大的块。结果,指针在指针有效之前和之后。您可以在此写入而不会引起分割故障或其他系统错误。但是,如果您在那里写信,则可以覆盖数据malloc的需求,以正确释放内存。由于这一点,Malloc和Free的随后呼叫的行为是不确定的。