Udostępnij za pośrednictwem


Błąd: stack-use-after-return

Błąd narzędzia do oczyszczania adresu: użycie pamięci stosu po powrocie

Ta kontrola wymaga generowania kodu aktywowanego przez dodatkową opcję kompilatora, /fsanitize-address-use-after-returni przez ustawienie zmiennej środowiskowej ASAN_OPTIONS=detect_stack_use_after_return=1.

Ta kontrola może znacznie spowolnić działanie aplikacji. Rozważ podsumowanie języka Clang algorytmu, który obsługuje użycie po powrocie, oraz większe koszty wydajności.

Ważne

Jeśli tworzysz plik obiektu przy użyciu dodatkowej opcji /fsanitize-address-use-after-returnkompilatora , kod wygenerowany przez kompilator podejmuje decyzję w czasie wykonywania o tym, jak przydzielić ramkę stosu. Jeśli zmienna środowiskowa ASAN_OPTIONS nie jest ustawiona na detect_stack_use_after_returnwartość , kod jest nadal wolniejszy niż sam w sobie /fsanitize=address . Jest wolniejsza, ponieważ nadal istnieje dodatkowe obciążenie z niektórych ramek stosu, które przydzielają miejsce dla części ramki przy użyciu polecenia alloca(). Najlepiej usunąć te pliki obiektów po zakończeniu przetwarzania błędów użycia po powrocie.

Przykład — prosty C

// example1.cpp
// stack-use-after-return error
volatile char* x;

void foo() {
    char stack_buffer[42];
    x = &stack_buffer[13];
}

int main() {

    foo();
    *x = 42; // Boom!

    return (*x == 42);
}

Aby skompilować i przetestować ten przykład, uruchom następujące polecenia w wierszu polecenia programu Visual Studio 2019 w wersji 16.9 lub nowszej:

cl example1.cpp /fsanitize=address /fsanitize-address-use-after-return /Zi
set ASAN_OPTIONS=detect_stack_use_after_return=1
devenv /debugexe example1.exe

Błąd wynikowy — prosty C

Zrzut ekranu przedstawiający debuger wyświetlający błąd stack-use-after-return w przykładzie 1.

Przykład — C++ i szablony

// example2.cpp
// stack-use-after-return error
#include <stdlib.h>

enum ReadOrWrite { Read = 0, Write = 1 };

struct S32 {
    char x[32];
};

template<class T>
T* LeakStack() {
    T t[100];
    static volatile T* x;
    x = &t[0];
    return (T*)x;
}

template<class T>
void StackUseAfterReturn(int Idx, ReadOrWrite w) {
    static T sink;
    T* t = LeakStack<T>();
    if (w)
        t[100 + Idx] = T();
    else
        sink = t[100 + Idx];
}

int main(int argc, char* argv[]) {

    if (argc != 2) return 1;
    int kind = atoi(argv[1]);

    switch (kind) {
    case 1: StackUseAfterReturn<char>(0, Read); break;
    case 2: StackUseAfterReturn<S32>(0, Write); break;
    }
    return 0;
}

Aby skompilować i przetestować ten przykład, uruchom następujące polecenia w wierszu polecenia programu Visual Studio 2019 w wersji 16.9 lub nowszej:

cl example2.cpp /fsanitize=address /fsanitize-address-use-after-return /Zi /Od
set ASAN_OPTIONS=detect_stack_use_after_return=1
devenv /debugexe example2.exe 1

ASAN jest formą analizy dynamicznej, co oznacza, że może wykrywać tylko zły kod, który jest rzeczywiście wykonywany. Optymalizator może określić, że wartość t[100 + Idx] lub sink nigdy nie jest używana i wychwytuje przypisanie. W związku z tym ten przykład wymaga flagi /Od .

Błąd wynikowy — C++ i szablony

Zrzut ekranu przedstawiający debuger wyświetlający błąd stack-use-after-return w przykładzie 2.

Zobacz też

AddressSanitizer — omówienie
Rozwiązywanie znanych problemów z programemSanitizer
Dokumentacja języka i kompilacji narzędzia AddressSanitizer
AddressSanitizer runtime reference (Dokumentacja środowiska uruchomieniowego AddressSanitizer)
Bajty w tle addressSanitizer
AddressSanitizer — chmura lub testowanie rozproszone
Integracja debugera AddressSanitizer
Przykłady błędów addressSanitizer