Skip to content

Fix ASan stack-use-after-scope on MSVC/Windows#157

Open
AlexK0 wants to merge 1 commit intogoogle:mainfrom
AlexK0:fix-asan-error
Open

Fix ASan stack-use-after-scope on MSVC/Windows#157
AlexK0 wants to merge 1 commit intogoogle:mainfrom
AlexK0:fix-asan-error

Conversation

@AlexK0
Copy link
Copy Markdown

@AlexK0 AlexK0 commented Mar 19, 2026

Hi everyone! 👋

This change fixes an ASan-detected stack-use-after-scope in DAP_IMPLEMENT_STRUCT_FIELD_SERIALIZATION on MSVC/Windows.

Problem:

DAP_IMPLEMENT_STRUCT_FIELD_SERIALIZATION() currently iterates over:
for (auto& field : std::initializer_list<Field>{__VA_ARGS__}) { ... }
and passes lambdas into fd->field(...) / fs->field(...) from inside that loop.

Under address sanitizer, this produces a reproducible stack-use-after-scope during serialization of DAP events such as dap::OutputEvent. A minimal downstream repro is a DAP adapter that sends an OutputEvent during startup.

Why this happens:

The macro builds a temporary std::initializer_list<Field> and iterates over it while creating deferred callbacks that capture local variables by reference.

Let me know if you have any questions. Thanks for reviewing!

ASan log:

1: stderr==================================================================
1: ==13104==ERROR: AddressSanitizer: stack-use-after-scope on address 0x00d1c55fbd10 at pc 0x7ff74187b86f bp 0x00d1c55fb780 sp 0x00d1c55fb788
1: READ of size 8 at 0x00d1c55fbd10 thread T1
1:     #0 0x7ff74187b86e in std::initializer_list<struct dap::Field>::begin(void) const C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.44.35207\include\initializer_list:37
1:     #1 0x7ff741cca0d7 in dap::TypeOf<struct dap::OutputEvent>::serializeFields(class dap::FieldSerializer *, void const *) C:\Projects\DEDA\cmake-build-debug-asan\_deps\cppdap-src\src\protocol_events.cpp:67
1:     #2 0x7ff741cde6a4 in <lambda_3c331b09e81c1179deddcc9964862e61>::operator() C:\Projects\DEDA\cmake-build-debug-asan\_deps\cppdap-src\src\protocol_events.cpp:67
1:     #3 0x7ff741cf6a46 in std::invoke<<lambda_3c331b09e81c1179deddcc9964862e61> &,dap::FieldSerializer *> C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.44.35207\include\type_traits:1680
1:     #4 0x7ff741ce7f88 in std::_Func_impl_no_alloc<<lambda_3c331b09e81c1179deddcc9964862e61>,bool,dap::FieldSerializer *>::_Do_call C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.44.35207\include\functional:882
1:     #5 0x7ff741e36a6e in std::_Func_class<bool, class dap::FieldSerializer *>::operator()(class dap::FieldSerializer *) const C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.44.35207\include\functional:926
1:     #6 0x7ff741ddded8 in dap::json::NlohmannSerializer::object(class std::function<(class dap::FieldSerializer *)> const &) C:\Projects\DEDA\cmake-build-debug-asan\_deps\cppdap-src\src\nlohmann_json_serializer.cpp:252
1:     #7 0x7ff741cde4f2 in `dap::TypeOf<dap::OutputEvent>::type'::`2'::TI::serialize C:\Projects\DEDA\cmake-build-debug-asan\_deps\cppdap-src\src\protocol_events.cpp:67
1:     #8 0x7ff741d084bc in <lambda_c8650357e7c600854a44ed7b1c7b8900>::operator() C:\Projects\DEDA\cmake-build-debug-asan\_deps\cppdap-src\src\session.cpp:133
1:     #9 0x7ff741d24096 in std::invoke<<lambda_c8650357e7c600854a44ed7b1c7b8900> &,dap::Serializer *> C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.44.35207\include\type_traits:1680
1:     #10 0x7ff741d0e5d8 in std::_Func_impl_no_alloc<<lambda_c8650357e7c600854a44ed7b1c7b8900>,bool,dap::Serializer *>::_Do_call C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.44.35207\include\functional:882
1:     #11 0x7ff741e36b2e in std::_Func_class<bool, class dap::Serializer *>::operator()(class dap::Serializer *) const C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.44.35207\include\functional:926
1:     #12 0x7ff741dde4ac in `dap::json::NlohmannSerializer::object'::`2'::FS::field C:\Projects\DEDA\cmake-build-debug-asan\_deps\cppdap-src\src\nlohmann_json_serializer.cpp:242
1:     #13 0x7ff741d07f5b in <lambda_0af7ab67d2ae2f8f31dc5afe63122248>::operator() C:\Projects\DEDA\cmake-build-debug-asan\_deps\cppdap-src\src\session.cpp:129
1:     #14 0x7ff741d23806 in std::invoke<<lambda_0af7ab67d2ae2f8f31dc5afe63122248> &,dap::FieldSerializer *> C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.44.35207\include\type_traits:1680
1:     #15 0x7ff741d0e748 in std::_Func_impl_no_alloc<<lambda_0af7ab67d2ae2f8f31dc5afe63122248>,bool,dap::FieldSerializer *>::_Do_call C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.44.35207\include\functional:882
1:     #16 0x7ff741e36a6e in std::_Func_class<bool, class dap::FieldSerializer *>::operator()(class dap::FieldSerializer *) const C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.44.35207\include\functional:926
1:     #17 0x7ff741ddded8 in dap::json::NlohmannSerializer::object(class std::function<(class dap::FieldSerializer *)> const &) C:\Projects\DEDA\cmake-build-debug-asan\_deps\cppdap-src\src\nlohmann_json_serializer.cpp:252
1:     #18 0x7ff741cff1d0 in `anonymous namespace'::Impl::send C:\Projects\DEDA\cmake-build-debug-asan\_deps\cppdap-src\src\session.cpp:128
1:     #19 0x7ff741a07d70 in dap::Session::send<struct dap::OutputEvent, void>(struct dap::OutputEvent const &) C:\Projects\DEDA\cmake-build-debug-asan\_deps\cppdap-src\include\dap\session.h:439
1:     #20 0x7ff741a085a1 in deda::DedaServer::sendEvent<struct dap::OutputEvent>(struct dap::OutputEvent const &) C:\Projects\DEDA\src\DedaServer.h:137
1:     #21 0x7ff741975aa0 in deda::DedaServer::onEngineOutput(unsigned long, class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char>> const &) C:\Projects\DEDA\src\DedaServer.cpp:1588
1:     #22 0x7ff7419aa548 in `deda::DedaServer::initializeDebugClient'::`2'::<lambda_8>::operator() C:\Projects\DEDA\src\DedaServer.cpp:1265
1:     #23 0x7ff7419f9e4f in std::invoke<`deda::DedaServer::initializeDebugClient'::`2'::<lambda_8> &,unsigned long,std::basic_string<char,std::char_traits<char>,std::allocator<char> > const &> C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.44.35207\include\type_traits:1680
1:     #24 0x7ff7419add0d in std::_Func_impl_no_alloc<`deda::DedaServer::initializeDebugClient'::`2'::<lambda_8>,void,unsigned long,std::basic_string<char,std::char_traits<char>,std::allocator<char> > const &>::_Do_call C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.44.35207\include\functional:880
1:     #25 0x7ff74194d0b3 in std::_Func_class<void, unsigned long, class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char>> const &>::operator()(unsigned long, class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char>> const &) const C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.44.35207\include\functional:926
1:     #26 0x7ff74194bb6b in deda::SimpleOutputCallbacks::Output(unsigned long, char const *) C:\Projects\DEDA\src\DebugOutputCallbacks.cpp:43
1:     #27 0x7ffdff621c91  (C:\WINDOWS\SYSTEM32\dbgeng.dll+0x180051c91)
1:     #28 0x7ffdff614824  (C:\WINDOWS\SYSTEM32\dbgeng.dll+0x180044824)
1:     #29 0x7ffdff610083  (C:\WINDOWS\SYSTEM32\dbgeng.dll+0x180040083)
1:     #30 0x7ffdff615033  (C:\WINDOWS\SYSTEM32\dbgeng.dll+0x180045033)
1:     #31 0x7ffdff607e31  (C:\WINDOWS\SYSTEM32\dbgeng.dll+0x180037e31)
1:     #32 0x7ffdff607a86  (C:\WINDOWS\SYSTEM32\dbgeng.dll+0x180037a86)
1:     #33 0x7ffdff607b15  (C:\WINDOWS\SYSTEM32\dbgeng.dll+0x180037b15)
1:     #34 0x7ffdff73e361  (C:\WINDOWS\SYSTEM32\dbgeng.dll+0x18016e361)
1:     #35 0x7ffdff7d473c  (C:\WINDOWS\SYSTEM32\dbgeng.dll+0x18020473c)
1:     #36 0x7ffdff7d5566  (C:\WINDOWS\SYSTEM32\dbgeng.dll+0x180205566)
1:     #37 0x7ffdff707f92  (C:\WINDOWS\SYSTEM32\dbgeng.dll+0x180137f92)
1:     #38 0x7ffdff70868f  (C:\WINDOWS\SYSTEM32\dbgeng.dll+0x18013868f)
1:     #39 0x7ffdff70815e  (C:\WINDOWS\SYSTEM32\dbgeng.dll+0x18013815e)
1:     #40 0x7ff74197217c in deda::DedaServer::initializeDebugClient(void) C:\Projects\DEDA\src\DedaServer.cpp:1283
1:     #41 0x7ff74197290c in deda::DedaServer::serveDebuggerThread(void) C:\Projects\DEDA\src\DedaServer.cpp:1301
1:     #42 0x7ff7419f9ed3 in std::invoke<void (__cdecl deda::DedaServer::*)(void), class deda::DedaServer *>(void (__cdecl deda::DedaServer::*&&)(void), class deda::DedaServer *&&) C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.44.35207\include\type_traits:1686
1:     #43 0x7ff7419e4c65 in std::thread::_Invoke<class std::tuple<void (__cdecl deda::DedaServer::*)(void), class deda::DedaServer *>, 0, 1>(void *) C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.44.35207\include\thread:60
1:     #44 0x7ffd0a842ec4  (C:\WINDOWS\SYSTEM32\ucrtbased.dll+0x1800a2ec4)
1:     #45 0x7ffd2dc4d19c in asan_thread_start D:\a\_work\1\s\src\vctools\asan\llvm\compiler-rt\lib\asan\asan_win.cpp:170
1:     #46 0x7ffe1a7be8d6  (C:\WINDOWS\System32\KERNEL32.DLL+0x18002e8d6)
1:     #47 0x7ffe1b90c48b  (C:\WINDOWS\SYSTEM32\ntdll.dll+0x18008c48b)
1: 
1: Address 0x00d1c55fbd10 is located in stack of thread T1 at offset 1312 in frame
1:     #0 0x7ff741cc90cf in dap::TypeOf<struct dap::OutputEvent>::serializeFields(class dap::FieldSerializer *, void const *) C:\Projects\DEDA\cmake-build-debug-asan\_deps\cppdap-src\src\protocol_events.cpp:77
1: 
1:   This frame has 12 object(s):
1:     [32, 480) '$S20'
1:     [48, 104) 'compiler temporary'
1:     [64, 120) 'compiler temporary'
1:     [80, 136) 'compiler temporary'
1:     [96, 152) 'compiler temporary'
1:     [112, 168) 'compiler temporary'
1:     [128, 184) 'compiler temporary'
1:     [144, 200) 'compiler temporary'
1:     [160, 216) 'compiler temporary'
1:     [176, 240) 'compiler temporary'
1:     [192, 208) 'compiler temporary'
1:     [208, 224) 'compiler temporary' <== Memory access at offset 1312 overflows this variable
1: HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
1:       (longjmp, SEH and C++ exceptions *are* supported)
1: Thread T1 created by T0 here:
1:     #0 0x7ffd2dc4d597 in CreateThread D:\a\_work\1\s\src\vctools\asan\llvm\compiler-rt\lib\asan\asan_win.cpp:228
1:     #1 0x7ffd0a843616  (C:\WINDOWS\SYSTEM32\ucrtbased.dll+0x1800a3616)
1:     #2 0x7ff7419e96d9 in std::thread::_Start<void (__cdecl deda::DedaServer::*)(void), class deda::DedaServer *>(void (__cdecl deda::DedaServer::*&&)(void), class deda::DedaServer *&&) C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.44.35207\include\thread:78
1:     #3 0x7ff7419b6216 in std::thread::thread<void (__cdecl deda::DedaServer::*)(void), class deda::DedaServer *, 0>(void (__cdecl deda::DedaServer::*&&)(void), class deda::DedaServer *&&) C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.44.35207\include\thread:93
1:     #4 0x7ff7419703ce in deda::DedaServer::DedaServer(class std::shared_ptr<class dap::Reader>, class std::shared_ptr<class dap::Writer>) C:\Projects\DEDA\src\DedaServer.cpp:1181
1:     #5 0x7ff74194ea55 in deda::serveDapRequests(void) C:\Projects\DEDA\src\Deda.cpp:50
1:     #6 0x7ff74194f014 in main C:\Projects\DEDA\src\Deda.cpp:81
1:     #7 0x7ff741ebc698 in invoke_main D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:78
1:     #8 0x7ff741ebc5e1 in __scrt_common_main_seh D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
1:     #9 0x7ff741ebc49d in __scrt_common_main D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:330
1:     #10 0x7ff741ebc70d in mainCRTStartup D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp:16
1:     #11 0x7ffe1a7be8d6  (C:\WINDOWS\System32\KERNEL32.DLL+0x18002e8d6)
1:     #12 0x7ffe1b90c48b  (C:\WINDOWS\SYSTEM32\ntdll.dll+0x18008c48b)
1: 
1: SUMMARY: AddressSanitizer: stack-use-after-scope C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.44.35207\include\initializer_list:37 in std::initializer_list<struct dap::Field>::begin(void) const
1: Shadow bytes around the buggy address:
1:   0x00d1c55fba80: f2 f2 f2 f2 f8 f8 f8 f8 f8 f8 f8 f2 f2 f2 f2 f8
1:   0x00d1c55fbb00: f8 f8 f8 f8 f8 f8 f2 f2 f2 f2 f8 f8 f8 f8 f8 f8
1:   0x00d1c55fbb80: f8 f2 f2 f2 f2 f8 f8 f8 f8 f8 f8 f8 f2 f2 f2 f2
1:   0x00d1c55fbc00: f8 f8 f8 f8 f8 f8 f8 f2 f2 f2 f2 f8 f8 f8 f8 f8
1:   0x00d1c55fbc80: f8 f8 f2 f2 f2 f2 00 00 00 00 00 00 00 00 f2 f2
1: =>0x00d1c55fbd00: f2 f2[f8]f8 f2 f2 f2 f2 f8 f8 f3 f3 f3 f3 00 00
1:   0x00d1c55fbd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1:   0x00d1c55fbe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1:   0x00d1c55fbe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1:   0x00d1c55fbf00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1:   0x00d1c55fbf80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1: Shadow byte legend (one shadow byte represents 8 application bytes):
1:   Addressable:           00
1:   Partially addressable: 01 02 03 04 05 06 07 
1:   Heap left redzone:       fa
1:   Freed heap region:       fd
1:   Stack left redzone:      f1
1:   Stack mid redzone:       f2
1:   Stack right redzone:     f3
1:   Stack after return:      f5
1:   Stack use after scope:   f8
1:   Global redzone:          f9
1:   Global init order:       f6
1:   Poisoned by user:        f7
1:   Container overflow:      fc
1:   Array cookie:            ac
1:   Intra object redzone:    bb
1:   ASan internal:           fe
1:   Left alloca redzone:     ca
1:   Right alloca redzone:    cb
1: ==13104==ABORTING

@google-cla
Copy link
Copy Markdown

google-cla bot commented Mar 19, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant