This is an archive of the discontinued LLVM Phabricator instance.

[libc++] Use addressof in assignment operator.
ClosedPublic

Authored by Mordante on Sep 30 2021, 10:04 AM.

Details

Summary

Replace &__rhs with _VSTD::addressof(__rhs) to guard against ADL hijacking
of operator& in operator=. Thanks to @CaseyCarter for bringing it to our
attention.

Similar issues with hijacking operator& still exist, they will be
addressed separately.

Diff Detail

Event Timeline

Mordante requested review of this revision.Sep 30 2021, 10:04 AM
Mordante created this revision.
Herald added a project: Restricted Project. · View Herald TranscriptSep 30 2021, 10:04 AM
Herald added a reviewer: Restricted Project. · View Herald Transcript
jloser added a subscriber: jloser.Sep 30 2021, 10:16 AM
jloser added inline comments.
libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.cons/assign_copy.compile.pass.cpp
24

Should we explicitly #include <vector>? Do we even need to provide our own container template (I think using the default of std::deque is fine, right?)?

libcxx/test/support/deleted_operator_ampersand.h
30

Nit: west const in the function argument list to be consistent with use in the rest of the file.

CaseyCarter added inline comments.Sep 30 2021, 10:22 AM
libcxx/test/support/deleted_operator_ampersand.h
19

typo: "additional"

29

#include<functional>?

30

#include<cstddef>?

Quuxplusone requested changes to this revision.Sep 30 2021, 10:35 AM
Quuxplusone added a subscriber: Quuxplusone.
Quuxplusone added inline comments.
libcxx/test/std/containers/associative/map/map.cons/copy_assign.compile.pass.cpp
23

The pattern I use for "ADL-proofing tests" is find libcxx/test/ -name robust_against_adl.pass.cpp:

struct Incomplete;
template<class T> struct Holder { T t; };

std::map<int, Holder<Incomplete>*> m;
m = m;

However, the problem here is that std::map actually is not fully ADL-proof, and cannot be made fully ADL-proof, because it relies on comparing allocator<pair<int, Holder<Incomplete>*>> objects with ADL operator!=. So I guess the deleted-specific-function version is the best we can do.

27–29

All of these tests can be one line shorter.

std::map<int, deleted_operator_ampersand> x;
x = x;
libcxx/test/std/containers/container.adaptors/queue/queue.defn/assign_copy.compile.pass.cpp
10

Throughout: Why is this unsupported in '03?
...oh, because of the =delete? Clang supports =delete in '03, so we can go ahead and test it. This will shorten most of these test files by another 2 lines each.

libcxx/test/support/deleted_operator_ampersand.h
19

additional

23–24

Let's delete the rest of the evil operators while we're here.

template<class T> friend void operator&(T&&) = delete;
template<class T, class U> friend void operator,(T&&, U&&) = delete;
template<class T, class U> friend void operator&&(T&&, U&&) = delete;
template<class T, class U> friend void operator||(T&&, U&&) = delete;

You can call the class something like class OperatorHijacker or class EvilOperators to keep the lines a bit shorter at the call-sites.

27–32
template<>
struct std::hash<WhateverName> {
    size_t operator()(const WhateverName&) const { return 0; }
};

(Never reopen namespace std from user code. Don't put noexcept cruft except where the library needs it.)

This revision now requires changes to proceed.Sep 30 2021, 10:35 AM
Mordante planned changes to this revision.Sep 30 2021, 11:46 AM

Thanks for the feedback. At the moment it seems the pre-merge checks are disable. So I'll fix it when the CI is in the air again.

Mordante updated this revision to Diff 376980.Oct 4 2021, 12:05 PM

Rebase to trigger CI.

Mordante planned changes to this revision.Oct 4 2021, 12:07 PM
Mordante marked 10 inline comments as done.Oct 5 2021, 10:17 AM
Mordante added inline comments.
libcxx/test/std/containers/associative/map/map.cons/copy_assign.compile.pass.cpp
27–29

Actually they can't this triggers a self-assignment warning. So I prefer to keep it as is.

libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.cons/assign_copy.compile.pass.cpp
24

std::vector is the default and yes we should include <vector>, instead I use std::queue which can be used without an additional include.

libcxx/test/std/containers/container.adaptors/queue/queue.defn/assign_copy.compile.pass.cpp
10

Indeed because of C++03, thanks for reminding that we can use =delete in Clang in C++03.

Mordante updated this revision to Diff 377290.Oct 5 2021, 10:18 AM
Mordante marked 3 inline comments as done.

Addresses review comments.
Disable failing debug iterator tests.

LGTM % priority_queue.

libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.cons/assign_copy.compile.pass.cpp
24

std::queue isn't even a container, let alone a random-access container. I agree with @jloser: we don't need to specify a container here. Just std::priority_queue<operator_hijacker> should be fine.

ldionne requested changes to this revision.Oct 5 2021, 11:14 AM
ldionne added a subscriber: ldionne.

Thanks for tackling this! LGTM but I do have a few questions.

libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/assign_copy.compile.pass.cpp
10

Why does this test require the debug mode?

libcxx/test/std/containers/sequences/list/list.cons/assign_copy.compile.pass.cpp
10

Can you add a comment explaining why that fails? Applies elsewhere too.

libcxx/test/support/operator_hijacker.h
12 ↗(On Diff #377290)

Generally this goes after including the libc++ headers.

This revision now requires changes to proceed.Oct 5 2021, 11:14 AM
libcxx/test/std/containers/sequences/list/list.cons/assign_copy.compile.pass.cpp
10

Good catches! I didn't notice these new XFAILs; I also don't see any reason they should be needed. I assume the problem has to do with __wrap_iter not being sufficiently ADL-proof? But then we should fix that too.

Mordante updated this revision to Diff 377572.Oct 6 2021, 9:28 AM
Mordante marked 5 inline comments as done.

Addresses review comments.

Mordante added inline comments.Oct 6 2021, 9:52 AM
libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/assign_copy.compile.pass.cpp
10

It doesn't I noticed it before uploading the changes yesterday, but seems I forgot to hit the save button.

libcxx/test/std/containers/sequences/list/list.cons/assign_copy.compile.pass.cpp
10

They fail since the debug iterators also use operator&. As stated in the original commit message; there will be follow-up commits. I prefer to keep reviews small. I'll add comments.

ldionne accepted this revision.Oct 6 2021, 11:00 AM

Awesome, thanks for the patch.

Can you rename the new tests to e.g. assign_copy.addressof.compile.pass.cpp so we can tell at a glance that it's testing some sort of addressof-related issue, or operator highjacking?

This revision is now accepted and ready to land.Oct 6 2021, 11:00 AM
This revision was automatically updated to reflect the committed changes.

Renamed the tests as requested.