Rediger

Del via


Error: stack-use-after-scope

Address Sanitizer Error: Use of out-of-scope stack memory

The use of a stack address outside the lexical scope of a variable's lifetime can happen many ways in C or C++.

Example 1 - simple nested local

// example1.cpp
// stack-use-after-scope error
int *gp;

bool b = true;

int main() {
    if (b) {
        int x[5];
        gp = x+1;
    }
    return *gp;  // Boom!
}

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 /Zi
devenv /debugexe example1.exe

Resulting error - simple nested local

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

Example 2 - lambda capture

// example2.cpp
// stack-use-after-scope error
#include <functional>

int main() {
    std::function<int()> f;
    {
        int x = 0;
        f = [&x]() {
            return x;  // Boom!
        };
    }
    return f();  // Boom!
}

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 /Zi
devenv /debugexe example2.exe

Resulting error - lambda capture

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

Example 3 - destructor ordering with locals

// example3.cpp
// stack-use-after-scope error
#include <stdio.h>

struct IntHolder {
    explicit IntHolder(int* val = 0) : val_(val) { }
    ~IntHolder() {
        printf("Value: %d\n", *val_);  // Bom!
    }
    void set(int* val) { val_ = val; }
    int* get() { return val_; }

    int* val_;
};

int main(int argc, char* argv[]) {
    // It's incorrect to use "x" inside the IntHolder destructor,
    // because the lifetime of "x" ends earlier. Per the C++ standard,
    // local lifetimes end in reverse order of declaration.
    IntHolder holder;
    int x = argc;
    holder.set(&x);
    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 example3.cpp /fsanitize=address /Zi /O1
devenv /debugexe example3.exe

Resulting error - destructor ordering

Screenshot of debugger displaying stack-use-after-scope error in example 3.

Example 4 - temporaries

// example4.cpp
// stack-use-after-scope error
#include <iostream>

struct A {
    A(const int& v) {
        p = &v;
    }
    void print() {
        std::cout << *p;
    }
    const int* p;
};

void explicit_temp() {
    A a(5);     // the temp for 5 is no longer live;
    a.print();
}

void temp_from_conversion() {
    double v = 5;
    A a(v);     // local v is no longer live.
    a.print();
}

void main() {
    explicit_temp();
    temp_from_conversion(); 
}

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

cl example4.cpp /EHsc /fsanitize=address /Zi /Od
devenv /debugexe example4.exe

ASAN is a form of dynamic analysis, which means it can only detect bad code that is actually executed. An optimizer may propagate the value of v in these cases instead of reading from the address stored in p. As a result, this example requires the /Od flag.

Resulting error - temporaries

Screenshot of debugger displaying stack-use-after-scope error in example 4.

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