106 lines · cpp
1//=== StringChecker.cpp -------------------------------------------*- C++ -*--//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements the modeling of the std::basic_string type.10// This involves checking preconditions of the operations and applying the11// effects of the operations, e.g. their post-conditions.12//13//===----------------------------------------------------------------------===//14 15#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"16#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"17#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"18#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"19#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"20 21using namespace clang;22using namespace ento;23 24namespace {25class StringChecker : public Checker<check::PreCall> {26 BugType BT_Null{this, "Dereference of null pointer", categories::LogicError};27 mutable const FunctionDecl *StringConstCharPtrCtor = nullptr;28 mutable CanQualType SizeTypeTy;29 const CallDescription TwoParamStdStringCtor = {30 CDM::CXXMethod, {"std", "basic_string", "basic_string"}, 2, 2};31 32 bool isCharToStringCtor(const CallEvent &Call, const ASTContext &ACtx) const;33 34public:35 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;36};37 38bool StringChecker::isCharToStringCtor(const CallEvent &Call,39 const ASTContext &ACtx) const {40 if (!TwoParamStdStringCtor.matches(Call))41 return false;42 const auto *FD = dyn_cast<FunctionDecl>(Call.getDecl());43 assert(FD);44 45 // See if we already cached it.46 if (StringConstCharPtrCtor && StringConstCharPtrCtor == FD)47 return true;48 49 // Verify that the parameters have the expected types:50 // - arg 1: `const CharT *`51 // - arg 2: some allocator - which is definitely not `size_t`.52 const QualType Arg1Ty = Call.getArgExpr(0)->getType().getCanonicalType();53 const QualType Arg2Ty = Call.getArgExpr(1)->getType().getCanonicalType();54 55 if (!Arg1Ty->isPointerType())56 return false;57 58 // It makes sure that we don't select the `string(const char* p, size_t len)`59 // overload accidentally.60 if (Arg2Ty.getCanonicalType() == ACtx.getSizeType())61 return false;62 63 StringConstCharPtrCtor = FD; // Cache the decl of the right overload.64 return true;65}66 67void StringChecker::checkPreCall(const CallEvent &Call,68 CheckerContext &C) const {69 if (!isCharToStringCtor(Call, C.getASTContext()))70 return;71 const auto Param = Call.getArgSVal(0).getAs<Loc>();72 if (!Param)73 return;74 75 // We managed to constrain the parameter to non-null.76 ProgramStateRef NotNull, Null;77 std::tie(NotNull, Null) = C.getState()->assume(*Param);78 79 if (NotNull) {80 const auto Callback = [Param](PathSensitiveBugReport &BR) -> std::string {81 return BR.isInteresting(*Param) ? "Assuming the pointer is not null."82 : "";83 };84 85 // Emit note only if this operation constrained the pointer to be null.86 C.addTransition(NotNull, Null ? C.getNoteTag(Callback) : nullptr);87 return;88 }89 90 // We found a path on which the parameter is NULL.91 if (ExplodedNode *N = C.generateErrorNode(C.getState())) {92 auto R = std::make_unique<PathSensitiveBugReport>(93 BT_Null, "The parameter must not be null", N);94 bugreporter::trackExpressionValue(N, Call.getArgExpr(0), *R);95 C.emitReport(std::move(R));96 }97}98 99} // end anonymous namespace100 101void ento::registerStringChecker(CheckerManager &Mgr) {102 Mgr.registerChecker<StringChecker>();103}104 105bool ento::shouldRegisterStringChecker(const CheckerManager &) { return true; }106