[llvm-bugs] [Bug 48993] New: Recent shared_ptr storage change (https://reviews.llvm.org/D91201) causes CFI cast failures during make_shared

via llvm-bugs llvm-bugs at lists.llvm.org
Mon Feb 1 15:15:57 PST 2021


https://bugs.llvm.org/show_bug.cgi?id=48993

            Bug ID: 48993
           Summary: Recent shared_ptr storage change
                    (https://reviews.llvm.org/D91201) causes CFI cast
                    failures during make_shared
           Product: libc++
           Version: unspecified
          Hardware: PC
                OS: All
            Status: NEW
          Severity: enhancement
          Priority: P
         Component: All Bugs
          Assignee: unassignedclangbugs at nondot.org
          Reporter: rnk at google.com
                CC: llvm-bugs at lists.llvm.org, mclow.lists at gmail.com

Consider this program that creates and destroys a shared_ptr object:

$ cat t.cpp 
#include <memory>
struct Foo {
  Foo() {}
  virtual ~Foo() {}
};
int main(int argc, char **argv) { std::make_shared<Foo>(); }

If you compile libc++, lld, clang, cfi, ubsan, etc, at ToT and build this
program like so, it results in CFI cast errors:

$ ./bin/clang++  t.cpp  -flto=thin -fuse-ld=lld -stdlib=libc++  -fsanitize=cfi
-fno-sanitize-trap  -fvisibility=hidden && LD_LIBRARY_PATH=lib  ./a.out   ;
echo $?
/usr/local/google/home/rnk/llvm-project/build/bin/../include/c++/v1/memory:2653:27:
runtime error: control flow integrity check for type 'Foo' failed during cast
to unrelated type (vtable address 0x000000000000)
0x000000000000: note: invalid vtable
<memory cannot be printed>
/usr/local/google/home/rnk/llvm-project/build/bin/../include/c++/v1/memory:2653:27:
note: check failed in /usr/local/google/home/rnk/llvm-project/build/a.out,
vtable located in (unknown)
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior
/usr/local/google/home/rnk/llvm-project/build/bin/../include/c++/v1/memory:2653:27
in 
1

Reverting 955dd7b7f3f6d / https://reviews.llvm.org/D91201 locally and
rebuilding libc++ to copy the headers makes the problem go away:

$ git revert 955dd7b7f3f6d
Removing
libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared_construct.pass.cpp
Removing
libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/libcxx.control_block_layout.pass.cpp
Auto-merging libcxx/include/memory
[main 5f7ab320457d] Revert "[libc++] LWG2070: Use Allocator construction for
objects created with allocate_shared"
 4 files changed, 19 insertions(+), 429 deletions(-)
 delete mode 100644
libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/libcxx.control_block_layout.pass.cpp
 delete mode 100644
libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared_construct.pass.cpp

$ ninja cxx
[1 processes, 11/11 @ 17.6/s : 0.626s ] Creating library symlink
lib/libc++.so.1 lib/libc++.so

$ ./bin/clang++  t.cpp  -flto=thin -fuse-ld=lld -stdlib=libc++  -fsanitize=cfi
-fno-sanitize-trap  -fvisibility=hidden && LD_LIBRARY_PATH=lib  ./a.out   ;
echo $?
0


These are the lines of the change that matter:

     _LIBCPP_HIDE_FROM_ABI
     explicit __shared_ptr_emplace(_Alloc __a, _Args&& ...__args)
-#ifndef _LIBCPP_CXX03_LANG
-        : __data_(piecewise_construct, _VSTD::forward_as_tuple(__a),
-                  _VSTD::forward_as_tuple(_VSTD::forward<_Args>(__args)...))
+        : __storage_(_VSTD::move(__a))
+    {
+#if _LIBCPP_STD_VER > 17
+        using _TpAlloc = typename __allocator_traits_rebind<_Alloc,
_Tp>::type;
+        _TpAlloc __tmp(*__get_alloc());
+        allocator_traits<_TpAlloc>::construct(__tmp, __get_elem(),
_VSTD::forward<_Args>(__args)...);
 #else
-        : __data_(__a, _Tp(_VSTD::forward<_Args>(__args)...))
+        ::new ((void*)__get_elem()) _Tp(_VSTD::forward<_Args>(__args)...);
 #endif

That change switches from calling the __compressed_pair constructor directly to
using placement new on the result of __get_elem. Inside __get_elem, the address
of the second pair element is cast to the type that will be constructed. That
reinterpret_cast on the memory before construction is the issue: CFI detects
cast confusion bugs by checking that the vtable is correct for the type of the
cast, but the vptr slot is still uninitialized.

-- 
You are receiving this mail because:
You are on the CC list for the bug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-bugs/attachments/20210201/183b6430/attachment.html>


More information about the llvm-bugs mailing list