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