Skip to content

[asan] Clarify how __sanitizer_annotate_contiguous_container works #146106

Open
@ldionne

Description

@ldionne

I was looking at libc++'s implementation of container ASAN annotations and I couldn't understand how __sanitizer_annotate_contiguous_container works. The documentation here says:

// This annotation tells the Sanitizer tool about the current state of the
/// container so that the tool can report errors when memory from
/// [mid, end) is accessed. Insert this annotation into methods like
/// push_back() or pop_back(). Supply the old and new values of
/// mid(old_mid and new_mid). In the initial
/// state mid == end, so that should be the final state when the
/// container is destroyed or when the container reallocates the storage.

This says that [mid, end) is the range considered invalid to access, which makes sense. However it also says that the initial state is mid == end, which means that the whole range [begin, mid) == [begin, end) is considered valid to access. Shouldn't the initial state be that mid == begin instead, so that the range [mid, end) == the whole vector be considered invalid to access?

This also ties into a question I have about our usage in libc++ (CC @AdvenamTacet):

template <class _Tp, class _Allocator>
void vector<_Tp, _Allocator>::__swap_out_circular_buffer(__split_buffer<value_type, allocator_type&>& __v) {
  __annotate_delete();
  auto __new_begin = __v.__begin_ - (__end_ - __begin_);
  std::__uninitialized_allocator_relocate(__alloc_, __begin_, __end_, __new_begin);
  __v.__begin_ = __new_begin;
  __end_       = __begin_; // All the objects have been destroyed by relocating them.
  std::swap(this->__begin_, __v.__begin_);
  std::swap(this->__end_, __v.__end_);
  std::swap(this->__cap_, __v.__cap_);
  __v.__first_ = __v.__begin_;
  __annotate_new(size());
}

__annotate_delete() basically calls

__sanitizer_annotate_contiguous_container(data(), data() + capacity(),
                                          data() + size(), data() + capacity());

If I understand correctly, this sets the sanitizer state to this (using the beg end mid terminology from the sanitizer documentation, not the terminology inside std::vector):

  • beg == data()
  • end == data() + capacity()
  • mid == data() + capacity()

This means that after calling __annotate_delete(), ASAN would consider the range [mid, end) == [data() + capacity(), data() + capacity) == empty-range as invalid. Is that intended? Is the goal to mark the whole range as accessible so we can then touch it (in __uninitialized_allocator_relocate) without triggering an ASAN violation?

Metadata

Metadata

Assignees

No one assigned

    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