google / sanitizers

AddressSanitizer, ThreadSanitizer, MemorySanitizer
Other
11.43k stars 1.03k forks source link

Asan doesn't work with exceptions on Windows #749

Open adamf88 opened 7 years ago

adamf88 commented 7 years ago

If I run this code with -fsanitize=address then I get an error message like below. What is the status of exception support on Windows by address sanitizer ?

#include <cstdio>
#include <exception>
int main()
{
    try {
        throw std::exception("test");
    }catch (const std::exception& ex){
        puts(ex.what());
    }
    return 0;
}

================================================================= ==6620==ERROR: AddressSanitizer: access-violation on unknown address 0x1024448f (pc 0x00061285 bp 0x003aff1c sp 0x003af620 T0) ==6620==The signal is caused by a READ memory access.

0 0x61284 in main D:\Private\asan_exception\AsanExcpetion\AsanExcpetion\Source.cpp:9

1 0x616eb in __scrt_common_main_seh f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl:253

2 0x76b33369 in BaseThreadInitThunk+0x11 (C:\Windows\syswow64\kernel32.dll+0x7dd73369)

3 0x77539901 in RtlInitializeExceptionChain+0x62 (C:\Windows\SysWOW64\ntdll.dll+0x7dea9901)

4 0x775398d4 in RtlInitializeExceptionChain+0x35 (C:\Windows\SysWOW64\ntdll.dll+0x7dea98d4)

AddressSanitizer can not provide additional info. SUMMARY: AddressSanitizer: access-violation D:\Private\asan_exception\AsanExcpetion\AsanExcpetion\Source.cpp:9 in ma in ==6620==ABORTING

kcc commented 7 years ago

@rnk

rnk commented 7 years ago

A simpler example:

extern "C" int printf(const char *, ...);
int main() {
  try {
    throw 1;
  } catch (int e) {
    printf("caught %d\n", e);
  }
  return 0;
}

With ASan this prints "caught 1561575566", and "caught 1" without. I think ASan instrumentation is interfering with catch object stack objects. It's attempting to instrument them, when they must appear at some fixed offset from the stack pointer. In your example, this interference results in a null dereference while evaluating ex.what().

rnk commented 7 years ago

Also, ASan instrumentation isn't inserting calls into EH pads with the right funclet token bundle, so any attempts to do ASan checks inside funclets will be removed by WinEHPrepare. This will require significant work to fix.

morehouse commented 6 years ago

@rnk Are exceptions supported on Windows?

rnk commented 6 years ago

No, the comment about EH pads and tokens is still relevant. It is more likely that we will change the way windows EH funclets work to make it so that ASan's instrumentation is more naturally compatible with Windows EH than that we will fix ASan to deal with the current system.

jrozansk commented 5 years ago

Is there any workaround for this problem? @rnk ?

Would try except __throw usage help here?

rnk commented 5 years ago

Is there any workaround for this problem?

Well, the workaround would be to apply __attribute__((no_sanitize_address)) to every function that uses EH, and if your application doesn't use EH in normal operation, things will work.

Would try except __throw usage help here?

What did you have in mind? Functions that use SEH (try / except) already have asan disabled for other reasons, so I don't think it would help.

0dminnimda commented 1 year ago

Damn, I've spent a whole day trying to understand what's failing in my code and I finally got here. I think there should be at least some compiler warning about this.

0dminnimda commented 1 year ago

Well, the workaround would be to apply __attribute__((no_sanitize_address)) to every function that uses EH, and if your application doesn't use EH in normal operation, things will work.

It doesn't sound very nice to not sanitize the whole functions, so a slightly better workaround is to wrap the try/catch in a lambda and execute it immediately.


#include <iostream>
#include <exception>

int main() {
    []() __attribute__((no_sanitize_address)) {
        try {
            throw std::runtime_error("test");
        } catch (const std::runtime_error &ex) {
            std::cout << ex.what() << std::endl;
        }
    }();

    std::cout << "end" << std::endl;
    return 0;
}

Unfortunately, handling the exception in a separate function doesn't really help, as it only reduces the length of the ASan message.

If this

#include <iostream>
#include <exception>

int main() {
    try {
        throw std::runtime_error("test");
    } catch (const std::runtime_error &ex) {
        std::cout << ex.what() << std::endl;
    }

    std::cout << "end" << std::endl;
    return 0;
}

results in

asan.cpp:8:22: runtime error: upcast of misaligned address 0x00000000000e for type 'std::runtime_error', which requires 8 byte alignment
0x00000000000e: note: pointer points here
<memory cannot be printed>
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior asan.cpp:8:22 in
asan.cpp:8:25: runtime error: member call on misaligned address 0x00000000000e for type 'std::exception', which requires 8 byte alignment
0x00000000000e: note: pointer points here
<memory cannot be printed>
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior asan.cpp:8:25 in
=================================================================
==4384==ERROR: AddressSanitizer: access-violation on unknown address 0x00000000000e (pc 0x7ff74e3d12ca bp 0x008e4edcfaf0 sp 0x008e4edcd7f0 T0)
==4384==The signal is caused by a READ memory access.
==4384==Hint: address points to the zero page.
    #0 0x7ff74e3d12c9 in main C:\projects\test\asan.cpp:8
    #1 0x7ff74e478ccf in _CallSettingFrame d:\a01\_work\6\s\src\vctools\crt\vcruntime\src\eh\amd64\handlers.asm:49
    #2 0x7ff74e46e6bb in __FrameHandler3::CxxCallCatchBlock(struct _EXCEPTION_RECORD *) d:\a01\_work\6\s\src\vctools\crt\vcruntime\src\eh\frame.cpp:1521
    #3 0x7fffdfbb1715  (C:\WINDOWS\SYSTEM32\ntdll.dll+0x1800a1715)
    #4 0x7ff74e3d113b in main C:\projects\test\asan.cpp:6
    #5 0x7ff74e435d6b in invoke_main d:\a01\_work\6\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:78
    #6 0x7ff74e435d6b in __scrt_common_main_seh d:\a01\_work\6\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
    #7 0x7fffdf757613  (C:\WINDOWS\System32\KERNEL32.DLL+0x180017613)
    #8 0x7fffdfb626b0  (C:\WINDOWS\SYSTEM32\ntdll.dll+0x1800526b0)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: access-violation C:\projects\test\asan.cpp:8 in main
==4384==ABORTING

This

#include <iostream>
#include <exception>

__attribute__((no_sanitize_address)) void handle(const std::runtime_error &ex) noexcept {
    std::cout << ex.what() << std::endl;
}

int main() {
    try {
        throw std::runtime_error("test");
    } catch (const std::runtime_error &ex) {
        handle(ex);
    }

    std::cout << "end" << std::endl;
    return 0;
}

results in

asan.cpp:12:16: runtime error: reference binding to misaligned address 0x00000000000e for type 'const std::runtime_error', which requires 8 byte alignment
0x00000000000e: note: pointer points here
<memory cannot be printed>
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior asan.cpp:12:16 in
asan.cpp:5:18: runtime error: upcast of misaligned address 0x00000000000e for type 'std::runtime_error', which requires 8 byte alignment
0x00000000000e: note: pointer points here
<memory cannot be printed>
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior asan.cpp:5:18 in
asan.cpp:5:21: runtime error: member call on misaligned address 0x00000000000e for type 'std::exception', which requires 8 byte alignment
0x00000000000e: note: pointer points here
<memory cannot be printed>
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior asan.cpp:5:21 in
=================================================================
==33904==ERROR: AddressSanitizer: access-violation on unknown address 0x00000000000e (pc 0x7ff70b881070 bp 0x00c27556da20 sp 0x00c27556d9d0 T0)
==33904==The signal is caused by a READ memory access.
==33904==Hint: address points to the zero page.
    #0 0x7ff70b88106f in handle(class std::runtime_error const &) C:\projects\test\asan.cpp:5
    #1 0x7ff70b88138c in main C:\projects\test\asan.cpp:12
    #2 0xc27556faff  (<unknown module>)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: access-violation C:\projects\test\asan.cpp:5 in handle(class std::runtime_error const &)
==33904==ABORTING
0dminnimda commented 1 year ago

And with more experementation we can see a wide array of interesting error messages

For code with handle():

using std::runtime_error &ex will result in

asan.cpp:12:16: runtime error: reference binding to misaligned address 0x7ff6d7a794b4 for type 'std::runtime_error', which requires 8 byte alignment
0x7ff6d7a794b4: note: pointer points here
  20 5b c3 cc 48 83 ec 28  48 8b 0d 69 87 9c 00 48  85 c9 74 29 48 8b 01 48  8b 40 10 ff 15 7f 0f 00
              ^
...
==4292==ERROR: AddressSanitizer: access-violation on unknown address 0xffffffffffffffff (pc 0x7ff6d7981073 bp 0x0091e7efd500 sp 0x0091e7efd4b0 T0)
==4292==The signal is caused by a READ memory access.
...

std::runtime_error ex will not trigger ASan, but will print Unknown exception While std::exception ex will print gibberish Л╟HЛ\$0HГ─ _├╠HЙ\LЙL$ WHГь IЛ┘IЛ°Л const std::exception ex will print Unknown exception again

For custom exception with handle():


class CustomException : public std::exception {
    const char *message;

public:
    CustomException(const char *msg) : message(msg) {}
    const char *what() const noexcept override { return message; }
};

const CustomException &ex results in

asan.cpp:20:16: runtime error: reference binding to misaligned address 0x7ff646c59894 for type 'const CustomException', which requires 8 byte alignment
0x7ff646c59894: note: pointer points here
  20 5b c3 cc 48 83 ec 28  48 8b 0d 89 96 9c 00 48  85 c9 74 29 48 8b 01 48  8b 40 10 ff 15 9f 0b 00
...
the rest is roughly the same

CustomException &ex results in

asan.cpp:20:16: runtime error: reference binding to misaligned address 0x00000000000e for type 'CustomException', which requires 8 byte alignment
0x00000000000e: note: pointer points here
<memory cannot be printed>
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior asan.cpp:20:16 in

CustomException ex and const CustomException ex results in no erros and gibberish @ўS∙3 and Л╟HЛ\$0HГ─ _├╠HЙ\LЙL$ WHГь IЛ┘IЛ°Л ш╝N

Gibberish does not change between runs and change only between compilations


And those cases compile and run perfectly fine without -fsanitize=address,undefined, so it doesn't just report false positives, it can negatively change the behavior in this case

0dminnimda commented 1 year ago

Also I check on several of the cases, fsanitize=undefined alone doesn't seem to change behavior / report problems

0dminnimda commented 1 year ago

I have finalized a ~solution~ workaround and posted it on SO, hours of pain should not go to waste :P

rnk commented 1 year ago

I can't speak to our help with the detailed problems you are having, but we can definitely improve the way this incompatibility is handled.

@smeenai , didn't we have some solution to this problem for other instrumentation tools? Alternatively, we can power-off ASan for functions using EH automatically, it's easy to detect a personality function and the presence of catchpad/cleanuppad.

smeenai commented 1 year ago

There's https://reviews.llvm.org/D143108, but it seems to have slipped through the cracks on everyone's ends. Would that help here?

rnk commented 1 year ago

Nice, that does look like a proper fix.

sylvain-audi commented 1 year ago

I'm sure the patch may help, but it seems to only be a part o the problem (trying the repro above with the patch still fails). I created #64990 for the issue with funclets, that the review mentioned above addresses.

sylvain-audi commented 7 months ago

There's https://reviews.llvm.org/D143108, but it seems to have slipped through the cracks on everyone's ends. Would that help here?

That review was redone as a PR : https://github.com/llvm/llvm-project/pull/82533 and was merged today.

It fixes one part of this issue: now Asan on Windows target should at least support the case of catch() without parameter. However catch(xxx) is still broken.