Project

General

Profile

Actions

Bug #3727

closed

OBufferStream destructor use-after-free

Added by Davide Pesavento over 8 years ago. Updated about 8 years ago.

Status:
Closed
Priority:
Normal
Assignee:
Category:
Base
Target version:
Start date:
Due date:
% Done:

100%

Estimated time:
2.00 h

Description

Environment: Ubuntu 16.04, ndn-cxx:commit:5a67310e89e02f1f5290901b57637e86b7d00104

Snippet to reproduce:

// g++ -o x -std=c++0x x.cpp $(pkg-config --cflags --libs libndn-cxx)
#include <ndn-cxx/encoding/buffer-stream.hpp>

int main()
{
  ndn::OBufferStream os;
  os << '1';

  return 0;
}

Valgrind output:

==3515== Invalid read of size 4
==3515==    at 0x804C081: void std::vector<unsigned char, std::allocator<unsigned char> >::emplace_back<unsigned char>(unsigned char&&) (in /code/x)
==3515==    by 0x804B957: std::vector<unsigned char, std::allocator<unsigned char> >::push_back(unsigned char&&) (in /code/x)
==3515==    by 0x804B264: std::back_insert_iterator<ndn::Buffer>::operator=(unsigned char&&) (in /code/x)
==3515==    by 0x804AF2A: std::back_insert_iterator<ndn::Buffer> std::__copy_move<false, false, std::random_access_iterator_tag>::__copy_m<char const*, std::back_insert_iterator<ndn::Buffer> >(char const*, char const*, std::back_insert_iterator<ndn::Buffer>) (in /code/x)
==3515==    by 0x804ACC9: std::back_insert_iterator<ndn::Buffer> std::__copy_move_a<false, char const*, std::back_insert_iterator<ndn::Buffer> >(char const*, char const*, std::back_insert_iterator<ndn::Buffer>) (in /code/x)
==3515==    by 0x804A8BE: std::back_insert_iterator<ndn::Buffer> std::__copy_move_a2<false, char const*, std::back_insert_iterator<ndn::Buffer> >(char const*, char const*, std::back_insert_iterator<ndn::Buffer>) (in /code/x)
==3515==    by 0x804A470: std::back_insert_iterator<ndn::Buffer> std::copy<char const*, std::back_insert_iterator<ndn::Buffer> >(char const*, char const*, std::back_insert_iterator<ndn::Buffer>) (in /code/x)
==3515==    by 0x804A0DF: ndn::iostreams::buffer_append_device::write(char const*, int) (in /code/x)
==3515==    by 0x804CE57: int boost::iostreams::detail::write_device_impl<boost::iostreams::output>::write<ndn::iostreams::buffer_append_device>(ndn::iostreams::buffer_append_device&, boost::iostreams::char_type_of<ndn::iostreams::buffer_append_device>::type const*, int) (in /code/x)
==3515==    by 0x804CD95: int boost::iostreams::write<ndn::iostreams::buffer_append_device>(ndn::iostreams::buffer_append_device&, boost::iostreams::char_type_of<ndn::iostreams::buffer_append_device>::type const*, int) (in /code/x)
==3515==    by 0x804CC9B: int boost::iostreams::detail::device_wrapper_impl<boost::iostreams::output>::write<ndn::iostreams::buffer_append_device, boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >(ndn::iostreams::buffer_append_device&, boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> >*, boost::iostreams::char_type_of<ndn::iostreams::buffer_append_device>::type const*, int) (in /code/x)
==3515==    by 0x804CA0B: int boost::iostreams::detail::concept_adapter<ndn::iostreams::buffer_append_device>::write<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >(char const*, int, boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> >*) (in /code/x)
==3515==  Address 0x4409a68 is 16 bytes inside a block of size 24 free'd
==3515==    at 0x402E7B8: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==3515==    by 0x804C910: __gnu_cxx::new_allocator<std::_Sp_counted_ptr_inplace<ndn::Buffer, std::allocator<ndn::Buffer>, (__gnu_cxx::_Lock_policy)2> >::deallocate(std::_Sp_counted_ptr_inplace<ndn::Buffer, std::allocator<ndn::Buffer>, (__gnu_cxx::_Lock_policy)2>*, unsigned int) (in /code/x)
==3515==    by 0x804C13B: std::allocator_traits<std::allocator<std::_Sp_counted_ptr_inplace<ndn::Buffer, std::allocator<ndn::Buffer>, (__gnu_cxx::_Lock_policy)2> > >::deallocate(std::allocator<std::_Sp_counted_ptr_inplace<ndn::Buffer, std::allocator<ndn::Buffer>, (__gnu_cxx::_Lock_policy)2> >&, std::_Sp_counted_ptr_inplace<ndn::Buffer, std::allocator<ndn::Buffer>, (__gnu_cxx::_Lock_policy)2>*, unsigned int) (in /code/x)
==3515==    by 0x804B9E2: std::__allocated_ptr<std::allocator<std::_Sp_counted_ptr_inplace<ndn::Buffer, std::allocator<ndn::Buffer>, (__gnu_cxx::_Lock_policy)2> > >::~__allocated_ptr() (in /code/x)
==3515==    by 0x804D280: std::_Sp_counted_ptr_inplace<ndn::Buffer, std::allocator<ndn::Buffer>, (__gnu_cxx::_Lock_policy)2>::_M_destroy() (in /code/x)
==3515==    by 0x804A9B9: std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (in /code/x)
==3515==    by 0x804A52E: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() (in /code/x)
==3515==    by 0x804A10E: std::__shared_ptr<ndn::Buffer, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() (in /code/x)
==3515==    by 0x804A127: std::shared_ptr<ndn::Buffer>::~shared_ptr() (in /code/x)
==3515==    by 0x804CFE3: ndn::OBufferStream::~OBufferStream() (in /code/x)
==3515==    by 0x8049B9A: main (in /code/x)
==3515==  Block was alloc'd at
==3515==    at 0x402D6BC: operator new(unsigned int) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==3515==    by 0x804C8F9: __gnu_cxx::new_allocator<std::_Sp_counted_ptr_inplace<ndn::Buffer, std::allocator<ndn::Buffer>, (__gnu_cxx::_Lock_policy)2> >::allocate(unsigned int, void const*) (in /code/x)
==3515==    by 0x804C107: std::allocator_traits<std::allocator<std::_Sp_counted_ptr_inplace<ndn::Buffer, std::allocator<ndn::Buffer>, (__gnu_cxx::_Lock_policy)2> > >::allocate(std::allocator<std::_Sp_counted_ptr_inplace<ndn::Buffer, std::allocator<ndn::Buffer>, (__gnu_cxx::_Lock_policy)2> >&, unsigned int) (in /code/x)
==3515==    by 0x804B99F: std::__allocated_ptr<std::allocator<std::_Sp_counted_ptr_inplace<ndn::Buffer, std::allocator<ndn::Buffer>, (__gnu_cxx::_Lock_policy)2> > > std::__allocate_guarded<std::allocator<std::_Sp_counted_ptr_inplace<ndn::Buffer, std::allocator<ndn::Buffer>, (__gnu_cxx::_Lock_policy)2> > >(std::allocator<std::_Sp_counted_ptr_inplace<ndn::Buffer, std::allocator<ndn::Buffer>, (__gnu_cxx::_Lock_policy)2> >&) (in /code/x)
==3515==    by 0x804B2CE: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<ndn::Buffer, std::allocator<ndn::Buffer>>(std::_Sp_make_shared_tag, ndn::Buffer*, std::allocator<ndn::Buffer> const&) (in /code/x)
==3515==    by 0x804AF88: std::__shared_ptr<ndn::Buffer, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<ndn::Buffer>>(std::_Sp_make_shared_tag, std::allocator<ndn::Buffer> const&) (in /code/x)
==3515==    by 0x804AD07: std::shared_ptr<ndn::Buffer>::shared_ptr<std::allocator<ndn::Buffer>>(std::_Sp_make_shared_tag, std::allocator<ndn::Buffer> const&) (in /code/x)
==3515==    by 0x804A938: std::shared_ptr<ndn::Buffer> std::allocate_shared<ndn::Buffer, std::allocator<ndn::Buffer>>(std::allocator<ndn::Buffer> const&) (in /code/x)
==3515==    by 0x804A4C5: std::shared_ptr<ndn::Buffer> std::make_shared<ndn::Buffer>() (in /code/x)
==3515==    by 0x804A2A8: ndn::OBufferStream::OBufferStream() (in /code/x)
==3515==    by 0x8049B6F: main (in /code/x)

Related issues 1 (0 open1 closed)

Blocks NFD - Task #2589: CI: enable AddressSanitizer for unit testsClosedDavide Pesavento

Actions
Actions #1

Updated by Davide Pesavento over 8 years ago

  • Blocks Task #2589: CI: enable AddressSanitizer for unit tests added
Actions #2

Updated by Davide Pesavento over 8 years ago

  • Description updated (diff)

Better stack traces.

Actions #3

Updated by Davide Pesavento over 8 years ago

I don't know the internals of the boost::iostreams library, but from the backtrace it seems pretty clear that the destructor of boost::iostreams::stream (base class of OBufferStream) triggers the flushing of its stream_buffer, which causes a write on the underlying device (ndn::iostreams::buffer_append_device), which in turn appends one or more bytes to the final output object (an ndn::Buffer). However, this ndn::Buffer object is owned by the OBufferStream via a shared_ptr, and due to the order of destruction defined by C++, it will already have been deallocated by the time the write is performed.

Actions #4

Updated by Davide Pesavento over 8 years ago

As a side note, our custom buffer_append_device can be replaced with boost::iostreams::back_insert_device<ndn::Buffer>.

Actions #5

Updated by Junxiao Shi about 8 years ago

I'm able to reproduce the issue with Valgrind on Ubuntu 16.04. ndn-cxx:commit:5a67310e89e02f1f5290901b57637e86b7d00104

==3436== Invalid read of size 4
==3436==    at 0x809D80F: void std::vector<unsigned char, std::allocator<unsigned char> >::emplace_back<unsigned char>(unsigned char&&) (vector.tcc:94)
==3436==    by 0x809D8AC: push_back (stl_vector.h:932)
==3436==    by 0x809D8AC: operator= (stl_iterator.h:458)
==3436==    by 0x809D8AC: __copy_m<char const*, std::back_insert_iterator<ndn::Buffer> > (stl_algobase.h:340)
==3436==    by 0x809D8AC: __copy_move_a<false, char const*, std::back_insert_iterator<ndn::Buffer> > (stl_algobase.h:402)
==3436==    by 0x809D8AC: __copy_move_a2<false, char const*, std::back_insert_iterator<ndn::Buffer> > (stl_algobase.h:440)
==3436==    by 0x809D8AC: copy<char const*, std::back_insert_iterator<ndn::Buffer> > (stl_algobase.h:472)
==3436==    by 0x809D8AC: write (buffer-stream.hpp:53)
==3436==    by 0x809D8AC: write<ndn::iostreams::buffer_append_device> (write.hpp:121)
==3436==    by 0x809D8AC: write<ndn::iostreams::buffer_append_device> (write.hpp:53)
==3436==    by 0x809D8AC: write<ndn::iostreams::buffer_append_device, boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > > (concept_adapter.hpp:194)
==3436==    by 0x809D8AC: write<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > > (concept_adapter.hpp:85)
==3436==    by 0x809D8AC: boost::iostreams::detail::indirect_streambuf<ndn::iostreams::buffer_append_device, std::char_traits<char>, std::allocator<char>, boost::iostreams::output>::sync_impl() (indirect_streambuf.hpp:394)
==3436==    by 0x809E327: boost::iostreams::detail::indirect_streambuf<ndn::iostreams::buffer_append_device, std::char_traits<char>, std::allocator<char>, boost::iostreams::output>::sync() (indirect_streambuf.hpp:314)
==3436==    by 0x809E840: boost::iostreams::detail::indirect_streambuf<ndn::iostreams::buffer_append_device, std::char_traits<char>, std::allocator<char>, boost::iostreams::output>::close_impl(std::_Ios_Openmode) (indirect_streambuf.hpp:377)
==3436==    by 0x809D0F6: close (linked_streambuf.hpp:82)
==3436==    by 0x809D0F6: operator() (functional.hpp:116)
==3436==    by 0x809D0F6: boost::iostreams::detail::execute_traits<boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::result_of<boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > > ()>::type>::result_type boost::iostreams::detail::execute_all<boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > > >(boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >) (local.hpp:37)
==3436==    by 0x809D15E: boost::iostreams::detail::execute_traits<boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::result_of<boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > > ()>::type>::result_type boost::iostreams::detail::execute_all<boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::iostreams::detail::reset_operation<boost::iostreams::detail::optional<boost::iostreams::detail::concept_adapter<ndn::iostreams::buffer_append_device> > > >(boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::iostreams::detail::reset_operation<boost::iostreams::detail::optional<boost::iostreams::detail::concept_adapter<ndn::iostreams::buffer_append_device> > >) (local.hpp:40)
==3436==    by 0x809D1BB: boost::iostreams::detail::execute_traits<boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::result_of<boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > > ()>::type>::result_type boost::iostreams::detail::execute_all<boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::iostreams::detail::reset_operation<boost::iostreams::detail::optional<boost::iostreams::detail::concept_adapter<ndn::iostreams::buffer_append_device> > >, boost::iostreams::detail::clear_flags_operation<int> >(boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::iostreams::detail::reset_operation<boost::iostreams::detail::optional<boost::iostreams::detail::concept_adapter<ndn::iostreams::buffer_append_device> > >, boost::iostreams::detail::clear_flags_operation<int>) (local.hpp:43)
==3436==    by 0x809D276: boost::iostreams::detail::indirect_streambuf<ndn::iostreams::buffer_append_device, std::char_traits<char>, std::allocator<char>, boost::iostreams::output>::close() (indirect_streambuf.hpp:204)
==3436==    by 0x82D0F06: ~stream_buffer (stream_buffer.hpp:91)
==3436==    by 0x82D0F06: ~base_from_member (base_from_member.hpp:124)
==3436==    by 0x82D0F06: ~stream_base (stream.hpp:74)
==3436==    by 0x82D0F06: ~stream (stream.hpp:112)
==3436==    by 0x82D0F06: ~OBufferStream (buffer-stream.hpp:79)
==3436==    by 0x82D0F06: ndn::security::transform::tests::Security::Transform::TestHexDecode::OddByte::test_method() (hex-decode.t.cpp:270)
==3436==    by 0x82D1387: ndn::security::transform::tests::Security::Transform::TestHexDecode::OddByte_invoker() (hex-decode.t.cpp:266)
==3436==    by 0x80971F1: invoke<void (*)()> (callback.hpp:56)
==3436==    by 0x80971F1: boost::unit_test::ut_detail::callback0_impl_t<boost::unit_test::ut_detail::unused, void (*)()>::invoke() (callback.hpp:89)
==3436==    by 0x4342EA5: ??? (in /usr/lib/i386-linux-gnu/libboost_unit_test_framework.so.1.58.0)
==3436==  Address 0x6d95a78 is 16 bytes inside a block of size 24 free'd
==3436==    at 0x402E7B8: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==3436==    by 0x8097967: deallocate (new_allocator.h:110)
==3436==    by 0x8097967: deallocate (alloc_traits.h:517)
==3436==    by 0x8097967: ~__allocated_ptr (allocated_ptr.h:72)
==3436==    by 0x8097967: std::_Sp_counted_ptr_inplace<ndn::Buffer, std::allocator<ndn::Buffer>, (__gnu_cxx::_Lock_policy)2>::_M_destroy() (shared_ptr_base.h:539)
==3436==    by 0x82D0EB0: _M_release (shared_ptr_base.h:167)
==3436==    by 0x82D0EB0: ~__shared_count (shared_ptr_base.h:659)
==3436==    by 0x82D0EB0: ~__shared_ptr (shared_ptr_base.h:925)
==3436==    by 0x82D0EB0: ~shared_ptr (shared_ptr.h:93)
==3436==    by 0x82D0EB0: ~OBufferStream (buffer-stream.hpp:79)
==3436==    by 0x82D0EB0: ndn::security::transform::tests::Security::Transform::TestHexDecode::OddByte::test_method() (hex-decode.t.cpp:270)
==3436==    by 0x82D1387: ndn::security::transform::tests::Security::Transform::TestHexDecode::OddByte_invoker() (hex-decode.t.cpp:266)
==3436==    by 0x80971F1: invoke<void (*)()> (callback.hpp:56)
==3436==    by 0x80971F1: boost::unit_test::ut_detail::callback0_impl_t<boost::unit_test::ut_detail::unused, void (*)()>::invoke() (callback.hpp:89)
==3436==    by 0x4342EA5: ??? (in /usr/lib/i386-linux-gnu/libboost_unit_test_framework.so.1.58.0)
==3436==    by 0x4322C00: boost::execution_monitor::catch_signals(boost::unit_test::callback0<int> const&) (in /usr/lib/i386-linux-gnu/libboost_unit_test_framework.so.1.58.0)
==3436==    by 0x432338A: boost::execution_monitor::execute(boost::unit_test::callback0<int> const&) (in /usr/lib/i386-linux-gnu/libboost_unit_test_framework.so.1.58.0)
==3436==    by 0x4343025: boost::unit_test::unit_test_monitor_t::execute_and_translate(boost::unit_test::test_case const&) (in /usr/lib/i386-linux-gnu/libboost_unit_test_framework.so.1.58.0)
==3436==    by 0x432A1E0: boost::unit_test::framework_impl::visit(boost::unit_test::test_case const&) (in /usr/lib/i386-linux-gnu/libboost_unit_test_framework.so.1.58.0)
==3436==    by 0x43619BC: boost::unit_test::traverse_test_tree(boost::unit_test::test_case const&, boost::unit_test::test_tree_visitor&) (in /usr/lib/i386-linux-gnu/libboost_unit_test_framework.so.1.58.0)
==3436==    by 0x4361A03: boost::unit_test::traverse_test_tree(unsigned long, boost::unit_test::test_tree_visitor&) (in /usr/lib/i386-linux-gnu/libboost_unit_test_framework.so.1.58.0)
==3436==  Block was alloc'd at
==3436==    at 0x402D6BC: operator new(unsigned int) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==3436==    by 0x809CF53: allocate (new_allocator.h:104)
==3436==    by 0x809CF53: allocate (alloc_traits.h:491)
==3436==    by 0x809CF53: std::__allocated_ptr<std::allocator<std::_Sp_counted_ptr_inplace<ndn::Buffer, std::allocator<ndn::Buffer>, (__gnu_cxx::_Lock_policy)2> > > std::__allocate_guarded<std::allocator<std::_Sp_counted_ptr_inplace<ndn::Buffer, std::allocator<ndn::Buffer>, (__gnu_cxx::_Lock_policy)2> > >(std::allocator<std::_Sp_counted_ptr_inplace<ndn::Buffer, std::allocator<ndn::Buffer>, (__gnu_cxx::_Lock_policy)2> >&) (allocated_ptr.h:102)
==3436==    by 0x82D03A6: __shared_count<ndn::Buffer, std::allocator<ndn::Buffer> > (shared_ptr_base.h:615)
==3436==    by 0x82D03A6: __shared_ptr<std::allocator<ndn::Buffer> > (shared_ptr_base.h:1097)
==3436==    by 0x82D03A6: shared_ptr<std::allocator<ndn::Buffer> > (shared_ptr.h:319)
==3436==    by 0x82D03A6: allocate_shared<ndn::Buffer, std::allocator<ndn::Buffer> > (shared_ptr.h:620)
==3436==    by 0x82D03A6: make_shared<ndn::Buffer> (shared_ptr.h:636)
==3436==    by 0x82D03A6: OBufferStream (buffer-stream.hpp:87)
==3436==    by 0x82D03A6: ndn::security::transform::tests::Security::Transform::TestHexDecode::OddByte::test_method() (hex-decode.t.cpp:270)
==3436==    by 0x82D1387: ndn::security::transform::tests::Security::Transform::TestHexDecode::OddByte_invoker() (hex-decode.t.cpp:266)
==3436==    by 0x80971F1: invoke<void (*)()> (callback.hpp:56)
==3436==    by 0x80971F1: boost::unit_test::ut_detail::callback0_impl_t<boost::unit_test::ut_detail::unused, void (*)()>::invoke() (callback.hpp:89)
==3436==    by 0x4342EA5: ??? (in /usr/lib/i386-linux-gnu/libboost_unit_test_framework.so.1.58.0)
==3436==    by 0x4322C00: boost::execution_monitor::catch_signals(boost::unit_test::callback0<int> const&) (in /usr/lib/i386-linux-gnu/libboost_unit_test_framework.so.1.58.0)
==3436==    by 0x432338A: boost::execution_monitor::execute(boost::unit_test::callback0<int> const&) (in /usr/lib/i386-linux-gnu/libboost_unit_test_framework.so.1.58.0)
==3436==    by 0x4343025: boost::unit_test::unit_test_monitor_t::execute_and_translate(boost::unit_test::test_case const&) (in /usr/lib/i386-linux-gnu/libboost_unit_test_framework.so.1.58.0)
==3436==    by 0x432A1E0: boost::unit_test::framework_impl::visit(boost::unit_test::test_case const&) (in /usr/lib/i386-linux-gnu/libboost_unit_test_framework.so.1.58.0)
==3436==    by 0x43619BC: boost::unit_test::traverse_test_tree(boost::unit_test::test_case const&, boost::unit_test::test_tree_visitor&) (in /usr/lib/i386-linux-gnu/libboost_unit_test_framework.so.1.58.0)
==3436==    by 0x4361A03: boost::unit_test::traverse_test_tree(unsigned long, boost::unit_test::test_tree_visitor&) (in /usr/lib/i386-linux-gnu/libboost_unit_test_framework.so.1.58.0)
Actions #6

Updated by Junxiao Shi about 8 years ago

  • Subject changed from Security/Transform/TestHexDecode/OddByte use-after-free to OBufferStream destructor use-after-free
  • Description updated (diff)
  • Category changed from Security to Base
  • Target version set to v0.5
  • Estimated time set to 2.00 h

Original description:

Ubuntu 16.04 64-bit, ndn-cxx commit 750472b63d7df6254540356bb09647132b1e3104.

==16535==ERROR: AddressSanitizer: heap-use-after-free on address 0x604000011a28 at pc 0x0000004d9b03 bp 0x7ffe8f70a160 sp 0x7ffe8f70a150
READ of size 8 at 0x604000011a28 thread T0
    #0 0x4d9b02 in void std::vector<unsigned char, std::allocator<unsigned char> >::emplace_back<unsigned char>(unsigned char&&) /usr/include/c++/5/bits/vector.tcc:94
    #1 0x4daa06 in std::vector<unsigned char, std::allocator<unsigned char> >::push_back(unsigned char&&) /usr/include/c++/5/bits/stl_vector.h:932
    #2 0x4daa06 in std::back_insert_iterator<ndn::Buffer>::operator=(unsigned char&&) /usr/include/c++/5/bits/stl_iterator.h:458
    #3 0x4daa06 in std::back_insert_iterator<ndn::Buffer> std::__copy_move<false, false, std::random_access_iterator_tag>::__copy_m<char const*, std::back_insert_iterator<ndn::Buffer> >(char const*, char const*, std::back_insert_iterator<ndn::Buffer>) /usr/include/c++/5/bits/stl_algobase.h:340
    #4 0x4daa06 in std::back_insert_iterator<ndn::Buffer> std::__copy_move_a<false, char const*, std::back_insert_iterator<ndn::Buffer> >(char const*, char const*, std::back_insert_iterator<ndn::Buffer>) /usr/include/c++/5/bits/stl_algobase.h:402
    #5 0x4daa06 in std::back_insert_iterator<ndn::Buffer> std::__copy_move_a2<false, char const*, std::back_insert_iterator<ndn::Buffer> >(char const*, char const*, std::back_insert_iterator<ndn::Buffer>) /usr/include/c++/5/bits/stl_algobase.h:440
    #6 0x4daa06 in std::back_insert_iterator<ndn::Buffer> std::copy<char const*, std::back_insert_iterator<ndn::Buffer> >(char const*, char const*, std::back_insert_iterator<ndn::Buffer>) /usr/include/c++/5/bits/stl_algobase.h:472
    #7 0x4daa06 in ndn::iostreams::buffer_append_device::write(char const*, long) /home/davide/ndn-cxx/src/encoding/buffer-stream.hpp:53
    #8 0x4daa06 in long boost::iostreams::detail::write_device_impl<boost::iostreams::output>::write<ndn::iostreams::buffer_append_device>(ndn::iostreams::buffer_append_device&, boost::iostreams::char_type_of<ndn::iostreams::buffer_append_device>::type const*, long) /usr/include/boost/iostreams/write.hpp:121
    #9 0x4daa06 in long boost::iostreams::write<ndn::iostreams::buffer_append_device>(ndn::iostreams::buffer_append_device&, boost::iostreams::char_type_of<ndn::iostreams::buffer_append_device>::type const*, long) /usr/include/boost/iostreams/write.hpp:53
    #10 0x4daa06 in long boost::iostreams::detail::device_wrapper_impl<boost::iostreams::output>::write<ndn::iostreams::buffer_append_device, boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >(ndn::iostreams::buffer_append_device&, boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> >*, boost::iostreams::char_type_of<ndn::iostreams::buffer_append_device>::type const*, long) /usr/include/boost/iostreams/detail/adapter/concept_adapter.hpp:194
    #11 0x4daa06 in long boost::iostreams::detail::concept_adapter<ndn::iostreams::buffer_append_device>::write<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >(char const*, long, boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> >*) /usr/include/boost/iostreams/detail/adapter/concept_adapter.hpp:85
    #12 0x4daa06 in boost::iostreams::detail::indirect_streambuf<ndn::iostreams::buffer_append_device, std::char_traits<char>, std::allocator<char>, boost::iostreams::output>::sync_impl() /usr/include/boost/iostreams/detail/streambuf/indirect_streambuf.hpp:394
    #13 0x4dadf0 in boost::iostreams::detail::indirect_streambuf<ndn::iostreams::buffer_append_device, std::char_traits<char>, std::allocator<char>, boost::iostreams::output>::sync() /usr/include/boost/iostreams/detail/streambuf/indirect_streambuf.hpp:314
    #14 0x4d9e13 in boost::iostreams::detail::indirect_streambuf<ndn::iostreams::buffer_append_device, std::char_traits<char>, std::allocator<char>, boost::iostreams::output>::close_impl(std::_Ios_Openmode) /usr/include/boost/iostreams/detail/streambuf/indirect_streambuf.hpp:377
    #15 0x4d9e13 in boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> >::close(std::_Ios_Openmode) /usr/include/boost/iostreams/detail/streambuf/linked_streambuf.hpp:82
    #16 0x4d9e13 in boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >::operator()() const /usr/include/boost/iostreams/detail/functional.hpp:116
    #17 0x4d9e13 in boost::iostreams::detail::execute_traits<boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::result_of<boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > > ()>::type>::result_type boost::iostreams::detail::execute_all<boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > > >(boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >) /usr/include/boost/preprocessor/iteration/detail/local.hpp:37
    #18 0x4d9e13 in boost::iostreams::detail::execute_traits<boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::result_of<boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > > ()>::type>::result_type boost::iostreams::detail::execute_all<boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::iostreams::detail::reset_operation<boost::iostreams::detail::optional<boost::iostreams::detail::concept_adapter<ndn::iostreams::buffer_append_device> > > >(boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::iostreams::detail::reset_operation<boost::iostreams::detail::optional<boost::iostreams::detail::concept_adapter<ndn::iostreams::buffer_append_device> > >) /usr/include/boost/preprocessor/iteration/detail/local.hpp:40
    #19 0x4d9e13 in boost::iostreams::detail::execute_traits<boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::result_of<boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > > ()>::type>::result_type boost::iostreams::detail::execute_all<boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::iostreams::detail::reset_operation<boost::iostreams::detail::optional<boost::iostreams::detail::concept_adapter<ndn::iostreams::buffer_append_device> > >, boost::iostreams::detail::clear_flags_operation<int> >(boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::iostreams::detail::member_close_operation<boost::iostreams::detail::linked_streambuf<char, std::char_traits<char> > >, boost::iostreams::detail::reset_operation<boost::iostreams::detail::optional<boost::iostreams::detail::concept_adapter<ndn::iostreams::buffer_append_device> > >, boost::iostreams::detail::clear_flags_operation<int>) /usr/include/boost/preprocessor/iteration/detail/local.hpp:43
    #20 0x4d9e13 in boost::iostreams::detail::indirect_streambuf<ndn::iostreams::buffer_append_device, std::char_traits<char>, std::allocator<char>, boost::iostreams::output>::close() /usr/include/boost/iostreams/detail/streambuf/indirect_streambuf.hpp:204
    #21 0x8f8e55 in boost::iostreams::stream_buffer<ndn::iostreams::buffer_append_device, std::char_traits<char>, std::allocator<char>, boost::iostreams::output>::~stream_buffer() /usr/include/boost/iostreams/stream_buffer.hpp:91
    #22 0x8f8e55 in boost::base_from_member<boost::iostreams::stream_buffer<ndn::iostreams::buffer_append_device, std::char_traits<char>, std::allocator<char>, boost::iostreams::output>, 0>::~base_from_member() /usr/include/boost/utility/base_from_member.hpp:124
    #23 0x8f8e55 in boost::iostreams::detail::stream_base<ndn::iostreams::buffer_append_device, std::char_traits<char>, std::allocator<char>, std::ostream>::~stream_base() /usr/include/boost/iostreams/stream.hpp:74
    #24 0x8f8e55 in boost::iostreams::stream<ndn::iostreams::buffer_append_device, std::char_traits<char>, std::allocator<char> >::~stream() /usr/include/boost/iostreams/stream.hpp:112
    #25 0x8f8e55 in ndn::OBufferStream::~OBufferStream() /home/davide/ndn-cxx/src/encoding/buffer-stream.hpp:79
    #26 0x8f8e55 in ndn::security::transform::tests::Security::Transform::TestHexDecode::OddByte::test_method() ../tests/unit-tests/security/transform/hex-decode.t.cpp:270
    #27 0x8f9c2b in OddByte_invoker ../tests/unit-tests/security/transform/hex-decode.t.cpp:266
    #28 0x4c9bea in boost::unit_test::ut_detail::unused boost::unit_test::ut_detail::invoker<boost::unit_test::ut_detail::unused>::invoke<void (*)()>(void (*&)()) /usr/include/boost/test/utils/callback.hpp:56
    #29 0x4c9bea in boost::unit_test::ut_detail::callback0_impl_t<boost::unit_test::ut_detail::unused, void (*)()>::invoke() /usr/include/boost/test/utils/callback.hpp:89
    #30 0x7fd7bdacacb0  (/usr/lib/x86_64-linux-gnu/libboost_unit_test_framework.so.1.58.0+0x6acb0)
    #31 0x7fd7bdaaa995 in boost::execution_monitor::catch_signals(boost::unit_test::callback0<int> const&) (/usr/lib/x86_64-linux-gnu/libboost_unit_test_framework.so.1.58.0+0x4a995)
    #32 0x7fd7bdaab1b2 in boost::execution_monitor::execute(boost::unit_test::callback0<int> const&) (/usr/lib/x86_64-linux-gnu/libboost_unit_test_framework.so.1.58.0+0x4b1b2)
    #33 0x7fd7bdacade1 in boost::unit_test::unit_test_monitor_t::execute_and_translate(boost::unit_test::test_case const&) (/usr/lib/x86_64-linux-gnu/libboost_unit_test_framework.so.1.58.0+0x6ade1)
    #34 0x7fd7bdab209d in boost::unit_test::framework_impl::visit(boost::unit_test::test_case const&) (/usr/lib/x86_64-linux-gnu/libboost_unit_test_framework.so.1.58.0+0x5209d)
    #35 0x7fd7bdae84ca in boost::unit_test::traverse_test_tree(boost::unit_test::test_suite const&, boost::unit_test::test_tree_visitor&) (/usr/lib/x86_64-linux-gnu/libboost_unit_test_framework.so.1.58.0+0x884ca)
    #36 0x7fd7bdae84ca in boost::unit_test::traverse_test_tree(boost::unit_test::test_suite const&, boost::unit_test::test_tree_visitor&) (/usr/lib/x86_64-linux-gnu/libboost_unit_test_framework.so.1.58.0+0x884ca)
    #37 0x7fd7bdae84ca in boost::unit_test::traverse_test_tree(boost::unit_test::test_suite const&, boost::unit_test::test_tree_visitor&) (/usr/lib/x86_64-linux-gnu/libboost_unit_test_framework.so.1.58.0+0x884ca)
    #38 0x7fd7bdae84ca in boost::unit_test::traverse_test_tree(boost::unit_test::test_suite const&, boost::unit_test::test_tree_visitor&) (/usr/lib/x86_64-linux-gnu/libboost_unit_test_framework.so.1.58.0+0x884ca)
    #39 0x7fd7bdaad9f5 in boost::unit_test::framework::run(unsigned long, bool) (/usr/lib/x86_64-linux-gnu/libboost_unit_test_framework.so.1.58.0+0x4d9f5)
    #40 0x7fd7bdac9286 in boost::unit_test::unit_test_main(bool (*)(), int, char**) (/usr/lib/x86_64-linux-gnu/libboost_unit_test_framework.so.1.58.0+0x69286)
    #41 0x7fd7bc61382f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
    #42 0x4b2898 in _start (/home/davide/ndn-cxx/build/unit-tests+0x4b2898)

0x604000011a28 is located 24 bytes inside of 40-byte region [0x604000011a10,0x604000011a38)
freed by thread T0 here:
    #0 0x7fd7bf3b9b2a in operator delete(void*) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x99b2a)
    #1 0x8f93e0 in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() /usr/include/c++/5/bits/shared_ptr_base.h:167
    #2 0x8f93e0 in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() /usr/include/c++/5/bits/shared_ptr_base.h:659
    #3 0x8f93e0 in std::__shared_ptr<ndn::Buffer, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() /usr/include/c++/5/bits/shared_ptr_base.h:925
    #4 0x8f93e0 in std::shared_ptr<ndn::Buffer>::~shared_ptr() /usr/include/c++/5/bits/shared_ptr.h:93
    #5 0x8f93e0 in ndn::OBufferStream::~OBufferStream() /home/davide/ndn-cxx/src/encoding/buffer-stream.hpp:79
    #6 0x8f93e0 in ndn::security::transform::tests::Security::Transform::TestHexDecode::OddByte::test_method() ../tests/unit-tests/security/transform/hex-decode.t.cpp:270
    #7 0x8f9c2b in OddByte_invoker ../tests/unit-tests/security/transform/hex-decode.t.cpp:266
    #8 0x4c9bea in boost::unit_test::ut_detail::unused boost::unit_test::ut_detail::invoker<boost::unit_test::ut_detail::unused>::invoke<void (*)()>(void (*&)()) /usr/include/boost/test/utils/callback.hpp:56
    #9 0x4c9bea in boost::unit_test::ut_detail::callback0_impl_t<boost::unit_test::ut_detail::unused, void (*)()>::invoke() /usr/include/boost/test/utils/callback.hpp:89
    #10 0x7fd7bdacacb0  (/usr/lib/x86_64-linux-gnu/libboost_unit_test_framework.so.1.58.0+0x6acb0)
    #11 0x60300002661f  (<unknown module>)

previously allocated by thread T0 here:
    #0 0x7fd7bf3b9532 in operator new(unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x99532)
    #1 0x8f8381 in __gnu_cxx::new_allocator<std::_Sp_counted_ptr_inplace<ndn::Buffer, std::allocator<ndn::Buffer>, (__gnu_cxx::_Lock_policy)2> >::allocate(unsigned long, void const*) /usr/include/c++/5/ext/new_allocator.h:104
    #2 0x8f8381 in std::allocator_traits<std::allocator<std::_Sp_counted_ptr_inplace<ndn::Buffer, std::allocator<ndn::Buffer>, (__gnu_cxx::_Lock_policy)2> > >::allocate(std::allocator<std::_Sp_counted_ptr_inplace<ndn::Buffer, std::allocator<ndn::Buffer>, (__gnu_cxx::_Lock_policy)2> >&, unsigned long) /usr/include/c++/5/bits/alloc_traits.h:491
    #3 0x8f8381 in std::__allocated_ptr<std::allocator<std::_Sp_counted_ptr_inplace<ndn::Buffer, std::allocator<ndn::Buffer>, (__gnu_cxx::_Lock_policy)2> > > std::__allocate_guarded<std::allocator<std::_Sp_counted_ptr_inplace<ndn::Buffer, std::allocator<ndn::Buffer>, (__gnu_cxx::_Lock_policy)2> > >(std::allocator<std::_Sp_counted_ptr_inplace<ndn::Buffer, std::allocator<ndn::Buffer>, (__gnu_cxx::_Lock_policy)2> >&) /usr/include/c++/5/bits/allocated_ptr.h:102
    #4 0x8f8381 in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<ndn::Buffer, std::allocator<ndn::Buffer>>(std::_Sp_make_shared_tag, ndn::Buffer*, std::allocator<ndn::Buffer> const&) /usr/include/c++/5/bits/shared_ptr_base.h:615
    #5 0x8f8381 in std::__shared_ptr<ndn::Buffer, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<ndn::Buffer>>(std::_Sp_make_shared_tag, std::allocator<ndn::Buffer> const&) /usr/include/c++/5/bits/shared_ptr_base.h:1097
    #6 0x8f8381 in std::shared_ptr<ndn::Buffer>::shared_ptr<std::allocator<ndn::Buffer>>(std::_Sp_make_shared_tag, std::allocator<ndn::Buffer> const&) /usr/include/c++/5/bits/shared_ptr.h:319
    #7 0x8f8381 in std::shared_ptr<ndn::Buffer> std::allocate_shared<ndn::Buffer, std::allocator<ndn::Buffer>>(std::allocator<ndn::Buffer> const&) /usr/include/c++/5/bits/shared_ptr.h:620
    #8 0x8f8381 in std::shared_ptr<ndn::Buffer> std::make_shared<ndn::Buffer>() /usr/include/c++/5/bits/shared_ptr.h:636
    #9 0x8f8381 in ndn::OBufferStream::OBufferStream() /home/davide/ndn-cxx/src/encoding/buffer-stream.hpp:87
    #10 0x8f8381 in ndn::security::transform::tests::Security::Transform::TestHexDecode::OddByte::test_method() ../tests/unit-tests/security/transform/hex-decode.t.cpp:270
    #11 0x8f9c2b in OddByte_invoker ../tests/unit-tests/security/transform/hex-decode.t.cpp:266
    #12 0x4c9bea in boost::unit_test::ut_detail::unused boost::unit_test::ut_detail::invoker<boost::unit_test::ut_detail::unused>::invoke<void (*)()>(void (*&)()) /usr/include/boost/test/utils/callback.hpp:56
    #13 0x4c9bea in boost::unit_test::ut_detail::callback0_impl_t<boost::unit_test::ut_detail::unused, void (*)()>::invoke() /usr/include/boost/test/utils/callback.hpp:89
    #14 0x7fd7bdacacb0  (/usr/lib/x86_64-linux-gnu/libboost_unit_test_framework.so.1.58.0+0x6acb0)
    #15 0x60300002661f  (<unknown module>)

SUMMARY: AddressSanitizer: heap-use-after-free /usr/include/c++/5/bits/vector.tcc:94 void std::vector<unsigned char, std::allocator<unsigned char> >::emplace_back<unsigned char>(unsigned char&&)
Shadow bytes around the buggy address:
  0x0c087fffa2f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c087fffa300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c087fffa310: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c087fffa320: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c087fffa330: fa fa fa fa fa fa fa fa fa fa fd fd fd fd fd fa
=>0x0c087fffa340: fa fa fd fd fd[fd]fd fa fa fa 00 00 00 00 00 00
  0x0c087fffa350: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 00
  0x0c087fffa360: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 00
  0x0c087fffa370: fa fa fd fd fd fd fd fa fa fa fd fd fd fd fd fa
  0x0c087fffa380: fa fa fd fd fd fd fd fa fa fa fd fd fd fd fd fa
  0x0c087fffa390: fa fa fd fd fd fd fd fa fa fa fd fd fd fd fd 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
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  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
==16535==ABORTING
Actions #7

Updated by Junxiao Shi about 8 years ago

  • Status changed from New to In Progress
  • Assignee set to Junxiao Shi

our custom buffer_append_device can be replaced with boost::iostreams::back_insert_device<ndn::Buffer>.

No. ndn::Buffer is std::vector<uint8_t>. It's not convertible to std::basic_ostream<char> because it is a std::basic_ostream<uint8_t>.

Actions #8

Updated by Junxiao Shi about 8 years ago

  • Status changed from In Progress to Code review
  • % Done changed from 0 to 100

The root cause is: OBufferStream::m_buffer is deallocated before running base class destructor, but base class destructor wants to append buffered octets into OBufferStream::m_buffer.

https://gerrit.named-data.net/3157 corrects code style.

https://gerrit.named-data.net/3158 calls close() in ~OBufferStream, so that base class destructor has nothing to append.

Actions #9

Updated by Davide Pesavento about 8 years ago

Junxiao Shi wrote:

our custom buffer_append_device can be replaced with boost::iostreams::back_insert_device<ndn::Buffer>.

No. ndn::Buffer is std::vector<uint8_t>. It's not convertible to std::basic_ostream<char> because it is a std::basic_ostream<uint8_t>.

Yeah right, I noticed that after commenting but forgot to amend the comment.

Actions #10

Updated by Junxiao Shi about 8 years ago

  • Status changed from Code review to Closed
Actions

Also available in: Atom PDF