2014-02-20 105 views
0

我已經在C++中做了基本的memset/memcpy/strcpy實現,它可以正常工作。然而,是否有檢測緩衝區溢出的方式,如果我是做這樣的事情:C++ memset/memcpy/strcpy實現 - 檢查緩衝區溢出

實施例:

int main() 
{ 
    char *buf = (char *)calloc(10, sizeof(char)); 
    __strcpy(buf, "Hello World"); 
    // buffer size: 10, copy size: 12 (including '\0') - overflow 
} 

實現(typedef unsigned int UINT):

void *__memset(void *_Dst, int _Val, UINT _Size) 
{ 
    UINT *buf = (UINT *)_Dst; 
    while (_Size--) 
    { 
     *buf++ = (UINT)_Val; 
    } 
    return _Dst; 
} 

void *__memcpy(void *_Dst, const void *_Src, UINT _Size) 
{ 
    UINT *buf = (UINT *)_Dst; 
    UINT *src = (UINT *)_Src; 
    while (_Size--) 
    { 
     *buf++ = *src++; 
    } 
    return _Dst; 
} 

char *__strcpy(char *_Dst, const char *_Src) 
{ 
    while ((*_Dst++ = *_Src++) != '\0'); 
    return _Dst; 
} 
+2

是不是實現了strncpy呢?也爲什麼這被標記爲C++,它聞起來很像C ... – PlasmaHH

+1

__strcpy是一個保留名稱。不要在名稱中使用雙下劃線。另外,大小應該是'size_t',而不是'unsigned int'。 – MSalters

+0

@ MSalters:MSVC 2013中的'__strcpy'沒有重載(只是我的實現) – Joseph

回答

2

緩衝區溢出是不以可檢測你的程序。操作系統正在檢測它們。您只能檢查代碼中的潛在缺陷(if/else,斷言,異常)。或者你使用像valgrind這樣的分析工具。

0

檢測緩衝區溢出的更安全的方法是提供您自己的實現calloc來代替。在返回的塊之前和之後提供填充字節,將它們設置爲已知值(NOT 0或255),並在調用free時檢查它們是否未觸及。此外,撥打free後,您應覆蓋整個區塊(包括兩側的填充區)以檢查雙重free呼叫。

0

你所有的mem *函數都是無效的。他們複製(或設置)unsigned int類型的對象,而不得不復制(或設置)unsigned char類型的對象。考慮到__Size可以是一個奇數。 這些函數他們自己無法檢查緩衝區溢出而不更改它們的聲明。

而且甚至第三個功能是無效

char *__strcpy(char *_Dst, const char *_Src) 
{ 
    while ((*_Dst++ = *_Src++) != '\0'); 
    return _Dst; 
} 

函數指針內_Dst改變並指向終止零。您必須返回該函數返回的地址,而返回由_Dst指向的字符串的第一個字符的地址。

對前兩個函數也是一樣。

0

您可能需要你,由於緩衝區,可用於迭代,很多地方複製檢查緩衝區溢出如果有的話,如下圖所示的size

char *__strcpy(char *_Dst, const char *_Src, int size) 
{ 
    while ((*_Dst++ = *_Src++) != '\0' && size--); //Iterate/copy to allocated Bytes 
    return _Dst; 
} 

int main() 
{ 
    int size; 
    char *buf = (char *)calloc(size, sizeof(char)); // Here you know the size of buf 
    __strcpy(buf, "Hello World", size); // Send size as parameter 
    // buffer size: 10, copy size: 12 (including '\0') - overflow 
} 
0

有可以是真正的C++實現,如果使用數組而不是指向char的指針。您可以將您的函數定義爲模板,並將數組大小設置爲模板參數。

template < std::size_t D > 
char* strcpy(char (&dest)[ D ], const char* source) 
{ 
    assert(D > std::strlen(source)); 
    ... 
} 

由於您真的只需要目標大小,我已將源大小排除在外。

int main() 
{ 
    char buf[ 10 ]; 
    // would assert, if assertions are enabled. 
    strcpy(buf, "Hello World"); 
} 
0

您可以檢測溢出,但只有當你實現自己的內存管理例程。在我們編寫嵌入式軟件時,我們曾經這樣做過,在沒有「真正」操作系統的設備上運行嵌入式軟件,然後纔有很多好的調試工具。

這個想法是圍繞malloc()(和calloc(),你的情況)構建自己的包裝,它將分配比調用者請求多幾個字節。然後在請求的內存之前和之後設置一些「保護字節」,並用可識別的數據初始化整個緩衝區。還建立一個圍繞free()的包裝,它在釋放內存之前檢查保護字節,並在其更改後生成錯誤。

#define GUARD_LEN = 4 // Arbitrary number of guard bytes. 
#define GUARD_BYTE = 0xA5 // Arbitrary but recognizable: 10100101b 
#define UNUSED_BYTE = 0x96 // Arbitrary but recognizable: 10010110b 
#define FREED_BYTE = 0xC3 // Arbitrary but recognizable: 11000011b 
#define MAX_ALLOCS = 1024 // Max # of malloc'ed buffers. 
struct { 
    void *addr; // Address of malloc'ed buffer 
    size_t len; // Number of requested bytes 
} Allocs[MAX_ALLOCS]; 

// Allocates and initializes memory. 
void *chk_malloc(size_t length) { 
    // Allocate memory for buffer + guard bytes. 
    void *mem = malloc(length + 2*GUARD_LEN); 
    if (mem == NULL) { 
    return NULL; 
    } 

    // Initialize: [GUARD][UNUSED_BUFFER][GUARD] 
    // Caller's usable memory starts after GUARD. 
    void *buffer = mem + GUARD_LEN; 
    memset(mem, GUARD_BYTE, GUARD_LEN); 
    memset(buffer, UNUSED_BYTE, length); 
    memset(buffer + length, GUARD_BYTE, GUARD_LEN); 

    // Remember the address and length. 
    // Simplified for demonstration; you may want this to be smarter. 
    for (int i = 0; i < MAX_ALLOCS; ++i) { 
    if (Allocs[i].addr == NULL) { 
     Allocs[i].addr = buffer; 
     Allocs[i].len = length; 
     return buffer; 
    } 
    return NULL; // Should also indicate MAX_ALLOCS is too small. 
} 

// Checks that buffer is filled with val. 
bool chk_filled(void *buffer, char val, size_t len) { 
    for (int i = 0; i < len; ++i) { 
    if (buffer[i] != val) { 
     return false; 
    } 
    } 
    return true; 
} 

// Checks for over/underrun and releases memory. 
void chk_free(void *buffer) { 
    // Find the buffer in the array of alloc'ed buffers. 
    for (int i = 0; i < MAX_ALLOCS; ++i) { 
    if (Allocs[i].addr == buffer) { 
     void *guard = buffer - GUARD_LEN; // Initial guard bytes. 
     if (!chk_filled(guard, GUARD_BYTE, GUARD_LEN)) { 
     // Underrun 
     } 
     end_guard = buffer + Allocs[i].len; // Terminal guard bytes. 
     if (!chk_filled(end_guard, GUARD_BYTE, GUARD_LEN)) { 
     // Overrun 
     } 

     // Mark the buffer as free and release it. 
     memset(guard, FREED_BYTE, Allocs[i].len + 2*GUARD_LEN); 
     Allocs[i].addr = -Allocs[i].addr; // See text below. 
     free(guard); 
     return; 
    } 
    } 
    // Error: attempt to free unalloc'ed memory. 
} 

在現實中你可能希望這在幾個方面是個聰明人:

  • 你可能不希望有MAX_ALLOCS限制。
  • 檢查程序退出時未釋放的已分配內存。
  • 打印Allocs[]退出。
  • 打印更多信息和/或在檢測到錯誤時立即退出。