-
Notifications
You must be signed in to change notification settings - Fork 15k
Description
Some operations, like emplace()
, might overwrite bytes of unrelated objects (example from #68733 (comment)):
struct bool_with_padding {
alignas(8) bool b = false;
};
struct s : std::expected<bool_with_padding, bool> {
const bool please_dont_overwrite_me[6] = //
{true, true, true, true, true, true};
};
static_assert(sizeof(s) == sizeof(bool_with_padding));
int main() {
s s;
s.emplace();
assert(s.please_dont_overwrite_me[5]); // will abort
assert(s.please_dont_overwrite_me[4]);
assert(s.please_dont_overwrite_me[3]);
assert(s.please_dont_overwrite_me[2]);
assert(s.please_dont_overwrite_me[1]);
assert(s.please_dont_overwrite_me[0]);
}
This happens because currently, std::__libcppdatasize<std::expected<bool_with_padding, bool>>::value == 2
: One byte because of std::__libcppdatasize<bool_with_padding>::value == 1
and one additional byte for expected
's "has value flag.
However, the emplace()
operation destructs/constructs another bool_with_padding
in place -- and that will overwrite all sizeof(bool_with_padding) == 8
bytes of the object, clobbering the six unrelated bool
s that are stored in expected
's tail padding.
std::expected
has to make sure that all of its tail padding (if it has some) cannot be touched by any destructor/constructor calls of the value/error.
There is a PR up that has a work in progress fix and some more discussion: #69673