Path: blob/main/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
35269 views
//===-- DereferenceChecker.cpp - Null dereference checker -----------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This defines NullDerefChecker, a builtin check in ExprEngine that performs9// checks for null pointers at loads and stores.10//11//===----------------------------------------------------------------------===//1213#include "clang/AST/ExprObjC.h"14#include "clang/AST/ExprOpenMP.h"15#include "clang/Basic/TargetInfo.h"16#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"17#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"18#include "clang/StaticAnalyzer/Core/Checker.h"19#include "clang/StaticAnalyzer/Core/CheckerManager.h"20#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"21#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"22#include "llvm/ADT/SmallString.h"23#include "llvm/Support/raw_ostream.h"2425using namespace clang;26using namespace ento;2728namespace {29class DereferenceChecker30: public Checker< check::Location,31check::Bind,32EventDispatcher<ImplicitNullDerefEvent> > {33enum DerefKind { NullPointer, UndefinedPointerValue, AddressOfLabel };3435BugType BT_Null{this, "Dereference of null pointer", categories::LogicError};36BugType BT_Undef{this, "Dereference of undefined pointer value",37categories::LogicError};38BugType BT_Label{this, "Dereference of the address of a label",39categories::LogicError};4041void reportBug(DerefKind K, ProgramStateRef State, const Stmt *S,42CheckerContext &C) const;4344bool suppressReport(CheckerContext &C, const Expr *E) const;4546public:47void checkLocation(SVal location, bool isLoad, const Stmt* S,48CheckerContext &C) const;49void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;5051static void AddDerefSource(raw_ostream &os,52SmallVectorImpl<SourceRange> &Ranges,53const Expr *Ex, const ProgramState *state,54const LocationContext *LCtx,55bool loadedFrom = false);5657bool SuppressAddressSpaces = false;58};59} // end anonymous namespace6061void62DereferenceChecker::AddDerefSource(raw_ostream &os,63SmallVectorImpl<SourceRange> &Ranges,64const Expr *Ex,65const ProgramState *state,66const LocationContext *LCtx,67bool loadedFrom) {68Ex = Ex->IgnoreParenLValueCasts();69switch (Ex->getStmtClass()) {70default:71break;72case Stmt::DeclRefExprClass: {73const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);74if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {75os << " (" << (loadedFrom ? "loaded from" : "from")76<< " variable '" << VD->getName() << "')";77Ranges.push_back(DR->getSourceRange());78}79break;80}81case Stmt::MemberExprClass: {82const MemberExpr *ME = cast<MemberExpr>(Ex);83os << " (" << (loadedFrom ? "loaded from" : "via")84<< " field '" << ME->getMemberNameInfo() << "')";85SourceLocation L = ME->getMemberLoc();86Ranges.push_back(SourceRange(L, L));87break;88}89case Stmt::ObjCIvarRefExprClass: {90const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);91os << " (" << (loadedFrom ? "loaded from" : "via")92<< " ivar '" << IV->getDecl()->getName() << "')";93SourceLocation L = IV->getLocation();94Ranges.push_back(SourceRange(L, L));95break;96}97}98}99100static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){101const Expr *E = nullptr;102103// Walk through lvalue casts to get the original expression104// that syntactically caused the load.105if (const Expr *expr = dyn_cast<Expr>(S))106E = expr->IgnoreParenLValueCasts();107108if (IsBind) {109const VarDecl *VD;110const Expr *Init;111std::tie(VD, Init) = parseAssignment(S);112if (VD && Init)113E = Init;114}115return E;116}117118bool DereferenceChecker::suppressReport(CheckerContext &C,119const Expr *E) const {120// Do not report dereferences on memory that use address space #256, #257,121// and #258. Those address spaces are used when dereferencing address spaces122// relative to the GS, FS, and SS segments on x86/x86-64 targets.123// Dereferencing a null pointer in these address spaces is not defined124// as an error. All other null dereferences in other address spaces125// are defined as an error unless explicitly defined.126// See https://clang.llvm.org/docs/LanguageExtensions.html, the section127// "X86/X86-64 Language Extensions"128129QualType Ty = E->getType();130if (!Ty.hasAddressSpace())131return false;132if (SuppressAddressSpaces)133return true;134135const llvm::Triple::ArchType Arch =136C.getASTContext().getTargetInfo().getTriple().getArch();137138if ((Arch == llvm::Triple::x86) || (Arch == llvm::Triple::x86_64)) {139switch (toTargetAddressSpace(E->getType().getAddressSpace())) {140case 256:141case 257:142case 258:143return true;144}145}146return false;147}148149static bool isDeclRefExprToReference(const Expr *E) {150if (const auto *DRE = dyn_cast<DeclRefExpr>(E))151return DRE->getDecl()->getType()->isReferenceType();152return false;153}154155void DereferenceChecker::reportBug(DerefKind K, ProgramStateRef State,156const Stmt *S, CheckerContext &C) const {157const BugType *BT = nullptr;158llvm::StringRef DerefStr1;159llvm::StringRef DerefStr2;160switch (K) {161case DerefKind::NullPointer:162BT = &BT_Null;163DerefStr1 = " results in a null pointer dereference";164DerefStr2 = " results in a dereference of a null pointer";165break;166case DerefKind::UndefinedPointerValue:167BT = &BT_Undef;168DerefStr1 = " results in an undefined pointer dereference";169DerefStr2 = " results in a dereference of an undefined pointer value";170break;171case DerefKind::AddressOfLabel:172BT = &BT_Label;173DerefStr1 = " results in an undefined pointer dereference";174DerefStr2 = " results in a dereference of an address of a label";175break;176};177178// Generate an error node.179ExplodedNode *N = C.generateErrorNode(State);180if (!N)181return;182183SmallString<100> buf;184llvm::raw_svector_ostream os(buf);185186SmallVector<SourceRange, 2> Ranges;187188switch (S->getStmtClass()) {189case Stmt::ArraySubscriptExprClass: {190os << "Array access";191const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);192AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),193State.get(), N->getLocationContext());194os << DerefStr1;195break;196}197case Stmt::ArraySectionExprClass: {198os << "Array access";199const ArraySectionExpr *AE = cast<ArraySectionExpr>(S);200AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),201State.get(), N->getLocationContext());202os << DerefStr1;203break;204}205case Stmt::UnaryOperatorClass: {206os << BT->getDescription();207const UnaryOperator *U = cast<UnaryOperator>(S);208AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),209State.get(), N->getLocationContext(), true);210break;211}212case Stmt::MemberExprClass: {213const MemberExpr *M = cast<MemberExpr>(S);214if (M->isArrow() || isDeclRefExprToReference(M->getBase())) {215os << "Access to field '" << M->getMemberNameInfo() << "'" << DerefStr2;216AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),217State.get(), N->getLocationContext(), true);218}219break;220}221case Stmt::ObjCIvarRefExprClass: {222const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);223os << "Access to instance variable '" << *IV->getDecl() << "'" << DerefStr2;224AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),225State.get(), N->getLocationContext(), true);226break;227}228default:229break;230}231232auto report = std::make_unique<PathSensitiveBugReport>(233*BT, buf.empty() ? BT->getDescription() : buf.str(), N);234235bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report);236237for (SmallVectorImpl<SourceRange>::iterator238I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)239report->addRange(*I);240241C.emitReport(std::move(report));242}243244void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,245CheckerContext &C) const {246// Check for dereference of an undefined value.247if (l.isUndef()) {248const Expr *DerefExpr = getDereferenceExpr(S);249if (!suppressReport(C, DerefExpr))250reportBug(DerefKind::UndefinedPointerValue, C.getState(), DerefExpr, C);251return;252}253254DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();255256// Check for null dereferences.257if (!isa<Loc>(location))258return;259260ProgramStateRef state = C.getState();261262ProgramStateRef notNullState, nullState;263std::tie(notNullState, nullState) = state->assume(location);264265if (nullState) {266if (!notNullState) {267// We know that 'location' can only be null. This is what268// we call an "explicit" null dereference.269const Expr *expr = getDereferenceExpr(S);270if (!suppressReport(C, expr)) {271reportBug(DerefKind::NullPointer, nullState, expr, C);272return;273}274}275276// Otherwise, we have the case where the location could either be277// null or not-null. Record the error node as an "implicit" null278// dereference.279if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) {280ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(),281/*IsDirectDereference=*/true};282dispatchEvent(event);283}284}285286// From this point forward, we know that the location is not null.287C.addTransition(notNullState);288}289290void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,291CheckerContext &C) const {292// If we're binding to a reference, check if the value is known to be null.293if (V.isUndef())294return;295296// One should never write to label addresses.297if (auto Label = L.getAs<loc::GotoLabel>()) {298reportBug(DerefKind::AddressOfLabel, C.getState(), S, C);299return;300}301302const MemRegion *MR = L.getAsRegion();303const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);304if (!TVR)305return;306307if (!TVR->getValueType()->isReferenceType())308return;309310ProgramStateRef State = C.getState();311312ProgramStateRef StNonNull, StNull;313std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());314315if (StNull) {316if (!StNonNull) {317const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true);318if (!suppressReport(C, expr)) {319reportBug(DerefKind::NullPointer, StNull, expr, C);320return;321}322}323324// At this point the value could be either null or non-null.325// Record this as an "implicit" null dereference.326if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) {327ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N,328&C.getBugReporter(),329/*IsDirectDereference=*/true};330dispatchEvent(event);331}332}333334// Unlike a regular null dereference, initializing a reference with a335// dereferenced null pointer does not actually cause a runtime exception in336// Clang's implementation of references.337//338// int &r = *p; // safe??339// if (p != NULL) return; // uh-oh340// r = 5; // trap here341//342// The standard says this is invalid as soon as we try to create a "null343// reference" (there is no such thing), but turning this into an assumption344// that 'p' is never null will not match our actual runtime behavior.345// So we do not record this assumption, allowing us to warn on the last line346// of this example.347//348// We do need to add a transition because we may have generated a sink for349// the "implicit" null dereference.350C.addTransition(State, this);351}352353void ento::registerDereferenceChecker(CheckerManager &mgr) {354auto *Chk = mgr.registerChecker<DereferenceChecker>();355Chk->SuppressAddressSpaces = mgr.getAnalyzerOptions().getCheckerBooleanOption(356mgr.getCurrentCheckerName(), "SuppressAddressSpaces");357}358359bool ento::shouldRegisterDereferenceChecker(const CheckerManager &mgr) {360return true;361}362363364