Ошибка: stack-use-after-return

Ошибка санитизатора адресов: использование памяти стека после возврата

Для этого проверка требуется создание кода, активируемое дополнительным параметром компилятора, /fsanitize-address-use-after-returnи путем задания переменной ASAN_OPTIONS=detect_stack_use_after_return=1среды.

Это проверка может существенно замедлить работу приложения. Рассмотрим сводку по алгоритму Clang, который поддерживает использование после возврата, и более крупные затраты на производительность.

Внимание

При создании файла объекта с помощью дополнительного параметра /fsanitize-address-use-after-returnкомпилятора код, созданный компилятором, принимает решение о том, как выделить кадр стека. Если переменная ASAN_OPTIONS среды не задана detect_stack_use_after_return, код по-прежнему медленнее, чем сам по /fsanitize=address себе. Это медленнее, так как некоторые кадры стека по-прежнему имеют дополнительные затраты, которые выделяют пространство для частей кадра с помощью alloca(). Рекомендуется удалить эти файлы объектов после завершения обработки ошибок использования после возврата.

Пример— простой 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);
}

Чтобы создать и проверить этот пример, выполните следующие команды в командной строке разработчика Visual Studio 2019 версии 16.9 или более поздней:

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

Результирующая ошибка — Simple C

Снимок экрана: отладчик, отображающий ошибку стек-use-after-return в примере 1.

Пример: C++ и шаблоны

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

Чтобы создать и проверить этот пример, выполните следующие команды в командной строке разработчика Visual Studio 2019 версии 16.9 или более поздней:

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 — это форма динамического анализа, что означает, что он может обнаруживать только плохой код, который фактически выполняется. Оптимизатор может определить, что значение t[100 + Idx] или sink никогда не используется и не скользит по назначению. В результате для этого примера требуется /Od флаг.

Результирующая ошибка — C++ и шаблоны

Снимок экрана: отладчик, отображающий ошибку

См. также

Обзор AddressSanitizer
Известные проблемы AddressSanitizer
Справочник по сборке и языку AddressSanitizer
Справочник по среде выполнения AddressSanitizer
Теневой байт AddressSanitizer
Облачное или распределенное тестирование AddressSanitizer
Интеграция отладчика AddressSanitizer
Примеры ошибок AddressSanitizer