[cfe-dev] Is this a bug or just illegal code
Justin Bogner via cfe-dev
cfe-dev at lists.llvm.org
Thu Jun 8 11:18:59 PDT 2017
don hinton via cfe-dev <cfe-dev at lists.llvm.org> writes:
> I'm seeing memory corruption when I use a literal string and std::string in
> a ternary (?:) expression. If I change ss to ss.c_str() on line 5, the
> corruption goes away. [1] is an AST diff, which might help explain it.
>
> Although clang doesn't give me an error or warning, is this code legal, or
> is this a bug?
This code is illegal. What's happening is that both results of the ?:
expression have to have the same type, and "const char *" is implicitly
convertible to std::string (but not the other way around). Because of
this, you get a temporary std::string for "noexcept", the StringRef
binds to that temporary, and it immediately goes out of scope.
> thanks...
> don
>
>
> $ grep -n "." x.cpp
> 1:#include "llvm/ADT/StringRef.h"
> 3:int main() {
> 4: const std::string ss;
> 5: llvm::StringRef Ref = true ? "noexcept" : ss;
> 6: std::string s = Ref;
> 7: return 0;
> 8:}
>
> $ ../../4.0.0/build/Release/bin/clang++ x.cpp -O1 -g -fsanitize=address
> -fno-omit-frame-pointer -std=c++11 -I ../../llvm/include/ -I include
>
> $ ./a.out
> =================================================================
> ==14769==ERROR: AddressSanitizer: heap-use-after-free on address
> 0x604000000068 at pc 0x0000004651b2 bp 0x7ffcc5b3a7d0 sp 0x7ffcc5b39f80
> READ of size 8 at 0x604000000068 thread T0
> #0 0x4651b1 in __interceptor_memcpy.part.36
> /home/d80049854/projects/clang/4.0.0/llvm/projects/compilerr
> t/lib/asan/../sanitizer_common/sanitizer_common_interceptors.inc:655
> #1 0x7f00fe4d10ed (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0xc40ed)
> #2 0x7f00fe4d298a in std::basic_string<char, std::char_traits<char>,
> std::allocator<char> >::basic_string(char const*, unsigned long,
> std::allocator<char> const&) (/usr/lib/x86_64-linux-gnu/lib
> stdc++.so.6+0xc598a)
> #3 0x50d3bc in llvm::StringRef::str() const
> /home/d80049854/projects/clang/build/Debug/../../llvm/includ
> e/llvm/ADT/StringRef.h:230:14
> #4 0x50d2cd in llvm::StringRef::operator std::string() const
> /home/d80049854/projects/clang/build/Debug/../../llvm/includ
> e/llvm/ADT/StringRef.h:257:14
> #5 0x50d0aa in main /home/d80049854/projects/clang
> /build/Debug/x.cpp:6:19
> #6 0x7f00fd522f44 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so
> .6+0x21f44)
> #7 0x41a10b in _start (/home/d80049854/projects/clan
> g/build/Debug/a.out+0x41a10b)
>
> 0x604000000068 is located 24 bytes inside of 33-byte region
> [0x604000000050,0x604000000071)
> freed by thread T0 here:
> #0 0x5098b0 in operator delete(void*) /home/d80049854/projects/clang
> /4.0.0/llvm/projects/compiler-rt/lib/asan/asan_new_delete.cc:126
> #1 0x7f00fe4d11cd in std::basic_string<char, std::char_traits<char>,
> std::allocator<char> >::~basic_string() (/usr/lib/x86_64-linux-gnu/lib
> stdc++.so.6+0xc41cd)
> #2 0x7f00fd522f44 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so
> .6+0x21f44)
>
> previously allocated by thread T0 here:
> #0 0x508b70 in operator new(unsigned long)
> /home/d80049854/projects/clang/4.0.0/llvm/projects/compiler-
> rt/lib/asan/asan_new_delete.cc:82
> #1 0x7f00fe4d0f68 in std::string::_Rep::_S_create(unsigned long,
> unsigned long, std::allocator<char> const&) (/usr/lib/x86_64-linux-gnu/lib
> stdc++.so.6+0xc3f68)
>
> SUMMARY: AddressSanitizer: heap-use-after-free
> /home/d80049854/projects/clang/4.0.0/llvm/projects/compiler-
> rt/lib/asan/../sanitizer_common/sanitizer_common_interceptors.inc:655 in
> __interceptor_memcpy.part.36
> Shadow bytes around the buggy address:
> 0x0c087fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> 0x0c087fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> 0x0c087fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> 0x0c087fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> 0x0c087fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> =>0x0c087fff8000: fa fa 00 00 00 00 00 01 fa fa fd fd fd[fd]fd fa
> 0x0c087fff8010: fa fa 00 00 00 00 01 fa fa fa fa fa fa fa fa fa
> 0x0c087fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
> 0x0c087fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
> 0x0c087fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
> 0x0c087fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
> Shadow byte legend (one shadow byte represents 8 application bytes):
> Addressable: 00
> Partially addressable: 01 02 03 04 05 06 07
> Heap left redzone: fa
> Freed heap region: fd
> Stack left redzone: f1
> Stack mid redzone: f2
> Stack right redzone: f3
> Stack after return: f5
> Stack use after scope: f8
> Global redzone: f9
> Global init order: f6
> Poisoned by user: f7
> Container overflow: fc
> Array cookie: ac
> Intra object redzone: bb
> ASan internal: fe
> Left alloca redzone: ca
> Right alloca redzone: cb
> ==14769==ABORTING
>
> 1. AST diff:
> < uses ss
>> uses ss.c_str()
>
> 49566c49566
> < | | |-CXXConstructorDecl 0xXXXXXXX <line:3010:7, col:XX> col:XX used
> basic_string 'void (const char *, const class std::allocator<char> &)'
> ---
>> | | |-CXXConstructorDecl 0xXXXXXXX <line:3010:7, col:XX> col:XX
> basic_string 'void (const char *, const class std::allocator<char> &)'
> 49568,49573c49568
> < | | | `-ParmVarDecl 0xXXXXXXX <col:XX, col:XX> col:XX __a 'const class
> std::allocator<char> &' cinit
> < | | | `-ExprWithCleanups 0xXXXXXXX <col:XX, col:XX> 'const class
> std::allocator<char>':'const class std::allocator<char>' lvalue
> < | | | `-MaterializeTemporaryExpr 0xXXXXXXX <col:XX, col:XX> 'const
> class std::allocator<char>':'const class std::allocator<char>' lvalue
> < | | | `-ImplicitCastExpr 0xXXXXXXX <col:XX, col:XX> 'const class
> std::allocator<char>':'const class std::allocator<char>' <NoOp>
> < | | | `-CXXBindTemporaryExpr 0xXXXXXXX <col:XX, col:XX> 'class
> std::allocator<char>':'class std::allocator<char>' (CXXTemporary 0xXXXXXXX)
> < | | | `-CXXTemporaryObjectExpr 0xXXXXXXX <col:XX, col:XX>
> 'class std::allocator<char>':'class std::allocator<char>' 'void (void)
> throw()'
> ---
>> | | | `-ParmVarDecl 0xXXXXXXX <col:XX, col:XX> col:XX __a 'const class
> std::allocator<char> &'
> 108945c108940
> < | | |-CXXConstructorDecl 0xXXXXXXX
> <../../llvm/include/llvm/Support/Compiler.h:194:38,
> ../../llvm/include/llvm/ADT/StringRef.h:96:49> line:95:18 used StringRef
> 'void (const std::string &)'
> ---
>> | | |-CXXConstructorDecl 0xXXXXXXX
>> | | | <../../llvm/include/llvm/Support/Compiler.h:194:38,
> ../../llvm/include/llvm/ADT/StringRef.h:96:49> line:95:18 StringRef 'void
> (const std::string &)'
> 111747,111765c111742,111749
> < | `-CXXConstructExpr 0xXXXXXXX <col:XX, col:XX>
> 'llvm::StringRef':'class llvm::StringRef' 'void (const std::string &)'
> < | `-MaterializeTemporaryExpr 0xXXXXXXX <col:XX, col:XX>
> 'const std::string':'const class std::basic_string<char>' lvalue
> < | `-ConditionalOperator 0xXXXXXXX <col:XX, col:XX>
> 'const std::string':'const class std::basic_string<char>'
> < | |-CXXBoolLiteralExpr 0xXXXXXXX <col:XX> '_Bool' true
> < | |-CXXBindTemporaryExpr 0xXXXXXXX <col:XX> 'const
> std::string':'const class std::basic_string<char>' (CXXTemporary 0xXXXXXXX)
> < | | `-CXXConstructExpr 0xXXXXXXX <col:XX> 'const
> std::string':'const class std::basic_string<char>' 'void (const class
> std::basic_string<char> &)' elidable
> < | | `-MaterializeTemporaryExpr 0xXXXXXXX <col:XX>
> 'const std::string':'const class std::basic_string<char>' lvalue
> < | | `-CXXBindTemporaryExpr 0xXXXXXXX <col:XX>
> 'const std::string':'const class std::basic_string<char>' (CXXTemporary
> 0xXXXXXXX)
> < | | `-CXXConstructExpr 0xXXXXXXX <col:XX>
> 'const std::string':'const class std::basic_string<char>' 'void (class
> std::basic_string<char> &&) noexcept' elidable
> < | | `-MaterializeTemporaryExpr 0xXXXXXXX
> <col:XX> 'std::string':'class std::basic_string<char>' xvalue
> < | | `-CXXBindTemporaryExpr 0xXXXXXXX
> <col:XX> 'std::string':'class std::basic_string<char>' (CXXTemporary
> 0xXXXXXXX)
> < | | `-ImplicitCastExpr 0xXXXXXXX <col:XX>
> 'std::string':'class std::basic_string<char>' <ConstructorConversion>
> < | | `-CXXConstructExpr 0xXXXXXXX
> <col:XX> 'std::string':'class std::basic_string<char>' 'void (const char *,
> const class std::allocator<char> &)'
> < | | |-ImplicitCastExpr 0xXXXXXXX
> <col:XX> 'const char *' <ArrayToPointerDecay>
> < | | | `-StringLiteral 0xXXXXXXX
> <col:XX> 'const char [9]' lvalue "noexcept"
> < | | `-CXXDefaultArgExpr 0xXXXXXXX
> <<invalid sloc>> 'const class std::allocator<char>':'const class
> std::allocator<char>' lvalue
> < | `-CXXBindTemporaryExpr 0xXXXXXXX <col:XX> 'const
> std::string':'const class std::basic_string<char>' (CXXTemporary 0xXXXXXXX)
> < | `-CXXConstructExpr 0xXXXXXXX <col:XX> 'const
> std::string':'const class std::basic_string<char>' 'void (const class
> std::basic_string<char> &)'
> < | `-DeclRefExpr 0xXXXXXXX <col:XX> 'const
> std::string':'const class std::basic_string<char>' lvalue Var 0xXXXXXXX
> 'ss' 'const std::string':'const class std::basic_string<char>'
> ---
>> | `-CXXConstructExpr 0xXXXXXXX <col:XX, col:XX>
> 'llvm::StringRef':'class llvm::StringRef' 'void (const char *)'
>> | `-ConditionalOperator 0xXXXXXXX <col:XX, col:XX> 'const
> char *'
>> | |-CXXBoolLiteralExpr 0xXXXXXXX <col:XX> '_Bool' true
>> | |-ImplicitCastExpr 0xXXXXXXX <col:XX> 'const char *'
> <ArrayToPointerDecay>
>> | | `-StringLiteral 0xXXXXXXX <col:XX> 'const char [9]'
> lvalue "noexcept"
>> | `-CXXMemberCallExpr 0xXXXXXXX <col:XX, col:XX> 'const
> char *'
>> | `-MemberExpr 0xXXXXXXX <col:XX, col:XX> '<bound
> member function type>' .c_str 0xXXXXXXX
>> | `-DeclRefExpr 0xXXXXXXX <col:XX> 'const
> std::string':'const class std::basic_string<char>' lvalue Var 0xXXXXXXX
> 'ss' 'const std::string':'const class std::basic_string<char>'
> _______________________________________________
> cfe-dev mailing list
> cfe-dev at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
More information about the cfe-dev
mailing list