//=== StringChecker.cpp -------------------------------------------*- C++ -*--// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements the modeling of the std::basic_string type. // This involves checking preconditions of the operations and applying the // effects of the operations, e.g. their post-conditions. // //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" using namespace clang; using namespace ento; namespace { class StringChecker : public Checker { BugType BT_Null{this, "Dereference of null pointer", categories::LogicError}; mutable const FunctionDecl *StringConstCharPtrCtor = nullptr; mutable CanQualType SizeTypeTy; const CallDescription TwoParamStdStringCtor = { {"std", "basic_string", "basic_string"}, 2, 2}; bool isCharToStringCtor(const CallEvent &Call, const ASTContext &ACtx) const; public: void checkPreCall(const CallEvent &Call, CheckerContext &C) const; }; bool StringChecker::isCharToStringCtor(const CallEvent &Call, const ASTContext &ACtx) const { if (!TwoParamStdStringCtor.matches(Call)) return false; const auto *FD = dyn_cast(Call.getDecl()); assert(FD); // See if we already cached it. if (StringConstCharPtrCtor && StringConstCharPtrCtor == FD) return true; // Verify that the parameters have the expected types: // - arg 1: `const CharT *` // - arg 2: some allocator - which is definately not `size_t`. const QualType Arg1Ty = Call.getArgExpr(0)->getType().getCanonicalType(); const QualType Arg2Ty = Call.getArgExpr(1)->getType().getCanonicalType(); if (!Arg1Ty->isPointerType()) return false; // It makes sure that we don't select the `string(const char* p, size_t len)` // overload accidentally. if (Arg2Ty.getCanonicalType() == ACtx.getSizeType()) return false; StringConstCharPtrCtor = FD; // Cache the decl of the right overload. return true; } void StringChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (!isCharToStringCtor(Call, C.getASTContext())) return; const auto Param = Call.getArgSVal(0).getAs(); if (!Param) return; // We managed to constrain the parameter to non-null. ProgramStateRef NotNull, Null; std::tie(NotNull, Null) = C.getState()->assume(*Param); if (NotNull) { const auto Callback = [Param](PathSensitiveBugReport &BR) -> std::string { return BR.isInteresting(*Param) ? "Assuming the pointer is not null." : ""; }; // Emit note only if this operation constrained the pointer to be null. C.addTransition(NotNull, Null ? C.getNoteTag(Callback) : nullptr); return; } // We found a path on which the parameter is NULL. if (ExplodedNode *N = C.generateErrorNode(C.getState())) { auto R = std::make_unique( BT_Null, "The parameter must not be null", N); bugreporter::trackExpressionValue(N, Call.getArgExpr(0), *R); C.emitReport(std::move(R)); } } } // end anonymous namespace void ento::registerStringChecker(CheckerManager &Mgr) { Mgr.registerChecker(); } bool ento::shouldRegisterStringChecker(const CheckerManager &) { return true; }