Skip to content

[libc++] std::expected: operations may overwrite bytes of unrelated objects #70494

@jiixyj

Description

@jiixyj

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 bools 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

Metadata

Metadata

Assignees

Labels

ABIApplication Binary Interfacelibc++libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions