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