Skip to content

[ASAN] [x86] Thrown exceptions corrupt stack near catch site #54804

@amyw-msft

Description

@amyw-msft

When an exception is thrown with AddressSanitizer in use, the x86 compiler fails to take into account stack red-zones added for catch bodies when determining variable addresses. Below are five examples.

>clang --version
clang version 14.0.0
Target: i686-pc-windows-msvc
Thread model: posix
InstalledDir: C:\Program Files (x86)\LLVM\bin

> clang -m32 test.cpp -fsanitize=address

#include <stdio.h>

void one() {
    // Set variables before and after caught exception.
    // Variable set after thrown exception is corrupted.
    puts("one");
    int test = 5;

    try {
        throw 42;
    } catch (...) {
    }

    int test2 = 10;

    printf("%p : %d\n", &test, test);
    printf("%p : %d\n", &test2, test2);
    // 014FFA10 : 5
    // 014FFA30 : 7213915 <-- should be 10
}

void two() {
    puts("two");
    // Expanding amount of stack variables show that the next 16 bytes are corrupted.
    int test = 5;

    try {
        throw 42;
    } catch (...) {
    }

    int vars[5] = {10, 15, 20, 25, 30};

    printf("%p : %d\n", &test, test);
    for (int& x : vars) {
        printf("%p : %d\n", &x, x);
    }
    // 014FF9F0 : 5
    // 014FFA00 : 67108874 <-- should be 10
    // 014FFA04 : 0        <-- should be 15 (always 0)
    // 014FFA08 : 22018464 <-- should be 20
    // 014FFA0C : 0        <-- should be 25 (always 0)
    // 014FFA10 : 30
}

void three() {
    puts("three");
    // Without the first variable set prior to exception throw, only 4 bytes are corrupted.
    try {
        throw 42;
    } catch (...) {
    }

    int vars[5] = {10, 15, 20, 25, 30};

    for (int& x : vars) {
        printf("%p : %d\n", &x, x);
    }

    // 014FFA00 : 10
    // 014FFA04 : 22018464 <-- should be 15
    // 014FFA08 : 20
    // 014FFA0C : 25
    // 014FFA10 : 30
}

void four() {
    puts("four");
    // Moving all variables before thrown exception corrupt in a different pattern.

    int vars[5] = {10, 15, 20, 25, 30};

    try {
        throw 42;
    } catch (...) {
    }

    for (int& x : vars) {
        printf("%p : %d\n", &x, x);
    }
    // 014FF9F0 : 10
    // 014FF9F4 : 0        <-- should be 15 (always 0)
    // 014FF9F8 : 22018464 <-- should be 20
    // 014FF9FC : 0        <-- should be 25 (always 0)
    // 014FFA00 : 30
}

void five() {
    puts("five");
    // Before and after also corrupt in a unique pattern.

    int vars[5] = {10, 15, 20, 25, 30};

    try {
        throw 42;
    } catch (...) {
    }

    int vars2[5] = {35, 40, 45, 50, 55};

    for (int& x : vars) {
        printf("%p : %d\n", &x, x);
    }

    for (int& x : vars2) {
        printf("%p : %d\n", &x, x);
    }
    // 014FF990 : 10
    // 014FF994 : 15
    // 014FF998 : 20
    // 014FF99C : 0        <-- should be 25 (always 0)
    // 014FF9A0 : 30
    // 014FF9E0 : 35
    // 014FF9E4 : 22018432 <-- should be 40
    // 014FF9E8 : 20       <-- should be 45 (always 20)
    // 014FF9EC : 50
    // 014FF9F0 : 55
}

int main() {
    one();
    two();
    three();
    four();
    five();
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions