簡體   English   中英

檢測對超出范圍的變量的訪問

[英]Detecting access to out-of-scope variables

像這樣的代碼是未定義的行為,因為它訪問不再在范圍內的本地變量(其生命周期已經結束)。

int main() {
    int *a;
    {
        int b = 42;
        a = &b;
    }
    printf("%d", *a); // UB!
    return 0;
}

我的問題:有沒有很好的技術可以自動檢測這樣的錯誤? 它似乎應該是可檢測的(當變量超出范圍時將堆棧空間的部分標記為不可用,然后在訪問該空間時抱怨),但Valgrind 3.10,Clang 4的AddressSanitizer和UndefinedBehaviorSanitizer,以及GCC 6的AddressSanitizer和UndefinedBehaviorSanitizer都不要不要抱怨

如果沒有特殊的編譯器支持,非侵入式內存調試器(如Valgrind)可以檢測對超出范圍的堆棧幀的訪問,但不能檢測到函數內的范圍。 這是因為編譯器(通常) 在一次通過 *中為堆棧幀分配所有內存 因此,為了檢測對同一函數中范圍外變量的訪問,我們需要特定的編譯器工具來“毒化”超出范圍但其封閉框架仍然有效的變量。

最新版本的clang和gcc中提供的ubsan AddressSanitizer使用的技術是用訪問特殊分配的內存替換堆棧訪問

為了實現堆棧內存的隔離,我們需要將堆棧升級到堆。 [...] __asan_stack_malloc(real_stack, frame_size)從線程局部堆狀結構(偽堆棧)中分配偽幀(frame_size字節)。 每個假框架都是未經處理的,然后紅色區域在已檢測的功能代碼中被中毒。 __asan_stack_free(fake_stack, real_stack, frame_size)中毒整個假幀並解除分配。

使用和輸出示例:

$ g++ -std=c++11 a.cpp -fsanitize=address && env ASAN_OPTIONS='detect_stack_use_after_return=1' ./a.out 
ERROR: AddressSanitizer: stack-use-after-scope on address 0x7fd0e8300020 at pc 0x000000400c1b bp 0x7fff5b45ecf0 sp 0x7fff5b45ece8
READ of size 4 at 0x7fd0e8300020 thread T0
    #0 0x400c1a in main (a.out+0x400c1a)
    #1 0x7fd0ebe18d5c in __libc_start_main (/lib64/libc.so.6+0x1ed5c)
    #2 0x400a48  (a.out+0x400a48)

Address 0x7fd0e8300020 is located in stack of thread T0 at offset 32 in frame
    #0 0x400b26 in main (a.out+0x400b26)

  This frame has 1 object(s):
    [32, 36) 'b' <== Memory access at offset 32 is inside this variable

請注意,因為它很昂貴,所以必須在編譯時( -fsanitize=address )和運行時( ASAN_OPTIONS='detect_stack_use_after_return=1'ASAN_OPTIONS='detect_stack_use_after_return=1' 關於最低版本; 它適用於gcc 7.1.0和clang trunk,但顯然不是任何已發布的clang版本,所以如果你想使用已發布的編譯器,你必須使用gcc。


*考慮到這兩個函數編譯(例如通過gcc at -O0 )到相同的機器代碼,因此非侵入式內存調試器無法告訴它們之間的區別:

int f() {
    int* a;
    {
        int b = 42;
        a = &b;
    }
    return *a;
}

int g() {
    int* a;
    int b = 42;
    a = &b;
    return *a;
}

**嚴格地說,如果調試符號可用,調試器可以跟蹤進出范圍的變量。 但通常如果您有可用的調試符號,則您擁有源代碼,因此可以使用檢測重新編譯該程序。

是。 Lint就是為此而設計的。 我們在嵌入式系統和汽車系統中經常使用它。 您可以使用在線演示來測試它對您的效果如何。 在您的具體案例中,其規則MISRA:2012:18.6.

樣品運行


FlexeLint for C/C++ (Unix) Vers. 9.00L, Copyright Gimpel Software 1985-2014
--- Module: misra3.c (C)
        _
     1  int main() {
misra3.c  1  Note 970:  Use of modifier or type 'int' outside of a typedef [MISRA 2012 Directive 4.6, advisory]
misra3.c  1  Note 9075:  external symbol 'main(void)' defined without a prior declaration [MISRA 2012 Rule 8.4, required]
            _
     2      int *a;
misra3.c  2  Note 970:  Use of modifier or type 'int' outside of a typedef [MISRA 2012 Directive 4.6, advisory]
     3      {
                _
     4          int b = 42;
misra3.c  4  Note 970:  Use of modifier or type 'int' outside of a typedef [MISRA 2012 Directive 4.6, advisory]
                      _
     5          a = &b;
misra3.c  5  Info 733:  Assigning address of auto variable 'b' to outer scope symbol 'a' [MISRA 2012 Rule 18.6, required]
     6      }
            _
     7      printf("%d", *a); // UB!
misra3.c  7  Info 718:  Symbol 'printf' undeclared, assumed to return int [MISRA 2012 Rule 17.3, mandatory]
misra3.c  7  Warning 586:  function 'printf' is deprecated. [MISRA 2012 Rule 21.6, required]
misra3.c  7  Info 746:  call to function 'printf()' not made in the presence of a prototype
     8      return 0;
                    _
     9  }

misra3.c  9  Info 783:  Line does not end with new-line
misra3.c  9  Note 954:  Pointer variable 'a' (line 2) could be declared as pointing to const [MISRA 2012 Rule 8.13, advisory]

/// Start of Pass 2 ///

--- Module: misra3.c (C)
     1  int main() {
     2      int *a;
     3      {
     4          int b = 42;
     5          a = &b;
     6      }
     7      printf("%d", *a); // UB!
     8      return 0;
     9  }

--- Global Wrap-up

Warning 526:  Symbol 'printf()' (line 7, file misra3.c) not defined
Warning 628:  no argument information provided for function 'printf()' (line 7, file misra3.c)

暫無
暫無

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

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