Skip to content

Commit

Permalink
revise std::vector uninit-mem hacks under libc++ (#1912)
Browse files Browse the repository at this point in the history
Summary:
X-link: facebook/folly#1912
Closes: facebook/folly#1867

The `std::__vector_base` class in libc++ is removed in libc++ 15 (llvm/llvm-project@b82da8b). So the two current implementations of the hacks - the implementation for libc++ 14 and the implementation for earlier libc++ - both fail.

Even with libc++ 14, which retains `std::__vector_base`, Xcode 14 fails to compile the hacks:

```
In file included from folly/memory/test/UninitializedMemoryHacksODR.cpp:17:
folly/memory/UninitializedMemoryHacks.h:461:1: error: conversion from 'std::__vector_base<char, std::allocator<char>>::pointer std::__vector_base<char, std::allocator<char>>::*' to 'char *std::vector<char>::*' is not allowed in a converted constant expression
FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT(char)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
folly/memory/UninitializedMemoryHacks.h:327:7: note: expanded from macro 'FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT'
      &std::vector<TYPE>::__end_,                                    \
      ^~~~~~~~~~~~~~~~~~~~~~~~~~
...
```

Since libc++ is unlikely to change the internal layout of `std::vector` on a cadence which is too frequent for folly - any material change would be an ABI break -, we can use a layout struct rather than a template instance for the hacks and have it work for a range of versions of libc++ and Xcode.

Reviewed By: simpkins, swolchok

Differential Revision: D42459219

fbshipit-source-id: e23ef383869681b5c81e4301114020b9089c7461
  • Loading branch information
yfeldblum authored and facebook-github-bot committed Jan 14, 2023
1 parent 173ffb0 commit 4b346fa
Showing 1 changed file with 38 additions and 79 deletions.
117 changes: 38 additions & 79 deletions third-party/folly/src/folly/memory/UninitializedMemoryHacks.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#pragma once

#include <cstddef>
#include <string>
#include <type_traits>
#include <vector>
Expand Down Expand Up @@ -291,89 +292,47 @@ namespace detail {
} \
}

#if defined(_LIBCPP_VECTOR) && _LIBCPP_VERSION >= 14000
// libc++ newer

template <
typename Tag,
typename T,
typename A,
A Ptr__end_,
typename B,
B Ptr__annotate_contiguous_container_>
struct MakeUnsafeVectorSetLargerSize {
friend void unsafeVectorSetLargerSizeImpl(std::vector<T>& v, std::size_t n) {
const auto old_size = v.size();
v.*Ptr__end_ += (n - v.size());

// libc++ contiguous containers use special annotation functions that help
// the address sanitizer to detect improper memory accesses. When ASAN is
// enabled we need to call the appropriate annotation functions in order to
// stop ASAN from reporting false positives. When ASAN is disabled, the
// annotation function is a no-op.
(v.*Ptr__annotate_contiguous_container_)(
v.data(),
v.data() + v.capacity(),
v.data() + old_size,
v.data() + v.size());
}
};

#define FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT(TYPE) \
template struct folly::detail::MakeUnsafeVectorSetLargerSize< \
FollyMemoryDetailTranslationUnitTag, \
TYPE, \
TYPE*(std::vector<TYPE, std::allocator<TYPE>>::*), \
&std::vector<TYPE>::__end_, \
void (std::vector<TYPE>::*)( \
const void*, const void*, const void*, const void*) const, \
&std::vector<TYPE>::__annotate_contiguous_container>; \
FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT_IMPL(TYPE)

#elif defined(_LIBCPP_VECTOR)
#if defined(_LIBCPP_VECTOR)
// libc++

template <
typename Tag,
typename T,
typename A,
A Ptr__end_,
typename B,
B Ptr__annotate_contiguous_container_>
struct MakeUnsafeVectorSetLargerSize {
friend void unsafeVectorSetLargerSizeImpl(std::vector<T>& v, std::size_t n) {
// v.__end_ += (n - v.size());
using Base = std::__vector_base<T, std::allocator<T>>;
static_assert(
std::is_standard_layout<std::vector<T>>::value &&
sizeof(std::vector<T>) == sizeof(Base),
"reinterpret_cast safety conditions not met");
const auto old_size = v.size();
reinterpret_cast<Base&>(v).*Ptr__end_ += (n - v.size());

// libc++ contiguous containers use special annotation functions that help
// the address sanitizer to detect improper memory accesses. When ASAN is
// enabled we need to call the appropriate annotation functions in order to
// stop ASAN from reporting false positives. When ASAN is disabled, the
// annotation function is a no-op.
(v.*Ptr__annotate_contiguous_container_)(
v.data(),
v.data() + v.capacity(),
v.data() + old_size,
v.data() + v.size());
}
template <typename T, typename Alloc = std::allocator<T>>
struct __std_vector_layout {
static_assert(!std::is_same<T, bool>::value, "bad instance");
using allocator_type = Alloc;
using pointer = typename std::allocator_traits<allocator_type>::pointer;

pointer __begin_;
pointer __end_;
std::__compressed_pair<pointer, allocator_type> __end_cap_;
};

#define FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT(TYPE) \
template struct folly::detail::MakeUnsafeVectorSetLargerSize< \
FollyMemoryDetailTranslationUnitTag, \
TYPE, \
TYPE*(std::__vector_base<TYPE, std::allocator<TYPE>>::*), \
&std::vector<TYPE>::__end_, \
void (std::vector<TYPE>::*)( \
const void*, const void*, const void*, const void*) const, \
&std::vector<TYPE>::__annotate_contiguous_container>; \
FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT_IMPL(TYPE)
template <typename T>
void unsafeVectorSetLargerSize(std::vector<T>& v, std::size_t n) {
using real = std::vector<T>;
using fake = __std_vector_layout<T>;
using pointer = typename fake::pointer;
static_assert(sizeof(fake) == sizeof(real), "mismatch");
static_assert(alignof(fake) == alignof(real), "mismatch");

auto const l = reinterpret_cast<unsigned char*>(&v);

auto const s = v.size();

auto& e = *reinterpret_cast<pointer*>(l + offsetof(fake, __end_));
e += (n - s);

// libc++ contiguous containers use special annotation functions that help
// the address sanitizer to detect improper memory accesses. When ASAN is
// enabled we need to call the appropriate annotation functions in order to
// stop ASAN from reporting false positives. When ASAN is disabled, the
// annotation function is a no-op.
#ifndef _LIBCPP_HAS_NO_ASAN
__sanitizer_annotate_contiguous_container(
v.data(), v.data() + v.capacity(), v.data() + s, v.data() + n);
#endif
}

#define FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT(TYPE)

#elif defined(_GLIBCXX_VECTOR)
// libstdc++
Expand Down

0 comments on commit 4b346fa

Please sign in to comment.