Skip to content

Commit f32f2ef

Browse files
yfeldblumfacebook-github-bot
authored andcommitted
revise std::vector uninit-mem hacks under libc++ (#1912)
Summary: Pull Request resolved: #1912 Closes: #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
1 parent 1a7cc67 commit f32f2ef

File tree

1 file changed

+38
-79
lines changed

1 file changed

+38
-79
lines changed

folly/memory/UninitializedMemoryHacks.h

Lines changed: 38 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#pragma once
1818

19+
#include <cstddef>
1920
#include <string>
2021
#include <type_traits>
2122
#include <vector>
@@ -291,89 +292,47 @@ namespace detail {
291292
} \
292293
}
293294

294-
#if defined(_LIBCPP_VECTOR) && _LIBCPP_VERSION >= 14000
295-
// libc++ newer
296-
297-
template <
298-
typename Tag,
299-
typename T,
300-
typename A,
301-
A Ptr__end_,
302-
typename B,
303-
B Ptr__annotate_contiguous_container_>
304-
struct MakeUnsafeVectorSetLargerSize {
305-
friend void unsafeVectorSetLargerSizeImpl(std::vector<T>& v, std::size_t n) {
306-
const auto old_size = v.size();
307-
v.*Ptr__end_ += (n - v.size());
308-
309-
// libc++ contiguous containers use special annotation functions that help
310-
// the address sanitizer to detect improper memory accesses. When ASAN is
311-
// enabled we need to call the appropriate annotation functions in order to
312-
// stop ASAN from reporting false positives. When ASAN is disabled, the
313-
// annotation function is a no-op.
314-
(v.*Ptr__annotate_contiguous_container_)(
315-
v.data(),
316-
v.data() + v.capacity(),
317-
v.data() + old_size,
318-
v.data() + v.size());
319-
}
320-
};
321-
322-
#define FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT(TYPE) \
323-
template struct folly::detail::MakeUnsafeVectorSetLargerSize< \
324-
FollyMemoryDetailTranslationUnitTag, \
325-
TYPE, \
326-
TYPE*(std::vector<TYPE, std::allocator<TYPE>>::*), \
327-
&std::vector<TYPE>::__end_, \
328-
void (std::vector<TYPE>::*)( \
329-
const void*, const void*, const void*, const void*) const, \
330-
&std::vector<TYPE>::__annotate_contiguous_container>; \
331-
FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT_IMPL(TYPE)
332-
333-
#elif defined(_LIBCPP_VECTOR)
295+
#if defined(_LIBCPP_VECTOR)
334296
// libc++
335297

336-
template <
337-
typename Tag,
338-
typename T,
339-
typename A,
340-
A Ptr__end_,
341-
typename B,
342-
B Ptr__annotate_contiguous_container_>
343-
struct MakeUnsafeVectorSetLargerSize {
344-
friend void unsafeVectorSetLargerSizeImpl(std::vector<T>& v, std::size_t n) {
345-
// v.__end_ += (n - v.size());
346-
using Base = std::__vector_base<T, std::allocator<T>>;
347-
static_assert(
348-
std::is_standard_layout<std::vector<T>>::value &&
349-
sizeof(std::vector<T>) == sizeof(Base),
350-
"reinterpret_cast safety conditions not met");
351-
const auto old_size = v.size();
352-
reinterpret_cast<Base&>(v).*Ptr__end_ += (n - v.size());
353-
354-
// libc++ contiguous containers use special annotation functions that help
355-
// the address sanitizer to detect improper memory accesses. When ASAN is
356-
// enabled we need to call the appropriate annotation functions in order to
357-
// stop ASAN from reporting false positives. When ASAN is disabled, the
358-
// annotation function is a no-op.
359-
(v.*Ptr__annotate_contiguous_container_)(
360-
v.data(),
361-
v.data() + v.capacity(),
362-
v.data() + old_size,
363-
v.data() + v.size());
364-
}
298+
template <typename T, typename Alloc = std::allocator<T>>
299+
struct __std_vector_layout {
300+
static_assert(!std::is_same<T, bool>::value, "bad instance");
301+
using allocator_type = Alloc;
302+
using pointer = typename std::allocator_traits<allocator_type>::pointer;
303+
304+
pointer __begin_;
305+
pointer __end_;
306+
std::__compressed_pair<pointer, allocator_type> __end_cap_;
365307
};
366308

367-
#define FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT(TYPE) \
368-
template struct folly::detail::MakeUnsafeVectorSetLargerSize< \
369-
FollyMemoryDetailTranslationUnitTag, \
370-
TYPE, \
371-
TYPE*(std::__vector_base<TYPE, std::allocator<TYPE>>::*), \
372-
&std::vector<TYPE>::__end_, \
373-
void (std::vector<TYPE>::*)( \
374-
const void*, const void*, const void*, const void*) const, \
375-
&std::vector<TYPE>::__annotate_contiguous_container>; \
376-
FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT_IMPL(TYPE)
309+
template <typename T>
310+
void unsafeVectorSetLargerSize(std::vector<T>& v, std::size_t n) {
311+
using real = std::vector<T>;
312+
using fake = __std_vector_layout<T>;
313+
using pointer = typename fake::pointer;
314+
static_assert(sizeof(fake) == sizeof(real), "mismatch");
315+
static_assert(alignof(fake) == alignof(real), "mismatch");
316+
317+
auto const l = reinterpret_cast<unsigned char*>(&v);
318+
319+
auto const s = v.size();
320+
321+
auto& e = *reinterpret_cast<pointer*>(l + offsetof(fake, __end_));
322+
e += (n - s);
323+
324+
// libc++ contiguous containers use special annotation functions that help
325+
// the address sanitizer to detect improper memory accesses. When ASAN is
326+
// enabled we need to call the appropriate annotation functions in order to
327+
// stop ASAN from reporting false positives. When ASAN is disabled, the
328+
// annotation function is a no-op.
329+
#ifndef _LIBCPP_HAS_NO_ASAN
330+
__sanitizer_annotate_contiguous_container(
331+
v.data(), v.data() + v.capacity(), v.data() + s, v.data() + n);
332+
#endif
333+
}
334+
335+
#define FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT(TYPE)
377336

378337
#elif defined(_GLIBCXX_VECTOR)
379338
// libstdc++

0 commit comments

Comments
 (0)