[llvm-bugs] [Bug 35431] New: CFI (Control Flow Integrity) emits invalid checks when using std::make_shared (C++11)

via llvm-bugs llvm-bugs at lists.llvm.org
Mon Nov 27 04:20:50 PST 2017


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

            Bug ID: 35431
           Summary: CFI (Control Flow Integrity) emits invalid checks when
                    using std::make_shared (C++11)
           Product: clang
           Version: trunk
          Hardware: PC
                OS: Linux
            Status: NEW
          Severity: normal
          Priority: P
         Component: -New Bugs
          Assignee: unassignedclangbugs at nondot.org
          Reporter: adrien at guinet.me
                CC: llvm-bugs at lists.llvm.org

Created attachment 19477
  --> https://bugs.llvm.org/attachment.cgi?id=19477&action=edit
test case

Hello all,

CFI (Control Flow Integrity) generates bad code when std::make_shared is used
for C++11 code. The bad code involve a comparaison which will always fail, and
jump to the "ud2" instruction (on X86), as if an invalid call (in the CFI
sense) would have been made.

Using the joint a.cpp file, one can reproduce this using current LLVM trunk
(r319014). The bug happens using GNU's libstdc++, with full or thin lto:

$ build/bin/clang++ -fsanitize=cfi -fvisibility=hidden -flto=full -O2 a.cpp 
-fPIC -o a_full -std=c++11 && ./a_full
Illegal instruction
$ build/bin/clang++ -fsanitize=cfi -fvisibility=hidden -flto=thin -O2 a.cpp 
-fPIC -o a_thin -std=c++11 && ./a_thin
Illegal instruction

If we take a look at the assembly generated in both cases, it looks like this:

- for a_full:
400850 new_ptr2(void)  proc near
400850   push    rbx 
400851   mov     rbx, rdi 
400854   mov     edi, 28h 
400859   call    operator new(ulong)
40085E   mov     rcx, 100000001h
400868   mov     [rax+8], rcx 
40086C   mov     qword ptr [rax], offset off_400C80
400873   mov     ecx, offset off_400CC0
400878   cmp     [rax+10h], rcx <-- memory at rax+0x10 has never been
initialized, this equality is always false!
40087C   jnz     short loc_40089D
40087E   mov     rcx, rax 
400881   add     rcx, 10h 
400885   xorps   xmm0, xmm0
400888   movups  xmmword ptr [rax+18h], xmm0
40088C   mov     qword ptr [rax+10h], offset off_400CC0
400894   mov     [rbx], rcx 
400897   mov     [rbx+8], rax 
40089B   pop     rbx 
40089C   retn

- for a_thin:
400850 new_ptr2(void)  proc near
400850    push    rbx
400851    mov     rbx, rdi     
400854    mov     edi, 28h     
400859    call    operator new(ulong)
40085E    mov     rcx, 100000001h
400868    mov     [rax+8], rcx 
40086C    mov     qword ptr [rax], offset
__typeid__ZTSSt23_Sp_counted_ptr_inplaceI2S2SaIS0_ELN9__gnu_cxx12_Lock_policyE2EE_global_addr400873
   mov     ecx, offset
__typeid__ZTSSt23_Sp_counted_ptr_inplaceI2S2SaIS0_ELN9__gnu_cxx12_Lock_policyE2EE_global_addr
400878    mov     edx, offset
__typeid__ZTSSt23_Sp_counted_ptr_inplaceI2S2SaIS0_ELN9__gnu_cxx12_Lock_policyE2EE_global_addr
40087D    cmp     rdx, rcx
400880    jnz     short loc_4008BE
400882    mov     ecx, offset __typeid__ZTS4Base_global_addr
400887    cmp     [rax+10h], rcx <-- same here, memory at rax+0x10 has never
been initialized, this equality is always false!
40088B    jnz     short loc_4008BE
40088D    xorps   xmm0, xmm0   
400890    movups  xmmword ptr [rax+18h], xmm0 
400894    mov     qword ptr [rax+10h], offset __typeid__ZTS4Base_global_addr
40089C    mov     ecx, offset __typeid__ZTS4Base_global_addr
4008A1    mov     edx, offset __typeid__ZTS4Base_global_addr
4008A6    cmp     rdx, rcx
4008A9    jnz     short loc_4008BE
4008AB    mov     rcx, rax     
4008AE    add     rcx, 10h     
4008B2    mov     [rbx], rcx   
4008B5    mov     [rbx+8], rax 
4008B9    mov     rax, rbx     
4008BC    pop     rbx
4008BD    retn


For the record, this does not happen using LLVM's libc++:
$ build/bin/clang++ -fsanitize=cfi -fvisibility=hidden -flto=full -O2 a.cpp 
-fPIC -o a -std=c++11 -stdlib=libc++ && ./a
S2
$ build/bin/clang++ -fsanitize=cfi -fvisibility=hidden -flto=thin -O2 a.cpp 
-fPIC -o a -std=c++11 -stdlib=libc++ && ./a
S2

Question is to know whether this is due to some bug in libstdc++ or a bug in
CFI. Note that using the official LLVM 5 release, the bug happens for both C++
libraries, which might point to an actual CFI bug.

For the record, this bug has been found while building Clang/LLVM using clang
with CFI. This kind of construction (using std::make_shared) exists for
instance in clang's RealFileSystem::dir_begin function (in
clang/lib/Basic/VirtualFileSystem.cpp).

Please let me know if you need further information.

Regards,

-- 
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/20171127/a502dd90/attachment.html>


More information about the llvm-bugs mailing list