簡體   English   中英

是否有任何 gcc 編譯器警告可以捕獲此 memory 錯誤?

[英]Is there any gcc compiler warning which could have caught this memory bug?

我已經有一段時間沒有對 C 編程了,而且我的指針 fu 已經降級了。 我犯了一個非常基本的錯誤,今天早上我花了一個多小時才找到我做了什么。 該錯誤在此處最少復制: https://godbolt.org/z/3MdzarP67 (我知道該程序在內存管理方面是荒謬的,只是顯示發生了什么)。

realloc()的第一次調用當然會中斷,因為它給出的指針指向堆棧 memory, valgrind使這一點非常明顯。

我對自己有一個規則,只要我追蹤一個錯誤,如果有一個警告可能已經捕獲它,我就會在我的項目中啟用它。 通常情況並非如此,因為許多錯誤來自編譯器無法檢查的邏輯錯誤。

然而在這里我有點驚訝。 我們malloc()然后立即重新分配該指針,使分配的 memory 無法訪問。 很明顯,返回的指針不在該if塊的 scope 之外,並且永遠不會free() 'd。 也許期望編譯器分析調用並意識到我們正在嘗試realloc()堆棧 memory 太過分了,但令我驚訝的是,我找不到任何關於malloc()返回指針泄漏的事情對我大喊大叫。 甚至clang的 static 分析儀scan-build也沒有接受它,我嘗試了各種相關選項。

我能找到的最好的是-fsanitize=address至少在崩潰期間打印出一些線索信息,而不是:

mremap_chunk(): invalid pointer

在 Godbolt 上,或

realloc(): invalid old size
Aborted (core dumped)

在我的機器上,兩者都有些神秘(雖然是的,它們確實清楚地表明存在一些memory 問題)。 不過,這編譯沒有問題。

由於 Godbolt 鏈接不會永遠存在,這里是代碼的關鍵部分:

void add_foo_to_bar(struct Bar** b, Foo* f) {
    if ((*b)->foos == NULL) {
        (*b)->foos = (Foo*)malloc(sizeof(Foo));
        // uncomment for correction
        //(*b)->foos[(*b)->n_foos] = *f;      
        // obvious bug here, we leak memory by losing the returned pointer from malloc
        // and assign the pointer to a stack address (&f1)
        // comment below line for correction
        (*b)->foos = f; // completely wrong
        (*b)->n_foos++;
    } else {
        (*b)->foos = (Foo*)realloc((*b)->foos, ((*b)->n_foos + 1) * sizeof(Foo));
        (*b)->foos[(*b)->n_foos] = *f;
        (*b)->n_foos++;
    }
}

發生錯誤是因為 f 是指向堆棧 memory 的指針(故意),但我們顯然不能分配應該是malloc()的東西。

如果您的編譯器足夠新,請嘗試-fanalyzer 運行它時,我得到:

../main.c:30:28: warning: ‘realloc’ of ‘&f1’ which points to memory not on the heap [CWE-590] [-Wanalyzer-free-of-non-heap]
   30 |         (*b)->foos = (Foo*)realloc((*b)->foos, ((*b)->n_foos + 1) * sizeof(Foo));
      |                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  ‘main’: events 1-2
    |
    |   37 | int main() {
    |      |     ^~~~
    |      |     |
    |      |     (1) entry to ‘main’
    |......
    |   45 |     add_foo_to_bar(&b, &f1);
    |      |     ~~~~~~~~~~~~~~~~~~~~~~~
    |      |     |
    |      |     (2) calling ‘add_foo_to_bar’ from ‘main’
    |
    +--> ‘add_foo_to_bar’: events 3-5
           |
           |   19 | void add_foo_to_bar(struct Bar** b, Foo* f) {
           |      |      ^~~~~~~~~~~~~~
           |      |      |
           |      |      (3) entry to ‘add_foo_to_bar’
           |   20 |     if ((*b)->foos == NULL) {
           |      |        ~
           |      |        |
           |      |        (4) following ‘true’ branch...
           |   21 |         (*b)->foos = (Foo*)malloc(sizeof(Foo));
           |      |         ~~~~
           |      |          |
           |      |          (5) ...to here
           |
    <------+
    |
  ‘main’: events 6-7
    |
    |   45 |     add_foo_to_bar(&b, &f1);
    |      |     ^~~~~~~~~~~~~~~~~~~~~~~
    |      |     |
    |      |     (6) returning to ‘main’ from ‘add_foo_to_bar’
    |   46 |     add_foo_to_bar(&b, &f2);
    |      |     ~~~~~~~~~~~~~~~~~~~~~~~
    |      |     |
    |      |     (7) calling ‘add_foo_to_bar’ from ‘main’
    |
    +--> ‘add_foo_to_bar’: events 8-11
           |
           |   19 | void add_foo_to_bar(struct Bar** b, Foo* f) {
           |      |      ^~~~~~~~~~~~~~
           |      |      |
           |      |      (8) entry to ‘add_foo_to_bar’
           |   20 |     if ((*b)->foos == NULL) {
           |      |        ~
           |      |        |
           |      |        (9) following ‘false’ branch...
           |......
           |   30 |         (*b)->foos = (Foo*)realloc((*b)->foos, ((*b)->n_foos + 1) * sizeof(Foo));
           |      |                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           |      |                            |                     |
           |      |                            |                     (10) ...to here
           |      |                            (11) call to ‘realloc’ here
           |

不,但是,運行時測試可以拯救你。

如果您可以節省執行開銷,我已經看到許多應用程序在 memory 分配中添加了一個額外的層,以跟蹤所做的分配並發現泄漏/錯誤。 通常他們用包含FILELINE的宏替換 malloc() 和 free()

可以在這里看到一個示例(檢查 Heap.c 和 Heap.h 文件) https://github.com/eclipse/paho.mqtt.c/tree/master/src

谷歌搜索“內存堆調試器”可能會出現其他示例。 或者你可以自己滾動。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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