NagyDonat e2f1cbae45
[analyzer] Use explicit call description mode (easy cases) (#88879)
This commit explicitly specifies the matching mode (C library function,
any non-method function, or C++ method) for the `CallDescription`s
constructed in various checkers where this transition was easy and
straightforward.

This change won't cause major functional changes, but isn't NFC because
it ensures that e.g. call descriptions for a non-method function won't
accidentally match a method that has the same name.

Separate commits will perform (or have already performed) this change in
other checkers. My goal is to ensure that the call description mode is
always explicitly specified and eliminate (or strongly restrict) the
vague "may be either a method or a simple function" mode that's the
current default.
2024-04-19 14:22:51 +02:00

106 lines
3.8 KiB
C++

//=== StringChecker.cpp -------------------------------------------*- C++ -*--//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements the modeling of the std::basic_string type.
// This involves checking preconditions of the operations and applying the
// effects of the operations, e.g. their post-conditions.
//
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
using namespace clang;
using namespace ento;
namespace {
class StringChecker : public Checker<check::PreCall> {
BugType BT_Null{this, "Dereference of null pointer", categories::LogicError};
mutable const FunctionDecl *StringConstCharPtrCtor = nullptr;
mutable CanQualType SizeTypeTy;
const CallDescription TwoParamStdStringCtor = {
CDM::CXXMethod, {"std", "basic_string", "basic_string"}, 2, 2};
bool isCharToStringCtor(const CallEvent &Call, const ASTContext &ACtx) const;
public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
};
bool StringChecker::isCharToStringCtor(const CallEvent &Call,
const ASTContext &ACtx) const {
if (!TwoParamStdStringCtor.matches(Call))
return false;
const auto *FD = dyn_cast<FunctionDecl>(Call.getDecl());
assert(FD);
// See if we already cached it.
if (StringConstCharPtrCtor && StringConstCharPtrCtor == FD)
return true;
// Verify that the parameters have the expected types:
// - arg 1: `const CharT *`
// - arg 2: some allocator - which is definately not `size_t`.
const QualType Arg1Ty = Call.getArgExpr(0)->getType().getCanonicalType();
const QualType Arg2Ty = Call.getArgExpr(1)->getType().getCanonicalType();
if (!Arg1Ty->isPointerType())
return false;
// It makes sure that we don't select the `string(const char* p, size_t len)`
// overload accidentally.
if (Arg2Ty.getCanonicalType() == ACtx.getSizeType())
return false;
StringConstCharPtrCtor = FD; // Cache the decl of the right overload.
return true;
}
void StringChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
if (!isCharToStringCtor(Call, C.getASTContext()))
return;
const auto Param = Call.getArgSVal(0).getAs<Loc>();
if (!Param)
return;
// We managed to constrain the parameter to non-null.
ProgramStateRef NotNull, Null;
std::tie(NotNull, Null) = C.getState()->assume(*Param);
if (NotNull) {
const auto Callback = [Param](PathSensitiveBugReport &BR) -> std::string {
return BR.isInteresting(*Param) ? "Assuming the pointer is not null."
: "";
};
// Emit note only if this operation constrained the pointer to be null.
C.addTransition(NotNull, Null ? C.getNoteTag(Callback) : nullptr);
return;
}
// We found a path on which the parameter is NULL.
if (ExplodedNode *N = C.generateErrorNode(C.getState())) {
auto R = std::make_unique<PathSensitiveBugReport>(
BT_Null, "The parameter must not be null", N);
bugreporter::trackExpressionValue(N, Call.getArgExpr(0), *R);
C.emitReport(std::move(R));
}
}
} // end anonymous namespace
void ento::registerStringChecker(CheckerManager &Mgr) {
Mgr.registerChecker<StringChecker>();
}
bool ento::shouldRegisterStringChecker(const CheckerManager &) { return true; }