llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
28.61k stars 11.82k forks source link

ASAN does not detect use-after-free for small strings #101955

Open usx95 opened 2 months ago

usx95 commented 2 months ago

https://godbolt.org/z/dj8K3xb17

#include <string>
#include <iostream>
#include <string_view>

struct MyThing {
    std::string_view s;
};

MyThing makeThing(std::string s) { // Use after return after the function call.
    return MyThing{s};
}

std::string getS() { return std::string("abcdabcdabcdabc"); }

int main() {
    auto thing = makeThing(getS());
    std::cout << thing.s << std::endl;
    return 0;
}

Is this a known false negative ? Works fine with strings with a length >=16. https://godbolt.org/z/8cGT7b38n

AdvenamTacet commented 2 months ago

Right now, string annotations are enabled only with libc++ compiled with ASan, and it looks like [it's not the case on godbolt](https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(filename:'1',fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,selection:(endColumn:2,endLineNumber:11,positionColumn:2,positionLineNumber:11,selectionStartColumn:2,selectionStartLineNumber:11,startColumn:2,startLineNumber:11),source:'%23include+%3Cstring%3E%0A%23include+%3Ciostream%3E%0A%0A%0Aint+main()+%7B%0A++++std::string+dst,+src+%3D+%221234567890123456789012345678901234567890long%22%3B%0A++++dst.reserve(src.size()+%2B+30)%3B%0A++++dst+%3D+src%3B%0A++++std::cout+%3C%3C+%22c:+%22+%3C%3C+dst%5Bdst.size()+%2B+4%5D+%3C%3C+std::endl%3B%0A++++return+0%3B%0A%7D'),l:'5',n:'1',o:'C%2B%2B+source+%231',t:'0')),k:38.53075642514707,l:'4',m:100,n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:clang_trunk,filters:(b:'0',binary:'1',binaryObject:'1',commentOnly:'0',debugCalls:'1',demangle:'0',directives:'0',execute:'0',intel:'0',libraryCode:'0',trim:'1',verboseDemangling:'0'),flagsViewOpen:'1',fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,libs:!((name:fmt,ver:'811')),options:'-Wextra+-Wall+-std%3Dc%2B%2B20+-fsanitize%3Daddress+-stdlib%3Dlibc%2B%2B+-O3',overrides:!(),selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'+x86-64+clang+(trunk)+(Editor+%231)',t:'0')),k:32.360514748877364,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:executor,i:(argsPanelShown:'1',compilationPanelShown:'0',compiler:clang_trunk,compilerName:'',compilerOutShown:'0',execArgs:'',execStdin:'',fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,libs:!(),options:'-fsanitize%3Daddress+-stdlib%3Dlibc%2B%2B+-std%3Dc%2B%2B20',overrides:!(),runtimeTools:!(),source:1,stdinPanelShown:'1',wrap:'1'),l:'5',n:'0',o:'Executor+x86-64+clang+(trunk)+(C%2B%2B,+Editor+%231)',t:'0')),k:29.10872882597559,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4). UAF from the example should be detected with string annotations, but I will confirm it.

However, container annotations are not necessary to detect UAF as shown here in case of a long string (when external buffer for content is allocated, >=16 for libstdc++ and >=23 for [libc++](https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(filename:'1',fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,selection:(endColumn:65,endLineNumber:13,positionColumn:65,positionLineNumber:13,selectionStartColumn:65,selectionStartLineNumber:13,startColumn:65,startLineNumber:13),source:'%23include+%3Cstring%3E%0A%23include+%3Ciostream%3E%0A%23include+%3Cstring_view%3E%0A%0Astruct+MyThing+%7B%0A++++std::string_view+s%3B%0A%7D%3B%0A%0AMyThing+makeThing(std::string+s)+%7B%0A++++return+MyThing%7Bs%7D%3B%0A%7D%0A%0Astd::string+getS()+%7B+return+std::string(%22abcdabcdabcdabcd1231234%22)%3B+%7D%0A%0Aint+main()+%7B%0A++++auto+thing+%3D+makeThing(getS())%3B%0A++++std::cout+%3C%3C+thing.s+%3C%3C+std::endl%3B%0A++++return+0%3B%0A%7D'),l:'5',n:'1',o:'C%2B%2B+source+%231',t:'0')),k:34.12532858401412,l:'4',m:100,n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:clang_trunk,filters:(b:'0',binary:'1',binaryObject:'1',commentOnly:'0',debugCalls:'1',demangle:'0',directives:'0',execute:'0',intel:'0',libraryCode:'0',trim:'1',verboseDemangling:'0'),flagsViewOpen:'1',fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,libs:!((name:fmt,ver:'811')),options:'-Wextra+-Wall+-std%3Dc%2B%2B20+-fsanitize%3Daddress+-stdlib%3Dlibc%2B%2B',overrides:!(),selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'+x86-64+clang+(trunk)+(Editor+%231)',t:'0')),k:31.490414230772725,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:tool,i:(args:'-checks%3Dclang-analyzer-*',argsPanelShown:'1',editorid:1,fontScale:14,fontUsePx:'0',j:1,monacoEditorHasBeenAutoOpened:'1',monacoEditorOpen:'1',monacoStdin:'1',stdin:'',stdinPanelShown:'1',toolId:clangtidytrunk,wrap:'1'),l:'5',n:'0',o:'clang-tidy+(trunk)+x86-64+clang+(trunk)+(Editor+%231,+Compiler+%231)',t:'0')),k:1.1794162633248197,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:output,i:(compilerName:'x86-64+clang+15.0.0',editorid:1,fontScale:14,fontUsePx:'0',j:1,wrap:'1'),l:'5',n:'0',o:'Output+of+x86-64+clang+(trunk)+(Compiler+%231)',t:'0')),k:33.20484092188836,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4)), because external memory is freed in the destructor, the error is reported.

In case of a short string, content is inside the object, so nothing is freed in the destructor. I believe (to be confirmed) it's not detected here, because the string is created in mains' stack frame and the frame is not yet freed. If getS() is called in another function, [UAF is detected](https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(filename:'1',fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,selection:(endColumn:2,endLineNumber:19,positionColumn:2,positionLineNumber:19,selectionStartColumn:2,selectionStartLineNumber:19,startColumn:2,startLineNumber:19),source:'%23include+%3Cstring%3E%0A%23include+%3Ciostream%3E%0A%23include+%3Cstring_view%3E%0A%0Astruct+MyThing+%7B%0A++++std::string_view+s%3B%0A%7D%3B%0A%0AMyThing+makeThing(std::string+s)+%7B+return+MyThing%7Bs%7D%3B+%7D%0A%0Astd::string+getS()+%7B+return+%22short%22%3B+%7D%0A%0AMyThing+foo()+%7B+return+makeThing(getS())%3B+%7D%0A%0Aint+main()+%7B%0A++++auto+thing+%3D+foo()%3B%0A++++std::cout+%3C%3C+%22S:+%22+%3C%3C+thing.s+%3C%3C+std::endl%3B%0A++++return+0%3B%0A%7D'),l:'5',n:'1',o:'C%2B%2B+source+%231',t:'0')),k:38.53075642514707,l:'4',m:100,n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:clang_trunk,filters:(b:'0',binary:'1',binaryObject:'1',commentOnly:'0',debugCalls:'1',demangle:'0',directives:'0',execute:'0',intel:'0',libraryCode:'0',trim:'1',verboseDemangling:'0'),flagsViewOpen:'1',fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,libs:!((name:fmt,ver:'811')),options:'-Wextra+-Wall+-std%3Dc%2B%2B20+-fsanitize%3Daddress+-stdlib%3Dlibc%2B%2B',overrides:!(),selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'+x86-64+clang+(trunk)+(Editor+%231)',t:'0')),k:27.7910842482704,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:executor,i:(argsPanelShown:'1',compilationPanelShown:'0',compiler:clang_trunk,compilerName:'',compilerOutShown:'0',execArgs:'',execStdin:'',fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,libs:!(),options:'-fsanitize%3Daddress+-stdlib%3Dlibc%2B%2B',overrides:!(),runtimeTools:!(),source:1,stdinPanelShown:'1',wrap:'1'),l:'5',n:'0',o:'Executor+x86-64+clang+(trunk)+(C%2B%2B,+Editor+%231)',t:'0')),k:33.67815932658255,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4).

I'm not sure if we can do something here. Have to confirm that ASan with container annotations (string annotations) detects it.