Skip to content

asan container-overflow false positive on std::vector assignment with custom allocator #60384

@zmodem

Description

@zmodem

https://reviews.llvm.org/rG490555026821db47d1cf4bf08c219b3e56ec6b45 made __sanitizer_annotate_contiguous_container apply to std::vector also when using custom allocators.

This can cause false container-overflow errors if the custom allocator touches the memory it manages.

Reproducer (reduced from https://crbug.com/1410719#c6):

$ cat /tmp/a.cc
#include <stdlib.h>
#include <string.h>
#include <vector>

template <typename T>
struct ZeroAlloc {
  using value_type = T;
  ZeroAlloc() noexcept {}
  template <typename U> ZeroAlloc(const ZeroAlloc<U>&) noexcept {}
  template <typename U> bool operator==(const ZeroAlloc<U>&) const noexcept { return true; }
  template <typename U> bool operator!=(const ZeroAlloc<U>&) const noexcept { return false; }

  T* allocate(size_t n) const {
    return (T*)malloc(sizeof(T) * n);
  }
  void deallocate(T* p, size_t n) const noexcept {
    memset(p, 0, sizeof(T) * n);
    free(p);
  }
};

int main() {
  using Vector = std::vector<int, ZeroAlloc<int>>;

  Vector v;
  v.resize(100);

  Vector w;
  w.push_back(0);
  w.reserve(10);

  w = v; // Goes boom when deallocating w's storage.

  return 0;
}

In the assignment, w's storage will get deallocated, causing memset to touch memory outside of w's current size:

$ build2/bin/clang++ -g -fsanitize=address -stdlib=libc++ /tmp/a.cc && ASAN_SYMBOLIZER_PATH=/work/llvm-project/build2/bin/llvm-symbolizer LD_LIBRARY_PATH=build2/lib/x86_64-unknown-linux-gnu/ ./a.out
=================================================================
==1559804==ERROR: AddressSanitizer: container-overflow on address 0x604000000050 at pc 0x5628a2ee5f38 bp 0x7ffde290e7b0 sp 0x7ffde290df78
WRITE of size 40 at 0x604000000050 thread T0
    #0 0x5628a2ee5f37 in __asan_memset /work/llvm-project/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp:26:3
    #1 0x5628a2f269b6 in ZeroAlloc<int>::deallocate(int*, unsigned long) const /tmp/a.cc:17:5
    #2 0x5628a2f266d4 in std::__1::allocator_traits<ZeroAlloc<int>>::deallocate[abi:v170000](ZeroAlloc<int>&, int*, unsigned long) /work/llvm-project/build2/bin/../include/c++/v1/__memory/allocator_traits.h:288:13
    #3 0x5628a2f2e022 in std::__1::vector<int, ZeroAlloc<int>>::__vdeallocate() /work/llvm-project/build2/bin/../include/c++/v1/vector:974:9
    #4 0x5628a2f2d9e0 in void std::__1::vector<int, ZeroAlloc<int>>::assign<int*, 0>(int*, int*) /work/llvm-project/build2/bin/../include/c++/v1/vector:1404:9
    #5 0x5628a2f26175 in std::__1::vector<int, ZeroAlloc<int>>::operator=[abi:v170000](std::__1::vector<int, ZeroAlloc<int>> const&) /work/llvm-project/build2/bin/../include/c++/v1/vector:1361:9
    #6 0x5628a2f258f3 in main /tmp/a.cc:32:5
    #7 0x7fa4d2cb1189 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #8 0x7fa4d2cb1244 in __libc_start_main csu/../csu/libc-start.c:381:3
    #9 0x5628a2e4c340 in _start (/work/llvm-project/a.out+0x1e340)

0x604000000050 is located 0 bytes inside of 40-byte region [0x604000000050,0x604000000078)
allocated by thread T0 here:
    #0 0x5628a2ee69be in __interceptor_malloc /work/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:69:3
    #1 0x5628a2f28fbc in ZeroAlloc<int>::allocate(unsigned long) const /tmp/a.cc:14:16
    #2 0x5628a2f28e20 in std::__1::__allocation_result<std::__1::allocator_traits<ZeroAlloc<int>>::pointer> std::__1::__allocate_at_least[abi:v170000]<ZeroAlloc<int>>(ZeroAlloc<int>&, unsigned long) /work/llvm-project/build2/bin/../include/c++/v1/__memory/allocate_at_least.h:55:19
    #3 0x5628a2f27559 in std::__1::__split_buffer<int, ZeroAlloc<int>&>::__split_buffer(unsigned long, unsigned long, ZeroAlloc<int>&) /work/llvm-project/build2/bin/../include/c++/v1/__split_buffer:323:29
    #4 0x5628a2f25fae in std::__1::vector<int, ZeroAlloc<int>>::reserve(unsigned long) /work/llvm-project/build2/bin/../include/c++/v1/vector:1520:53
    #5 0x5628a2f258e1 in main /tmp/a.cc:30:5
    #6 0x7fa4d2cb1189 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

The reallocation logic in the assignment code looks like this:

        __vdeallocate();                                                             
        __vallocate(__recommend(__new_size));
        __construct_at_end(__first, __last, __new_size);

Perhaps annotations should be added around the __vdeallocate call to avoid the false positive.

Metadata

Metadata

Assignees

Labels

compiler-rt:asanAddress sanitizerlibc++libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions