This is an archive of the discontinued LLVM Phabricator instance.

Avoid cast<T*> before T is constructed to pacify CFI checks
ClosedPublic

Authored by rnk on Feb 1 2021, 3:20 PM.

Details

Summary

Fixes usage of shared_ptr with CFI enabled, which is llvm.org/pr48993.

Diff Detail

Event Timeline

rnk requested review of this revision.Feb 1 2021, 3:20 PM
rnk created this revision.
Herald added a project: Restricted Project. · View Herald TranscriptFeb 1 2021, 3:20 PM
Herald added a reviewer: Restricted Project. · View Herald Transcript

Ah, I just commented on the bug report, but your patch confirms what I thought. Thanks for the patch. So this is basically a CFI false positive, do we agree?

Another approach would be:

template <class _Pointer = _Tp*>
_Pointer __get_elem() _NOEXCEPT {
    _CompressedPair *__as_pair = reinterpret_cast<_CompressedPair*>(__blob_);
    typename _CompressedPair::_Base2* __second = _CompressedPair::__get_second_base(__as_pair);
    _Pointer __elem = reinterpret_cast<_Pointer>(__second);
    return __elem;
}

Then, we can use it as __get_elem<void*>() for the placement-new use case, and just __get_elem() for the normal case. What do you think?

libcxx/include/memory
2656

Indentation looks off.

rnk planned changes to this revision.Feb 1 2021, 3:54 PM

Ah, I just commented on the bug report, but your patch confirms what I thought. Thanks for the patch. So this is basically a CFI false positive, do we agree?

I think the code is fine, but I'm not 100% sure it isn't technically UB. I mean, I'd write it.

Then, we can use it as __get_elem<void*>() for the placement-new use case, and just __get_elem() for the normal case. What do you think?

That works, I tried it, but I realized the C++17+ cases are still broken.

libcxx/include/memory
2594

I realize that the same issue exists here. However, the prorotype of C++17 std::construct_at expects a _Tp* argument, not void*, so this change is really fighting uphill against future standards versions, and that makes me reconsider the whole effort. I guess I'll hold off on this change for now and go forward with the exclusion.

We could add _LIBCPP_NO_CFI on the problematic method, like we do for std::addressof. Can you try that out?

rnk updated this revision to Diff 320871.Feb 2 2021, 11:53 AM
  • use attribute to disable CFI
rnk added a comment.Feb 2 2021, 11:56 AM

We could add _LIBCPP_NO_CFI on the problematic method, like we do for std::addressof. Can you try that out?

Yep, it works.

I haven't put together a test for this. Are there existing tests for CFI or sanitizers that I could use to build a test for this?

ldionne accepted this revision.Feb 2 2021, 12:05 PM
In D95827#2537371, @rnk wrote:

We could add _LIBCPP_NO_CFI on the problematic method, like we do for std::addressof. Can you try that out?

Yep, it works.

I haven't put together a test for this. Are there existing tests for CFI or sanitizers that I could use to build a test for this?

No, I don't think we have any. I see two options:

  1. Add a buildbot that runs with CFI enabled.
  2. Add a single .sh.cpp test that runs this specifically.

I think option (1) would be the best. Is it sufficient to add -fsanitize=cfi -flto when compiling a test file to reproduce the issue? If so, I think we could simply add a new CFI sanitizer configuration to Lit that does that, and add a corresponding job to run the whole test suite with CFI enabled. We can tackle that as a separate effort.

This revision is now accepted and ready to land.Feb 2 2021, 12:05 PM
This revision was landed with ongoing or failed builds.Feb 2 2021, 12:39 PM
This revision was automatically updated to reflect the committed changes.
rnk added a comment.Feb 2 2021, 12:43 PM

I think option (1) would be the best. Is it sufficient to add -fsanitize=cfi -flto when compiling a test file to reproduce the issue? If so, I think we could simply add a new CFI sanitizer configuration to Lit that does that, and add a corresponding job to run the whole test suite with CFI enabled. We can tackle that as a separate effort.

Makes sense to me. I think CFI also requires -fvisibility=hidden to make its vtable checks work. -fno-sanitize-trap makes CFI violations produce diagnostics instead of simply trapping, so you would want to include it in a test config.