简体   繁体   中英

address sanitizer sometimes misses heap-use-after-free

Keeping a pointer on an element of a vector which is resized and dereferencing it afterwards is undefined behavior.

When testing this bad practice on the following program with a std::vector<int> (with #if 0 ), the address sanitizer correctly reports a heap-use-after-free error.

$ ./prog
capa: 8
v[0]: 0x603000000010 <1000>
p: 0x603000000010 <1000>
capa: 16
v[0]: 0x6060000000e0 <1000>
=================================================================
==23068==ERROR: AddressSanitizer: heap-use-after-free on address 0x603000000010

But when trying the same experiment with std::vector<std::string> (with #if 1 ), the address sanitizer does not report anything, which leads to using a destroyed string (probably moved-from during the resize) through the pointer!

$ ./prog
capa: 8
v[0]: 0x611000000040 <1000>
p: 0x611000000040 <1000>
capa: 16
v[0]: 0x615000000080 <1000>
p: 0x611000000040 <>

My question: why does not the address sanitizer report the error in this second case?
edit: valgrind reports the error.

I tested the following program on GNU/Linux x86_64 (Archlinux) with g++ 9.2.0 and clang++ 9.0.0.

/**
  g++ -std=c++17 -o prog prog.cpp \
      -pedantic -Wall -Wextra -Wconversion -Wno-sign-conversion \
      -g -O0 -UNDEBUG -fsanitize=address,undefined
**/

#include <iostream>
#include <vector>

#if 1
# include <string>
  inline auto make_elem(int n) { return std::to_string(n); }
#else
  inline auto make_elem(int n) { return n; }
#endif

using elem_t = decltype(make_elem(0));

inline
void
fill(std::vector<elem_t> &v,
     int sz)
{
  v.resize(std::size_t(sz));
  for(auto i=0; i<sz; ++i)
  {
    v[i]=make_elem(1000+i);
  }
}

inline
void
show(const std::vector<elem_t> &v,
     const elem_t *p)
{
  std::cout << "capa: " << v.capacity() << '\n';
  std::cout << "v[0]: " << &v[0] << " <" << v[0] << ">\n";
  std::cout << "p: " << p << " <" << *p << ">\n"; // <-- possible invalid pointer here
}

int
main()
{
  constexpr auto sz=8;
  auto v=std::vector<elem_t>{};
  fill(v, sz);
  const auto *p=data(v);
  show(v, p);
  fill(v, 2*sz);
  show(v, p);
  return 0;
}

I've also filed upstream bug about this.

I've commented on github issue, but the short answer is that due to the way libstdc++.so.6 splits certain common template instantiations, such as

basic_ostream<...>::operator<<(basic_ostream<...>&, const std::string &);

and instantiates them only once inside libstdc++.so.6 , and because libstdc++.so.6 itself is not asan-instrumented, all that instrumented code can see is that you are passing a dangling pointer into an external function. It doesn't know what the external function will do with this pointer, and so can't report the error.

The problem does not reproduce with clang++... -stdlib=libc++ (dangling access is properly reported).

By default Asan detects overflows in buffers allocated with malloc . Vector's methods ( push_back , clear , resize , etc.) may or may not call malloc / free depending on element size, current capacity, etc. so Asan will often miss vector (or any other STL container) overflows.

Recent versions of Asan got better STL support (at least for std::vector and std::string ) and can detect accesses outside of container's "logical" bounds. These checks are enabled by default in Clang but not in GCC (you need to compile with -D_GLIBCXX_SANITIZE_STD_ALLOCATOR=1 there). Enabling this feature may introduce false positives so GCC's approach is IMHO safer.

As a side note, it's risky to test Asan's functionality with simple programs like this one. Modern compilers will often often detect and optimize out obviously UB code at early phases, sometimes even at -O0 (hopefully with a warning so bug won't go unnoticed) before Asan instrumentation is added so Asan has no chance of detecting the bug. You can prevent unwanted "optimizations" with __attribute__((noinline,noclone)) , inline asm, separate compilation, etc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM