簡體   English   中英

內存分配違規后free()的奇怪行為

[英]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 faultBefore free()After free() ,但經過幾次迭代之后它會崩潰,總是處於free() (打印After segmentation faultBefore 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM