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