Commit 431c16c3 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook GitHub Bot

fix F14 fallback find() for stateful key-equal

Summary:
The fallback implementation of F14 does a clever trick in its implementation of `find()` to support heterogeneous lookup. But this trick is UB and its implementation assumes that the key-equal type is empty, not specially-aligned, and not final. Relax these constraints.

As it turns out, fbthrift streaming uses an F14 map with a stateful key-equal, triggering this UB in the fallback implementation: `StreamMap` uses key-equal type `StreamMapEquals`, which is nonempty and has a `StreamIdResolver` field, which itself is empty but which causes `StreamMapEquals` not to be empty. The simplest fix is, of course, to switch to using `StreamIdResolver` as a base, but this would be a bug in the F14 fallback regardless.

`BottomKeyEqual` must have the same size, alignment, emptiness, and finality as `KeyEqual`.

Reviewed By: aary

Differential Revision: D26905527

fbshipit-source-id: 023feb3963fc7b2e779fb591e737ea4b70ded476
parent 0efd32c4
......@@ -151,12 +151,39 @@ class F14BasicSet
//// PUBLIC - Lookup
private:
template <typename K>
struct BottomKeyEqual {
// BottomKeyEqual must have same size, alignment, emptiness, and finality as
// KeyEqual
struct BottomKeyEqualEmpty {};
template <size_t S, size_t A>
struct BottomKeyEqualNonEmpty {
alignas(A) char data[S];
};
using BottomKeyEqualBase = conditional_t<
std::is_empty<KeyEqual>::value,
BottomKeyEqualEmpty,
BottomKeyEqualNonEmpty<sizeof(KeyEqual), alignof(KeyEqual)>>;
template <bool IsFinal, typename K>
struct BottomKeyEqualCond : BottomKeyEqualBase {
[[noreturn]] bool operator()(K const&, K const&) const {
assume_unreachable();
}
};
template <typename K>
struct BottomKeyEqualCond<true, K> final : BottomKeyEqualCond<false, K> {};
template <typename K>
using BottomKeyEqual = BottomKeyEqualCond<
std::is_final<KeyEqual>::value || std::is_union<KeyEqual>::value,
K>;
using BottomTest = BottomKeyEqual<char>;
static_assert(sizeof(BottomTest) == sizeof(KeyEqual), "mismatch size");
static_assert(alignof(BottomTest) == alignof(KeyEqual), "mismatch align");
static_assert(
std::is_empty<BottomTest>::value == std::is_empty<KeyEqual>::value,
"mismatch is-empty");
static_assert(
(std::is_final<BottomTest>::value || std::is_union<BottomTest>::value) ==
(std::is_final<KeyEqual>::value || std::is_union<KeyEqual>::value),
"mismatch is-final");
template <typename Iter, typename LocalIter>
static std::
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment