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 = {
30e1fdec87SBalazs Benics {"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
isCharToStringCtor(const CallEvent & Call,const ASTContext & ACtx) const38e1fdec87SBalazs 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 *`
51e1fdec87SBalazs Benics // - arg 2: some allocator - which is definately 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
checkPreCall(const CallEvent & Call,CheckerContext & C) const67e1fdec87SBalazs 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>();
72*452db157SKazu 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
registerStringChecker(CheckerManager & Mgr)101e1fdec87SBalazs Benics void ento::registerStringChecker(CheckerManager &Mgr) {
102e1fdec87SBalazs Benics Mgr.registerChecker<StringChecker>();
103e1fdec87SBalazs Benics }
104e1fdec87SBalazs Benics
shouldRegisterStringChecker(const CheckerManager &)105e1fdec87SBalazs Benics bool ento::shouldRegisterStringChecker(const CheckerManager &) { return true; }
106