StringChecker.cpp 3.8 KB

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