Compartilhar via


Erro: stack-use-after-return

Erro na limpeza de endereço: uso da memória da pilha após o retorno

Essa verificação requer a geração de código ativada por uma opção do compilador extra, /fsanitize-address-use-after-return, e configurando a variável de ambiente ASAN_OPTIONS=detect_stack_use_after_return=1.

Essa verificação pode reduzir substancialmente o aplicativo. Considere o resumo de Clang do algoritmo que dá suporte ao uso após o retorno e os custos de desempenho maiores.

Importante

Se você criar um arquivo de objeto usando a opção /fsanitize-address-use-after-return do compilador extra, o código gerado pelo compilador tomará uma decisão de runtime sobre como alocar um registro de ativação. Se a variável de ambiente ASAN_OPTIONS não estiver definida como detect_stack_use_after_return, o código ainda será mais lento do que o uso de /fsanitize=address por si só. É mais lento porque ainda há sobrecarga adicional de alguns registros de ativação que alocam espaço para partes de um quadro usando alloca(). É melhor excluir esses arquivos-objeto quando você terminar de processar erros de uso após o retorno.

Exemplo – C simples

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

Para compilar e testar esse exemplo, execute estes comandos em um prompt de comando do desenvolvedor do Visual Studio 2019 versão 16.9 ou posterior:

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

Erro resultante – C simples

Captura de tela do depurador exibindo o erro stack-use-after-return no exemplo 1.

Exemplo – C++ e modelos

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

Para compilar e testar esse exemplo, execute estes comandos em um prompt de comando do desenvolvedor do Visual Studio 2019 versão 16.9 ou posterior:

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

O ASAN é uma forma de análise dinâmica, o que significa que ele só pode detectar código ruim que é realmente executado. Um otimizador pode determinar que o valor de t[100 + Idx] ou sink nunca é usado e elidir a atribuição. Como resultado, este exemplo requer o /Od sinalizador.

Erro resultante – C++ e modelos

Captura de tela do depurador exibindo o erro stack-use-after-return no exemplo 2.

Confira também

Visão geral do AddressSanitizer
Problemas conhecidos do AddressSanitizer
Referência de linguagem e build do AddressSanitizer
Referência de runtime do AddressSanitizer
Bytes de sombra de AddressSanitizer
Nuvem do AddressSanitizer ou teste distribuído
Integração do depurador do AddressSanitizer
Exemplos de erro do AddressSanitizer