Bewerken

Delen via


Error: stack-use-after-return

Address Sanitizer Error: Use of stack memory after return

This check requires code generation that's activated by an extra compiler option, /fsanitize-address-use-after-return, and by setting the environment variable ASAN_OPTIONS=detect_stack_use_after_return=1.

This check can slow your application down substantially. Consider the Clang summary of the algorithm that supports use after return, and the larger performance costs.

Important

If you create an object file using the extra compiler option /fsanitize-address-use-after-return, the code generated by the compiler makes a runtime decision about how to allocate a stack frame. If the environment variable ASAN_OPTIONS isn't set to detect_stack_use_after_return, the code is still slower than using /fsanitize=address by itself. It's slower because there's still additional overhead from some stack frames that allocate space for parts of a frame by using alloca(). It's best to delete these object files when you're finished processing use-after-return errors.

Example - Simple 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);
}

To build and test this example, run these commands in a Visual Studio 2019 version 16.9 or later developer command prompt:

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

Resulting error - Simple C

Screenshot of debugger displaying stack-use-after-return error in example 1.

Example - C++ and templates

// 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;
}

To build and test this example, run these commands in a Visual Studio 2019 version 16.9 or later developer command prompt:

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 is a form of dynamic analysis, which means it can only detect bad code that is actually executed. An optimizer may determine that the value of t[100 + Idx] or sink is never used and elide the assignment. As a result, this example requires the /Od flag.

Resulting error - C++ and templates

Screenshot of debugger displaying stack-use-after-return error in example 2.

See also

AddressSanitizer overview
AddressSanitizer known issues
AddressSanitizer build and language reference
AddressSanitizer runtime reference
AddressSanitizer shadow bytes
AddressSanitizer cloud or distributed testing
AddressSanitizer debugger integration
AddressSanitizer error examples