chriskohlhoff / asio

Asio C++ Library
http://think-async.com/Asio
4.98k stars 1.22k forks source link

AddressSanitizer reports stack-buffer-overflow in ~io_context function #906

Open xhz636 opened 3 years ago

xhz636 commented 3 years ago

using g++ v9.1.0 and Boost v1.77 AddressSanitizer reports stack-buffer-overflow when program exit

==358148==WARNING: ASan is ignoring requested __asan_handle_no_return: stack top: 0x7ffc4a415000; bottom 0x631000022000; size: 0x1cec4a3f3000 (31801183514624)
False positive error reports may follow
For details see https://github.com/google/sanitizers/issues/189
=================================================================
==358148==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x6310000241c8 at pc 0x7fac98d0db20 bp 0x631000023f90 sp 0x631000023740
WRITE of size 8 at 0x6310000241c8 thread T0
    #0 0x7fac98d0db1f in __interceptor_memcpy ../../../../libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:790
    #1 0x7fac979412fe  (/lib64/libgcc_s.so.1+0xd2fe)
    #2 0x7fac97943e3c in _Unwind_Resume (/lib64/libgcc_s.so.1+0xfe3c)
    #3 0x407bfa in loop(boost::asio::basic_yield_context<boost::asio::executor_binder<void (*)(), boost::asio::any_io_executor> >) /home/yangli.wang/test/asan/main.cpp:18
    #4 0x46537a in boost::asio::detail::coro_entry_point<boost::asio::executor_binder<void (*)(), boost::asio::strand<boost::asio::io_context::basic_executor_type<std::allocator<void>, 0u> > >, int (*)(boost::asio::basic_yield_context<boost::asio::executor_binder<void (*)(), boost::asio::any_io_executor> >)>::operator()(boost::coroutines::pull_coroutine<void>&) boost_1_77_0/include/boost/asio/impl/spawn.hpp:346
    #5 0x464f8d in boost::coroutines::detail::push_coroutine_object<boost::coroutines::pull_coroutine<void>, void, boost::asio::detail::coro_entry_point<boost::asio::executor_binder<void (*)(), boost::asio::strand<boost::asio::io_context::basic_executor_type<std::allocator<void>, 0u> > >, int (*)(boost::asio::basic_yield_context<boost::asio::executor_binder<void (*)(), boost::asio::any_io_executor> >)>&, boost::coroutines::basic_standard_stack_allocator<boost::coroutines::stack_traits> >::run() boost_1_77_0/include/boost/coroutine/detail/push_coroutine_object.hpp:302
    #6 0x464c44 in void boost::coroutines::detail::trampoline_push_void<boost::coroutines::detail::push_coroutine_object<boost::coroutines::pull_coroutine<void>, void, boost::asio::detail::coro_entry_point<boost::asio::executor_binder<void (*)(), boost::asio::strand<boost::asio::io_context::basic_executor_type<std::allocator<void>, 0u> > >, int (*)(boost::asio::basic_yield_context<boost::asio::executor_binder<void (*)(), boost::asio::any_io_executor> >)>&, boost::coroutines::basic_standard_stack_allocator<boost::coroutines::stack_traits> > >(boost::context::detail::transfer_t) boost_1_77_0/include/boost/coroutine/detail/trampoline_push.hpp:70
    #7 0x7fac9864aa1e in make_fcontext (boost_1_77_0/lib/libboost_context.so.1.77.0+0xa1e)

......

SUMMARY: AddressSanitizer: stack-buffer-overflow ../../../../libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:790 in __interceptor_memcpy
Shadow bytes around the buggy address:
  0x0c627fffc7e0: 00 00 f1 f1 f1 f1 00 00 f3 f3 00 00 00 00 00 00
  0x0c627fffc7f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c627fffc800: f1 f1 f1 f1 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c627fffc810: 00 00 00 00 00 00 00 f3 f3 f3 f3 f3 f3 f3 f3 f3
  0x0c627fffc820: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f2
=>0x0c627fffc830: f2 f2 00 f2 f2 f2 00 f3 f3[f3]00 00 00 00 00 00
  0x0c627fffc840: 00 00 f1 f1 f1 f1 f1 f1 f8 f2 f8 f2 00 00 00 f2
  0x0c627fffc850: f2 f2 f2 f2 00 00 00 04 f2 f2 f2 f2 00 00 00 00
  0x0c627fffc860: 00 00 00 00 00 00 f2 f2 f2 f2 f8 f8 f8 f8 f8 f8
  0x0c627fffc870: f8 f8 f8 f8 f8 f3 f3 f3 f3 f3 00 00 00 00 00 00
  0x0c627fffc880: 00 00 00 00 f1 f1 f1 f1 f1 f1 00 00 f2 f2 00 00
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
  Shadow gap:              cc
==358148==ABORTING

there is the code

#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <iostream>
#include <chrono>
#include <thread>
#include <atomic>

volatile std::atomic_bool run = true;
boost::asio::io_context ioc;
boost::asio::ip::tcp::acceptor acceptor(ioc);

int loop(boost::asio::yield_context yield)
{
    boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), 12345);
    acceptor.open(endpoint.protocol());
    acceptor.bind(endpoint);
    acceptor.listen();
    boost::asio::ip::tcp::socket socket(ioc);
    boost::system::error_code ec;
    while (run)
    {
        acceptor.async_accept(socket, yield[ec]);
        std::cout << ec << std::endl;
    }
    return 0;
}

void test()
{
    spawn(boost::asio::make_strand(ioc), loop);
    while (run)
    {
        ioc.run();
    }
}

int main()
{
    std::thread thd(test);
    using namespace std::chrono_literals;
    std::this_thread::sleep_for(1s);
    run = false;

    // it will exit normally
    // boost::asio::post(ioc, [](){ acceptor.cancel(); });

    // it will abort by AddressSanitizer
    ioc.stop();

    thd.join();
    return 0;
}

and the directory structure is

.
|-- boost_1_77_0
|   |-- include
|   |-- lib
|
|-- main.cpp

i use the command g++ main.cpp -g -std=c++17 -fsanitize=address -I./boost_1_77_0/include/ -L./boost_1_77_0/lib/ -lboost_coroutine -lboost_thread -lboost_context -lboost_chrono -lpthread to compile, and use env LD_LIBRARY_PATH=./boost_1_77_0/lib/:$LD_LIBRARY_PATH ./a.out to run

if using ioc.stop() to make ioc.run() exit, it will abort by AddressSanitizer when the program exit. i use gdb to trace and found that aborting occurred at ~io_context function, which call make_fcontext finally.

i guess this is a false positive caused by make_fcontext, but why? is there any way to solve this false positive?

in addition, should i also use ioc.stop() to stop io_context? is there a problem with this code?

Trigve commented 3 years ago

AFAIK make_fcontext() is used in boost::coroutines to start the stackfull coroutine, so there could be some "magic" code which does setup the coroutine and sanitizer didnt like it.