Fix audit findings: soundness, coverage reporting, docs, and review follow-ups #1
4 changed files with 30 additions and 5 deletions
fix(encoder): a global C++ lookalike is not a C library function
Problem: isCLibraryFunction accepted any global-scope non-member function, so a project's `void free(Obj*)` overload was modeled as libc free: calling it twice was a double free, and reading after it a use-after-free. Fix: a global function counts only when it is extern "C", in namespace std, or declared in a system header. extern "C" also covers C translation units, where external functions have C language linkage. Test: test_library_callee_identity "[global]" (DoubleFree and UseAfterFree now 0; were 1 each); test_encoder_callee_names flips the global C++ helper to not-a-library-callee. Full suite: 140/140.
commit
00082448f4
|
|
@ -38,9 +38,10 @@ namespace fold {
|
|||
[[nodiscard]] std::string summaryStoreKeyForCall(const clang::CallExpr* ce);
|
||||
|
||||
// True when `fd` can be the C library function its name suggests: not a class
|
||||
// member, and declared at global scope, in namespace std, or extern "C".
|
||||
// Library models (malloc/free, taint sources and sinks) must check this; the
|
||||
// bare identifier alone also matches `mine::free` or `Pool::free`.
|
||||
// member, and extern "C", in namespace std, or declared at global scope in a
|
||||
// system header. Library models (malloc/free, taint sources and sinks) must
|
||||
// check this; the bare identifier alone also matches `mine::free`,
|
||||
// `Pool::free` or a project's global `free(Obj*)` overload.
|
||||
[[nodiscard]] bool isCLibraryFunction(const clang::FunctionDecl* fd);
|
||||
|
||||
// isCLibraryFunction of a call's direct callee; false for indirect calls.
|
||||
|
|
|
|||
|
|
@ -76,7 +76,12 @@ bool isCLibraryFunction(const clang::FunctionDecl* fd) {
|
|||
return false;
|
||||
if (fd->isExternC() || fd->isInStdNamespace())
|
||||
return true;
|
||||
return fd->getDeclContext()->getRedeclContext()->isTranslationUnit();
|
||||
// A global C++ function is a library function only when a system header
|
||||
// declares it; a project overload that shares a libc name is not.
|
||||
if (!fd->getDeclContext()->getRedeclContext()->isTranslationUnit())
|
||||
return false;
|
||||
const auto& sm = fd->getASTContext().getSourceManager();
|
||||
return sm.isInSystemHeader(fd->getCanonicalDecl()->getLocation());
|
||||
}
|
||||
|
||||
bool isCLibraryCallee(const clang::CallExpr* ce) {
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ TEST_CASE("isCLibraryCallee: only C library functions, never members or namespac
|
|||
CHECK(fold::isCLibraryCallee(calls[1])); // std::free resolves to ::free
|
||||
CHECK_FALSE(fold::isCLibraryCallee(calls[2])); // mine::free
|
||||
CHECK_FALSE(fold::isCLibraryCallee(calls[3])); // Pool::free
|
||||
CHECK(fold::isCLibraryCallee(calls[4])); // global scope: C semantics
|
||||
CHECK_FALSE(fold::isCLibraryCallee(calls[4])); // global C++ function, not extern "C"
|
||||
CHECK_FALSE(fold::isCLibraryCallee(calls[5])); // indirect call
|
||||
CHECK_FALSE(fold::isCLibraryCallee(nullptr));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,3 +115,22 @@ TEST_CASE("taint: libc system with tainted input is still a sink", "[library-cal
|
|||
"int f() { return system(getenv(\"CMD\")); }\n";
|
||||
CHECK(countKind(runTaint(code), fold::FindingKind::TaintTrack) == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("heap model: a global C++ function named free is not libc free",
|
||||
"[library-callee][double-free][global]") {
|
||||
// A global-scope C++ overload (no extern "C", not in a system header) is a
|
||||
// project function that shares libc's name, not the library function.
|
||||
const std::string code = kLibc + "struct Obj { int v; };\n"
|
||||
"void free(Obj*) {}\n"
|
||||
"int f() {\n"
|
||||
" Obj* o = static_cast<Obj*>(malloc(sizeof(Obj)));\n"
|
||||
" if (!o) return 0;\n"
|
||||
" o->v = 1;\n"
|
||||
" free(o);\n"
|
||||
" int v = o->v;\n"
|
||||
" free(o);\n"
|
||||
" return v;\n"
|
||||
"}\n";
|
||||
CHECK(countKind(runDoubleFree(code), fold::FindingKind::DoubleFree) == 0);
|
||||
CHECK(countKind(runUseAfterFree(code), fold::FindingKind::UseAfterFree) == 0);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue