Skip to content

ASan heap-use-after-free in tcpserver tests #303

Open
@dmitrykobets-msft

Description

@dmitrykobets-msft

The MSVC Address Sanitizer detected a heap-use-after-free in test\tcp_server_test.cc.

The test-case testTCPServer1 calls tcp_client_thread.reset();, which frees the TCPClient::loop_ member of client:

TEST_UNIT(testTCPServer1) {
    // ...
    std::unique_ptr<evpp::EventLoopThread> tcp_client_thread(new evpp::EventLoopThread);
    // ...
    std::shared_ptr<evpp::TCPClient> client = StartTCPClient(tcp_client_thread->loop());
    // ...
    tcp_client_thread.reset(); // client->loop_ is freed
    tcp_server_thread.reset();
    tsrv.reset();
    assert(evpp::GetActiveEventCount() == 0);
} // ~TCPClient() called

But the ~TCPClient() destructor tries to access the loop_ member:
evpp\tcp_client.cc

TCPClient::~TCPClient() {
    // ...
    TCPConnPtr c = conn();
    // ...
}

TCPConnPtr TCPClient::conn() const {
    if (loop_->IsInLoopThread()) { // accessing freed memory `loop_`
        return conn_;
    } else {
        // ...
    }
}

Fix

One potential fix is to manually reset the client pointer first, inside the test-case:

TEST_UNIT(testTCPServer1) {
    // ...
    client.reset();  // free this first
    tcp_client_thread.reset();
    tcp_server_thread.reset();
    tsrv.reset();
    assert(evpp::GetActiveEventCount() == 0);
}

Alternatively, all of the reset() smartpointer calls can be removed in all of the tests in this file. Are these reset calls needed for testing?

Repro

To rebuild and run tests via MSVC with AddressSanitizer enabled:

# setup vcpkg
git clone F:\gitP\Microsoft\vcpkg
cd F:\gitP\Microsoft\vcpkg
bootstrap-vcpkg.bat 2>&1
vcpkg.exe install --recurse gflags glog openssl libevent thrift --triplet x64-windows --clean-after-build 2>&1
# setup evpp
mkdir F:\gitP\Qihoo360\evpp\build_amd64
cd F:\gitP\Qihoo360\evpp\build_amd64
set VSCMD_SKIP_SENDTELEMETRY=1 & "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat" -host_arch=amd64 -arch=amd64 & set _CL_= /fsanitize=address /GS- /wd5072 & set _LINK_= /InferASanLibs /incremental:no /debug
cmake -G "Visual Studio 17 2022" -A x64 -DCMAKE_SYSTEM_VERSION=10.0.22621.0 -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=F:\gitP\Microsoft\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows .. 2>&1
# build
msbuild /m /p:Platform=x64 /p:Configuration=Release safe-evpp.sln /t:Rebuild 2>&1
# test
cd F:\gitP\Qihoo360\evpp\build_amd64\bin\Release
evpp_unittest.exe --gtest_filter=-GtestObjectClass_testTimestamp.testTimestamp:GtestObjectClass_TestEventLoop1.TestEventLoop1:GtestObjectClass_testInvokerTimerCancel.testInvokerTimerCancel:GtestObjectClass_testHTTPRequest* 2>&1

To run only the problematic test-case:

evpp_unittest.exe --gtest_filter=GtestObjectClass_testTCPServer1.testTCPServer1 2>&1

AddressSanitizer output:

=================================================================
==11028==ERROR: AddressSanitizer: heap-use-after-free on address 0x12807f6fb654 at pc 0x7ff67651c2bb bp 0x0099ff75f2b0 sp 0x0099ff75f2b8
READ of size 4 at 0x12807f6fb654 thread T0
    #0 0x7ff67651c2ba in std::operator== C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.36.32532\include\thread:242
    #1 0x7ff67651c2ba in evpp::EventLoop::IsInLoopThread F:\gitP\Qihoo360\evpp\evpp\event_loop.h:84
    #2 0x7ff67651c2ba in evpp::TCPClient::conn(void) const F:\gitP\Qihoo360\evpp\evpp\tcp_client.cc:138
    #3 0x7ff676519757 in evpp::TCPClient::~TCPClient(void) F:\gitP\Qihoo360\evpp\evpp\tcp_client.cc:26
    #4 0x7ff6764c7df2 in std::_Ref_count<class evpp::TCPClient>::_Destroy(void) C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.36.32532\include\memory:1205
    #5 0x7ff67648d203 in std::_Ref_count_base::_Decref(void) C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.36.32532\include\memory:1178
    #6 0x7ff6764c9b49 in std::_Ptr_base<evpp::TCPClient>::_Decref C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.36.32532\include\memory:1403
    #7 0x7ff6764c9b49 in std::shared_ptr<evpp::TCPClient>::{dtor} C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.36.32532\include\memory:1687
    #8 0x7ff6764c9b49 in GtestObjectClass_testTCPServer1_testTCPServer1_Test::TestBody(void) F:\gitP\Qihoo360\evpp\test\tcp_server_test.cc:101
    #9 0x7ff67643a2a7 in testing::internal::HandleSehExceptionsInMethodIfSupported<class testing::Test, void>(class testing::Test *, void (__cdecl testing::Test::*)(void), char const *) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:2063
    #10 0x7ff676439df9 in testing::internal::HandleExceptionsInMethodIfSupported<class testing::Test, void>(class testing::Test *, void (__cdecl testing::Test::*)(void), char const *) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:2114
    #11 0x7ff676469818 in testing::TestInfo::Run(void) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:2326
    #12 0x7ff67646944f in testing::TestCase::Run(void) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:2444
    #13 0x7ff676469f6e in testing::internal::UnitTestImpl::RunAllTests(void) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:4315
    #14 0x7ff67643a3c7 in testing::internal::HandleSehExceptionsInMethodIfSupported<class testing::internal::UnitTestImpl, bool>(class testing::internal::UnitTestImpl *, bool (__cdecl testing::internal::UnitTestImpl::*)(void), char const *) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:2063
    #15 0x7ff67643a1f9 in testing::internal::HandleExceptionsInMethodIfSupported<class testing::internal::UnitTestImpl, bool>(class testing::internal::UnitTestImpl *, bool (__cdecl testing::internal::UnitTestImpl::*)(void), char const *) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:2114
    #16 0x7ff676469b2d in testing::UnitTest::Run(void) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:3926
    #17 0x7ff676476bf1 in RUN_ALL_TESTS F:\gitP\Qihoo360\evpp\3rdparty\gtest\gtest.h:2288
    #18 0x7ff676476bf1 in main F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest_main.cc:37
    #19 0x7ff676533333 in invoke_main C:\repos\msvc\src\vctools\crt\vcstartup\src\startup\exe_common.inl:78
    #20 0x7ff676533333 in __scrt_common_main_seh C:\repos\msvc\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
    #21 0x7ffa9cba4ddf  (C:\Windows\System32\KERNEL32.DLL+0x180014ddf)
    #22 0x7ffa9e19e44a  (C:\Windows\SYSTEM32\ntdll.dll+0x18007e44a)

0x12807f6fb654 is located 20 bytes inside of 272-byte region [0x12807f6fb640,0x12807f6fb750)
freed by thread T0 here:
    #0 0x7ff6765325e3 in operator delete(void *, unsigned __int64) C:\repos\msvc\src\vctools\asan\llvm\compiler-rt\lib\asan\asan_win_delete_scalar_size_thunk.cpp:41
    #1 0x7ff67648d203 in std::_Ref_count_base::_Decref(void) C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.36.32532\include\memory:1178
    #2 0x7ff6764dda62 in std::_Ptr_base<evpp::EventLoop>::_Decref C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.36.32532\include\memory:1403
    #3 0x7ff6764dda62 in std::shared_ptr<evpp::EventLoop>::{dtor} C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.36.32532\include\memory:1687
    #4 0x7ff6764dda62 in evpp::EventLoopThread::~EventLoopThread(void) F:\gitP\Qihoo360\evpp\evpp\event_loop_thread.cc:17
    #5 0x7ff6764c9aab in GtestObjectClass_testTCPServer1_testTCPServer1_Test::TestBody(void) F:\gitP\Qihoo360\evpp\test\tcp_server_test.cc:97
    #6 0x7ff67643a2a7 in testing::internal::HandleSehExceptionsInMethodIfSupported<class testing::Test, void>(class testing::Test *, void (__cdecl testing::Test::*)(void), char const *) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:2063
    #7 0x7ff676439df9 in testing::internal::HandleExceptionsInMethodIfSupported<class testing::Test, void>(class testing::Test *, void (__cdecl testing::Test::*)(void), char const *) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:2114
    #8 0x7ff676469818 in testing::TestInfo::Run(void) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:2326
    #9 0x7ff67646944f in testing::TestCase::Run(void) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:2444
    #10 0x7ff676469f6e in testing::internal::UnitTestImpl::RunAllTests(void) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:4315
    #11 0x7ff67643a3c7 in testing::internal::HandleSehExceptionsInMethodIfSupported<class testing::internal::UnitTestImpl, bool>(class testing::internal::UnitTestImpl *, bool (__cdecl testing::internal::UnitTestImpl::*)(void), char const *) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:2063
    #12 0x7ff67643a1f9 in testing::internal::HandleExceptionsInMethodIfSupported<class testing::internal::UnitTestImpl, bool>(class testing::internal::UnitTestImpl *, bool (__cdecl testing::internal::UnitTestImpl::*)(void), char const *) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:2114
    #13 0x7ff676469b2d in testing::UnitTest::Run(void) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:3926
    #14 0x7ff676476bf1 in RUN_ALL_TESTS F:\gitP\Qihoo360\evpp\3rdparty\gtest\gtest.h:2288
    #15 0x7ff676476bf1 in main F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest_main.cc:37
    #16 0x7ff676533333 in invoke_main C:\repos\msvc\src\vctools\crt\vcstartup\src\startup\exe_common.inl:78
    #17 0x7ff676533333 in __scrt_common_main_seh C:\repos\msvc\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
    #18 0x7ffa9cba4ddf  (C:\Windows\System32\KERNEL32.DLL+0x180014ddf)
    #19 0x7ffa9e19e44a  (C:\Windows\SYSTEM32\ntdll.dll+0x18007e44a)

previously allocated by thread T0 here:
    #0 0x7ff676532555 in operator new(unsigned __int64) C:\repos\msvc\src\vctools\asan\llvm\compiler-rt\lib\asan\asan_win_new_scalar_thunk.cpp:40
    #1 0x7ff6764dd5b8 in evpp::EventLoopThread::EventLoopThread(void) F:\gitP\Qihoo360\evpp\evpp\event_loop_thread.cc:9
    #2 0x7ff6764c952c in GtestObjectClass_testTCPServer1_testTCPServer1_Test::TestBody(void) F:\gitP\Qihoo360\evpp\test\tcp_server_test.cc:71
    #3 0x7ff67643a2a7 in testing::internal::HandleSehExceptionsInMethodIfSupported<class testing::Test, void>(class testing::Test *, void (__cdecl testing::Test::*)(void), char const *) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:2063
    #4 0x7ff676439df9 in testing::internal::HandleExceptionsInMethodIfSupported<class testing::Test, void>(class testing::Test *, void (__cdecl testing::Test::*)(void), char const *) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:2114
    #5 0x7ff676469818 in testing::TestInfo::Run(void) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:2326
    #6 0x7ff67646944f in testing::TestCase::Run(void) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:2444
    #7 0x7ff676469f6e in testing::internal::UnitTestImpl::RunAllTests(void) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:4315
    #8 0x7ff67643a3c7 in testing::internal::HandleSehExceptionsInMethodIfSupported<class testing::internal::UnitTestImpl, bool>(class testing::internal::UnitTestImpl *, bool (__cdecl testing::internal::UnitTestImpl::*)(void), char const *) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:2063
    #9 0x7ff67643a1f9 in testing::internal::HandleExceptionsInMethodIfSupported<class testing::internal::UnitTestImpl, bool>(class testing::internal::UnitTestImpl *, bool (__cdecl testing::internal::UnitTestImpl::*)(void), char const *) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:2114
    #10 0x7ff676469b2d in testing::UnitTest::Run(void) F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest.cc:3926
    #11 0x7ff676476bf1 in RUN_ALL_TESTS F:\gitP\Qihoo360\evpp\3rdparty\gtest\gtest.h:2288
    #12 0x7ff676476bf1 in main F:\gitP\Qihoo360\evpp\3rdparty\gtest\src\gtest_main.cc:37
    #13 0x7ff676533333 in invoke_main C:\repos\msvc\src\vctools\crt\vcstartup\src\startup\exe_common.inl:78
    #14 0x7ff676533333 in __scrt_common_main_seh C:\repos\msvc\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
    #15 0x7ffa9cba4ddf  (C:\Windows\System32\KERNEL32.DLL+0x180014ddf)
    #16 0x7ffa9e19e44a  (C:\Windows\SYSTEM32\ntdll.dll+0x18007e44a)

SUMMARY: AddressSanitizer: heap-use-after-free C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.36.32532\include\thread:242 in std::operator==
Shadow bytes around the buggy address:
  0x04ac8f05f670: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x04ac8f05f680: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fa
  0x04ac8f05f690: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x04ac8f05f6a0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x04ac8f05f6b0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fa
=>0x04ac8f05f6c0: fa fa fa fa fa fa fa fa fd fd[fd]fd fd fd fd fd
  0x04ac8f05f6d0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x04ac8f05f6e0: fd fd fd fd fd fd fd fd fd fd fa fa fa fa fa fa
  0x04ac8f05f6f0: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x04ac8f05f700: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x04ac8f05f710: fd fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==11028==ABORTING

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions