Please see: https://marc.info/?l=wine-devel&m=174715050805731 as well as the commit messages for more information.
-- v6: ntdll: Report more info about heap problems detected by ASan. ntdll: Show (partial) stack trace in ASan reports. ntdll: Add heap quarantine for ASan. ntdll: Add asan poisoning and redzoning to heap allocator ntdll: Implement ASan fake stack. ntdll: Don't use address of local variables as the frame address. ntdll: During unwind, also check if frame is on fake stack. ntdll: Implement __asan_{un,}poison_memory_region. ntdll: Make sure to not write into poisoned memory in KeUserModeCallback. ntdll: Implement __asan_set_shadow_*. ntdll: Implement reporting of ASan errors. ntdll: Implement __asan_{memory,region}_is_poisoned. ntdll: Implement __asan_{un,}poison_stack_memory. ntdll: Unpoison stack in __asan_handle_no_return. ntdll: Add API for checking whether address is in fake stack frame. kernel32: Check for poison in LocalLock if ASan is enabled. kernel32: Fix ASan reports in IsBad* ntdll: Call __asan_handle_no_return in RtlRestoreContext. makedep: Support sanitizer flags. asan_dynamic_thunk: Add ASan dynamic thunk for DLLs. ntdll: Add stub ASan runtime.
This merge request has too many patches to be relayed via email. Please visit the URL below to see the contents of the merge request. https://gitlab.winehq.org/wine/wine/-/merge_requests/8026
On Thu May 15 19:44:03 2025 +0000, Yuxuan Shui wrote:
updated based on suggestions from @iamahuman - now no new symbols are exported from ntdll and i was able to get rid of the pesky DLL initialization hook as well (see https://gitlab.winehq.org/wine/wine/-/commit/6616df42bcaf754ddeb40100f89bddb...). thanks!
updated the dynamic thunk to add loader stubs (placeholder wrappers that loads the real function pointers) for every ASan API, not just `__asan_stack_malloc_*`
On Fri May 16 15:28:23 2025 +0000, Jinoh Kang wrote:
Some ASAN annotations can be shared with Valgrind, so maybe we can keep them; just make them nonspecific to ASAN. For the rest, I'd generally vote for -fno-sanitize for 90% of the code. As we see more bug cases in support of ASAN, we can gradually and incrementally adopt the instrumentation.
I sympathise with the worry about long term maintainability. I will say I am willing to take on this task and resolve any ASan related problems that might come up in the future. I don't know how much weight you will put on my words, but there's that.
we should be careful not to affect compatibility (maybe achieavable)
If I did everything correctly, my changes should compile to nothing if ASan isn't enabled.
* * *
Then I want to defend the changes a little bit. Some of the changes are arguably not really ASan specific:
RtlRestoreContext (that is probably not exhaustive, we have more no return places like that?)
I inserted a call to `__asan_handle_no_return`, but the same can be achieved by correctly annotating no return functions with `DECLSPEC_NORETURN`.
KeUserModeCallback (while maybe `__builtin_frame_address` is not portable)
using the address of a local variable as the value of the stack pointer works, but I would argue using compiler intrinsic is the more proper way. clang/gcc both support `__builtin_frame_address`, on the MSVC side there's `_AddressOfReturnAddress`.
unwinding
i am not very knowledgeable on this, but wouldn't it be better to always use native unwinding mechanism rather than wine's hack? i don't know if this is doable, but if we can make `__TRY/__EXCEPT` expand to SEH directives for PE and dwarf directives for ELF, then get rid of `__WINE_FRAME`, then we don't need ASan specific workarounds.
* * *
but some workarounds are ASan specific:
kernelbase (probably similar should be in virtual_check_buffer_for_write and friends in ntdll unless it is already there), kernel32
yes, but these changes pretty clean and shouldn't be too difficult to maintain.
I sympathise with the worry about long term maintainability. I will say I am willing to take on this task and resolve any ASan related problems that might come up in the future. I don't know how much weight you will put on my words, but there's that.
Sure, it's great that you are willing to do that. But note that it's actually an argument for keeping it as a separate fork that you have the responsibility to maintain, as opposed to merging it upstream where it becomes everybody's responsibility when making changes to affected areas. Considering that you have to rebuild the whole tree anyway, it doesn't seem too onerous to tell people "to do an ASAN test run, apply this patch and rebuild".
i am not very knowledgeable on this, but wouldn't it be better to always use native unwinding mechanism rather than wine's hack? i don't know if this is doable, but if we can make __TRY/__EXCEPT expand to SEH directives for PE and dwarf directives for ELF, then get rid of __WINE_FRAME, then we don't need ASan specific workarounds.
Sadly that's not feasible without compiler support.
But note that it's actually an argument for keeping it as a separate fork that you have the responsibility to maintain.
That doesn't sound too bad actually. This is intended for developers anyway, so as long as devs are made aware and have easy access to this, it can be useful. maybe things will change in the future and this eventually get merged, but for now keeping it as a fork doesn't sound too bad.
Sadly that's not feasible without compiler support.
can you expand a bit on this? i am curious and do want to learn more.
i know mingw has their crappy `__try1/__except1` macros, that doesn't quite work like the real `__try/__except`. is there something that prevents us from improving upon them?
On Fri May 16 20:26:42 2025 +0000, Yuxuan Shui wrote:
But note that it's actually an argument for keeping it as a separate
fork that you have the responsibility to maintain. That doesn't sound too bad actually. This is intended for developers anyway, so as long as devs are made aware and have easy access to this, it can be useful. maybe things will change in the future and this eventually get merged, but for now keeping it as a fork doesn't sound too bad.
Sadly that's not feasible without compiler support.
can you expand a bit on this? i am curious and do want to learn more. i know mingw has their crappy `__try1/__except1` macros, that doesn't quite work like the real `__try/__except`. is there something that prevents us from improving upon them?
also did a quick & crude survey on this, based on bernhardu's work, out of ~3.6M total lines of code, almost exactly 3M can be covered by PE ASan, so about 83.5%. out of those lines that can't be covered, 35.2% are unixlibs, the rest are DLLs that have to be exempted, presumably because they caused problems. (i excluded `libs`, and `tools`. comments and empty lines are not counted. obviously SLOC doesn't mean much, so this is more or less just for fun.).
i know mingw has their crappy `__try1/__except1` macros, that doesn't quite work like the real `__try/__except`. is there something that prevents us from improving upon them?
Doing proper exception handling implies generating correct code range tables, and correct handler and unwinding code sequences, all of which depend on the code generated by the compiler. There's just no way to do that by injecting random asm statements inside the C code. The only way to do SEH without compiler support is by writing entire functions in assembly, which we've done in a few places for simple things, but obviously that doesn't scale.
On Fri May 16 20:43:23 2025 +0000, Alexandre Julliard wrote:
i know mingw has their crappy `__try1/__except1` macros, that doesn't
quite work like the real `__try/__except`. is there something that prevents us from improving upon them? Doing proper exception handling implies generating correct code range tables, and correct handler and unwinding code sequences, all of which depend on the code generated by the compiler. There's just no way to do that by injecting random asm statements inside the C code. The only way to do SEH without compiler support is by writing entire functions in assembly, which we've done in a few places for simple things, but obviously that doesn't scale.
I'm sure others know more, but my understanding is that GCC/mingw doesn't support Windows SEH, hence the need for the mingw macros and Wine's own support. Clang does support SEH, although it doesn't look like Wine uses it (`__TRY` always uses `WINE_FRAME` except on MSVC). If Wine's macros used real SEH on Clang, an ASan build could just require Clang be used for PE cross-compiling and you could ignore `WINE_FRAME`.
On Fri May 16 20:53:16 2025 +0000, Brendan Shanks wrote:
I'm sure others know more, but my understanding is that GCC/mingw doesn't support Windows SEH, hence the need for the mingw macros and Wine's own support. Clang does support SEH, although it doesn't look like Wine uses it (`__TRY` always uses `WINE_FRAME` except on MSVC). If Wine's macros used real SEH on Clang, an ASan build could just require Clang be used for PE cross-compiling and you could ignore `WINE_FRAME`.
I think GCC does support SEH? i.e. it will happily use it for C++ exceptions. (I contemplated suggesting using C++ but only for exception handling). It also generates enough SEH directives for functions so they can be unwound (urgh, i don't know the terminologies for these). That's why mingw's `__try1/__except1` work (to an extent).
And yes, clang does support `__try/__except`, but for windows targets only. wine also uses them on the unix side.
On Fri May 16 21:05:07 2025 +0000, Yuxuan Shui wrote:
I think GCC does support SEH? i.e. it will happily use it for C++ exceptions. (I contemplated suggesting using C++ but only for exception handling). It also generates enough SEH directives for functions so they can be unwound (urgh, i don't know the terminologies for these). That's why mingw's `__try1/__except1` work (to an extent). And yes, clang does support `__try/__except`, but for windows targets only. wine also uses them on the unix side.
Yes, gcc only supports them in C++ mode, and clang only in MSVC mode. That's not good enough in general, but it may be usable for your purposes.