[英]Strange behaviour of free() after memory allocation violation
就在不久前,我正在尋找一些我正在編寫的大型圖書館中的一個錯誤,它耗費了我很長一段時間。 問題是我違反了某些結構成員的內存界限,但是它沒有出現segmentation fault
或只是一個簡單的崩潰,而是出了意想不到的事情(至少我沒想到)。 讓我舉一個例子:
segmentation_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 fault\n");
memory[memorySize] = 0x0D; /* segmentation fault */
if (segmentationFaultFlag)
exitError("detected segmentation fault");
printf("After segmentation fault\n");
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
來確定程序崩潰的位置,但確定必須有更好的工具來執行此操作)它很難被檢測到(通常它不會在違規代碼中崩潰,但是,如示例中所示,稍后在代碼中,當嘗試再次對此內存執行某些操作時)。 當然我應該能夠釋放這個內存,因為我正確分配它並且沒有修改指針。
您只能檢測偽造環境中的違規行為。 在這種情況下,你違反了你從系統中獲得的記憶,你再也不能相信了。 因為現在發生的所有事情都是未定義的行為,你不能指望會發生什么,因為沒有任何規則。
因此,如果您想檢查程序是否存在內存泄漏或某些讀/寫違規。 你必須編寫一個程序來獲取屬於它的內存區域,並將該區域的一部分提供給“待檢查”程序。 你必須檢查過程並跟蹤它寫入的位置並讀入我們的內存,你必須使用內存的其他部分來檢查它是否允許在那里讀取寫入(即在你的偽造環境中通過設置一些標志並檢查它們是否已更改)。
因為如果程序離開你擁有的區域。 你無法確定你是否會發現這種行為。 因此,您必須制定自己的記憶管理以檢查此類行為。
當malloc返回指向一塊內存的指針時,它會使用一些有關此指針的附加信息(如分配空間的大小)。 此信息通常存儲在返回指針之前的地址上。 同樣經常,malloc可以返回指向比您要求的更大塊的指針。 因此,指針前后的地址有效。 你可以在那里寫,而不會引起分段錯誤或其他系統錯誤。 但是,如果你在那里寫,你可能會覆蓋數據malloc需要正確釋放內存。 從那時起,后續調用malloc和free的行為是不確定的。
在內存中讀取或寫入時,您不會擁有未定義的行為。
這並不總是導致分段錯誤。 在實踐中,代碼更有可能破壞其他一些數據,並且您的程序將在某些其他地方崩潰,這使得難以調試。
在此示例中,您寫入了無效的堆地址。 您可能會損壞一些內部堆結構,這可能會導致程序在任何后續malloc或free調用時崩潰。
有一些工具可以檢查您的堆使用情況,並且可以告訴您是否寫出了邊界。 我喜歡並建議使用valgrind for linux和gflags for windows。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.