| //=== 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<check::PreCall> { |
| 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<FunctionDecl>(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<Loc>(); |
| if (!Param.hasValue()) |
| 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<PathSensitiveBugReport>( |
| 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<StringChecker>(); |
| } |
| |
| bool ento::shouldRegisterStringChecker(const CheckerManager &) { return true; } |