511 lines · cpp
1//===-- DereferenceChecker.cpp - Null dereference checker -----------------===//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-exception6//7//===----------------------------------------------------------------------===//8//9// This defines NullDerefChecker, a builtin check in ExprEngine that performs10// checks for null pointers at loads and stores.11//12//===----------------------------------------------------------------------===//13 14#include "clang/AST/ExprObjC.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/Support/FormatVariadic.h"23#include "llvm/Support/raw_ostream.h"24 25using namespace clang;26using namespace ento;27 28namespace {29 30class DerefBugType : public BugType {31 StringRef ArrayMsg, FieldMsg;32 33public:34 DerefBugType(CheckerFrontend *FE, StringRef Desc, const char *AMsg,35 const char *FMsg = nullptr)36 : BugType(FE, Desc), ArrayMsg(AMsg), FieldMsg(FMsg ? FMsg : AMsg) {}37 StringRef getArrayMsg() const { return ArrayMsg; }38 StringRef getFieldMsg() const { return FieldMsg; }39};40 41class DereferenceChecker42 : public CheckerFamily<check::Location, check::Bind,43 check::PreStmt<BinaryOperator>,44 EventDispatcher<ImplicitNullDerefEvent>> {45 void reportDerefBug(const DerefBugType &BT, ProgramStateRef State,46 const Stmt *S, CheckerContext &C) const;47 48 bool suppressReport(CheckerContext &C, const Expr *E) const;49 50public:51 void checkLocation(SVal location, bool isLoad, const Stmt* S,52 CheckerContext &C) const;53 void checkBind(SVal L, SVal V, const Stmt *S, bool AtDeclInit,54 CheckerContext &C) const;55 void checkPreStmt(const BinaryOperator *Op, CheckerContext &C) const;56 57 static void AddDerefSource(raw_ostream &os,58 SmallVectorImpl<SourceRange> &Ranges,59 const Expr *Ex, const ProgramState *state,60 const LocationContext *LCtx,61 bool loadedFrom = false);62 63 CheckerFrontend NullDerefChecker, FixedDerefChecker, NullPointerArithmChecker;64 const DerefBugType NullBug{&NullDerefChecker, "Dereference of null pointer",65 "a null pointer dereference",66 "a dereference of a null pointer"};67 const DerefBugType UndefBug{&NullDerefChecker,68 "Dereference of undefined pointer value",69 "an undefined pointer dereference",70 "a dereference of an undefined pointer value"};71 const DerefBugType LabelBug{&NullDerefChecker,72 "Dereference of the address of a label",73 "an undefined pointer dereference",74 "a dereference of an address of a label"};75 const DerefBugType FixedAddressBug{&FixedDerefChecker,76 "Dereference of a fixed address",77 "a dereference of a fixed address"};78 const BugType NullPointerArithmBug{79 &NullPointerArithmChecker,80 "Possibly undefined arithmetic operation involving a null pointer"};81 82 StringRef getDebugTag() const override { return "DereferenceChecker"; }83};84 85struct ValueDescStr {86 SmallVectorImpl<SourceRange> &Ranges;87 const Expr *Ex;88 const ProgramState *State;89 const LocationContext *LCtx;90 bool IsPointer;91 ConditionTruthVal IsNull;92};93 94} // end anonymous namespace95 96void97DereferenceChecker::AddDerefSource(raw_ostream &os,98 SmallVectorImpl<SourceRange> &Ranges,99 const Expr *Ex,100 const ProgramState *state,101 const LocationContext *LCtx,102 bool loadedFrom) {103 Ex = Ex->IgnoreParenLValueCasts();104 switch (Ex->getStmtClass()) {105 default:106 break;107 case Stmt::DeclRefExprClass: {108 const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);109 if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {110 os << " (" << (loadedFrom ? "loaded from" : "from")111 << " variable '" << VD->getName() << "')";112 Ranges.push_back(DR->getSourceRange());113 }114 break;115 }116 case Stmt::MemberExprClass: {117 const MemberExpr *ME = cast<MemberExpr>(Ex);118 os << " (" << (loadedFrom ? "loaded from" : "via")119 << " field '" << ME->getMemberNameInfo() << "')";120 SourceLocation L = ME->getMemberLoc();121 Ranges.push_back(SourceRange(L, L));122 break;123 }124 case Stmt::ObjCIvarRefExprClass: {125 const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);126 os << " (" << (loadedFrom ? "loaded from" : "via")127 << " ivar '" << IV->getDecl()->getName() << "')";128 SourceLocation L = IV->getLocation();129 Ranges.push_back(SourceRange(L, L));130 break;131 }132 }133}134 135static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){136 const Expr *E = nullptr;137 138 // Walk through lvalue casts to get the original expression139 // that syntactically caused the load.140 if (const Expr *expr = dyn_cast<Expr>(S))141 E = expr->IgnoreParenLValueCasts();142 143 if (IsBind) {144 const VarDecl *VD;145 const Expr *Init;146 std::tie(VD, Init) = parseAssignment(S);147 if (VD && Init)148 E = Init;149 }150 return E;151}152 153bool DereferenceChecker::suppressReport(CheckerContext &C,154 const Expr *E) const {155 // Do not report dereferences on memory that use address space #256, #257,156 // and #258. Those address spaces are used when dereferencing address spaces157 // relative to the GS, FS, and SS segments on x86/x86-64 targets.158 // Dereferencing a null pointer in these address spaces is not defined159 // as an error. All other null dereferences in other address spaces160 // are defined as an error unless explicitly defined.161 // See https://clang.llvm.org/docs/LanguageExtensions.html, the section162 // "X86/X86-64 Language Extensions"163 164 QualType Ty = E->getType();165 if (!Ty.hasAddressSpace())166 return false;167 if (C.getAnalysisManager()168 .getAnalyzerOptions()169 .ShouldSuppressAddressSpaceDereferences)170 return true;171 172 const llvm::Triple::ArchType Arch =173 C.getASTContext().getTargetInfo().getTriple().getArch();174 175 if ((Arch == llvm::Triple::x86) || (Arch == llvm::Triple::x86_64)) {176 switch (toTargetAddressSpace(E->getType().getAddressSpace())) {177 case 256:178 case 257:179 case 258:180 return true;181 }182 }183 return false;184}185 186static bool isDeclRefExprToReference(const Expr *E) {187 if (const auto *DRE = dyn_cast<DeclRefExpr>(E))188 return DRE->getDecl()->getType()->isReferenceType();189 return false;190}191 192void DereferenceChecker::reportDerefBug(const DerefBugType &BT,193 ProgramStateRef State, const Stmt *S,194 CheckerContext &C) const {195 if (&BT == &FixedAddressBug) {196 if (!FixedDerefChecker.isEnabled())197 // Deliberately don't add a sink node if check is disabled.198 // This situation may be valid in special cases.199 return;200 } else {201 if (!NullDerefChecker.isEnabled()) {202 C.addSink();203 return;204 }205 }206 207 // Generate an error node.208 ExplodedNode *N = C.generateErrorNode(State);209 if (!N)210 return;211 212 SmallString<100> Buf;213 llvm::raw_svector_ostream Out(Buf);214 215 SmallVector<SourceRange, 2> Ranges;216 217 switch (S->getStmtClass()) {218 case Stmt::ArraySubscriptExprClass: {219 Out << "Array access";220 const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);221 AddDerefSource(Out, Ranges, AE->getBase()->IgnoreParenCasts(), State.get(),222 N->getLocationContext());223 Out << " results in " << BT.getArrayMsg();224 break;225 }226 case Stmt::ArraySectionExprClass: {227 Out << "Array access";228 const ArraySectionExpr *AE = cast<ArraySectionExpr>(S);229 AddDerefSource(Out, Ranges, AE->getBase()->IgnoreParenCasts(), State.get(),230 N->getLocationContext());231 Out << " results in " << BT.getArrayMsg();232 break;233 }234 case Stmt::UnaryOperatorClass: {235 Out << BT.getDescription();236 const UnaryOperator *U = cast<UnaryOperator>(S);237 AddDerefSource(Out, Ranges, U->getSubExpr()->IgnoreParens(), State.get(),238 N->getLocationContext(), true);239 break;240 }241 case Stmt::MemberExprClass: {242 const MemberExpr *M = cast<MemberExpr>(S);243 if (M->isArrow() || isDeclRefExprToReference(M->getBase())) {244 Out << "Access to field '" << M->getMemberNameInfo() << "' results in "245 << BT.getFieldMsg();246 AddDerefSource(Out, Ranges, M->getBase()->IgnoreParenCasts(), State.get(),247 N->getLocationContext(), true);248 }249 break;250 }251 case Stmt::ObjCIvarRefExprClass: {252 const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);253 Out << "Access to instance variable '" << *IV->getDecl() << "' results in "254 << BT.getFieldMsg();255 AddDerefSource(Out, Ranges, IV->getBase()->IgnoreParenCasts(), State.get(),256 N->getLocationContext(), true);257 break;258 }259 default:260 break;261 }262 263 auto BR = std::make_unique<PathSensitiveBugReport>(264 BT, Buf.empty() ? BT.getDescription() : Buf.str(), N);265 266 bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *BR);267 268 for (const auto &R : Ranges)269 BR->addRange(R);270 271 C.emitReport(std::move(BR));272}273 274void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,275 CheckerContext &C) const {276 // Check for dereference of an undefined value.277 if (l.isUndef()) {278 const Expr *DerefExpr = getDereferenceExpr(S);279 if (!suppressReport(C, DerefExpr))280 reportDerefBug(UndefBug, C.getState(), DerefExpr, C);281 return;282 }283 284 DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();285 286 // Check for null dereferences.287 if (!isa<Loc>(location))288 return;289 290 ProgramStateRef state = C.getState();291 292 ProgramStateRef notNullState, nullState;293 std::tie(notNullState, nullState) = state->assume(location);294 295 if (nullState) {296 if (!notNullState) {297 // We know that 'location' can only be null. This is what298 // we call an "explicit" null dereference.299 const Expr *expr = getDereferenceExpr(S);300 if (!suppressReport(C, expr)) {301 reportDerefBug(NullBug, nullState, expr, C);302 return;303 }304 }305 306 // Otherwise, we have the case where the location could either be307 // null or not-null. Record the error node as an "implicit" null308 // dereference.309 if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) {310 ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(),311 /*IsDirectDereference=*/true};312 dispatchEvent(event);313 }314 }315 316 if (location.isConstant()) {317 const Expr *DerefExpr = getDereferenceExpr(S, isLoad);318 if (!suppressReport(C, DerefExpr))319 reportDerefBug(FixedAddressBug, notNullState, DerefExpr, C);320 return;321 }322 323 // From this point forward, we know that the location is not null.324 C.addTransition(notNullState);325}326 327void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,328 bool AtDeclInit, CheckerContext &C) const {329 // If we're binding to a reference, check if the value is known to be null.330 if (V.isUndef())331 return;332 333 // One should never write to label addresses.334 if (auto Label = L.getAs<loc::GotoLabel>()) {335 reportDerefBug(LabelBug, C.getState(), S, C);336 return;337 }338 339 const MemRegion *MR = L.getAsRegion();340 const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);341 if (!TVR)342 return;343 344 if (!TVR->getValueType()->isReferenceType())345 return;346 347 ProgramStateRef State = C.getState();348 349 ProgramStateRef StNonNull, StNull;350 std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());351 352 if (StNull) {353 if (!StNonNull) {354 const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true);355 if (!suppressReport(C, expr)) {356 reportDerefBug(NullBug, StNull, expr, C);357 return;358 }359 }360 361 // At this point the value could be either null or non-null.362 // Record this as an "implicit" null dereference.363 if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) {364 ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N,365 &C.getBugReporter(),366 /*IsDirectDereference=*/true};367 dispatchEvent(event);368 }369 }370 371 if (V.isConstant()) {372 const Expr *DerefExpr = getDereferenceExpr(S, true);373 if (!suppressReport(C, DerefExpr))374 reportDerefBug(FixedAddressBug, State, DerefExpr, C);375 return;376 }377 378 // Unlike a regular null dereference, initializing a reference with a379 // dereferenced null pointer does not actually cause a runtime exception in380 // Clang's implementation of references.381 //382 // int &r = *p; // safe??383 // if (p != NULL) return; // uh-oh384 // r = 5; // trap here385 //386 // The standard says this is invalid as soon as we try to create a "null387 // reference" (there is no such thing), but turning this into an assumption388 // that 'p' is never null will not match our actual runtime behavior.389 // So we do not record this assumption, allowing us to warn on the last line390 // of this example.391 //392 // We do need to add a transition because we may have generated a sink for393 // the "implicit" null dereference.394 C.addTransition(State, this);395}396 397namespace llvm {398template <> struct format_provider<ValueDescStr> {399 static void format(const ValueDescStr &V, raw_ostream &Stream,400 StringRef Style) {401 static const char *ValueStr[2][3] = {402 {"zero", "nonzero integer value", "probably nonzero integer value"},403 {"null pointer", "non-null pointer", "probably non-null pointer"},404 };405 Stream406 << ValueStr[V.IsPointer][V.IsNull.isConstrainedTrue()407 ? 0408 : (V.IsNull.isConstrainedFalse() ? 1 : 2)];409 DereferenceChecker::AddDerefSource(Stream, V.Ranges, V.Ex, V.State, V.LCtx,410 false);411 }412};413} // namespace llvm414 415void DereferenceChecker::checkPreStmt(const BinaryOperator *Op,416 CheckerContext &C) const {417 if (!Op->isAdditiveOp() || !NullPointerArithmChecker.isEnabled())418 return;419 const Expr *E1 = Op->getLHS();420 const Expr *E2 = Op->getRHS();421 QualType T1 = E1->getType().getCanonicalType();422 QualType T2 = E2->getType().getCanonicalType();423 bool T1IsPointer = T1->isPointerType();424 bool T2IsPointer = T2->isPointerType();425 if (T1->isIntegerType() && T2->isIntegerType())426 return;427 if (!T1IsPointer && !T1->isIntegerType() && !T2IsPointer &&428 !T2->isIntegerType())429 return;430 431 ProgramStateRef State = C.getState();432 ConditionTruthVal V1IsNull = State->isNull(C.getSVal(E1));433 ConditionTruthVal V2IsNull = State->isNull(C.getSVal(E2));434 bool IsConstrained = true;435 436 // Check cases 'NULL + x' and 'NULL - x'437 if (T1IsPointer && !T2IsPointer) {438 if (!V1IsNull.isConstrainedTrue() || V2IsNull.isConstrainedTrue())439 return;440 IsConstrained = V2IsNull.isConstrainedFalse();441 }442 443 // Check case 'x + NULL'444 if (!T1IsPointer && T2IsPointer) {445 if (V1IsNull.isConstrainedTrue() || !V2IsNull.isConstrainedTrue())446 return;447 IsConstrained = V1IsNull.isConstrainedFalse();448 }449 450 // Check case 'NULL - p' or 'p - NULL'451 if (T1IsPointer && T2IsPointer) {452 if (!V1IsNull.isConstrainedTrue() && !V2IsNull.isConstrainedTrue())453 return;454 if (V1IsNull.isConstrainedTrue() && V2IsNull.isConstrainedTrue())455 return;456 IsConstrained =457 V1IsNull.isConstrainedFalse() || V2IsNull.isConstrainedFalse();458 }459 460 SmallVector<SourceRange, 2> Ranges;461 const char *OpcodeStr =462 Op->getOpcode() == BO_Add ? "Addition" : "Subtraction";463 const char *ResultStr = IsConstrained ? "results" : "may result";464 ValueDescStr DerefArg1{465 Ranges, E1, State.get(), C.getLocationContext(), T1IsPointer, V1IsNull};466 ValueDescStr DerefArg2{467 Ranges, E2, State.get(), C.getLocationContext(), T2IsPointer, V2IsNull};468 std::string Msg =469 llvm::formatv("{0} of a {1} and a {2} {3} in undefined behavior",470 OpcodeStr, DerefArg1, DerefArg2, ResultStr);471 472 ExplodedNode *N = C.generateErrorNode(State);473 if (!N)474 return;475 auto BR =476 std::make_unique<PathSensitiveBugReport>(NullPointerArithmBug, Msg, N);477 if (V1IsNull.isConstrainedTrue())478 bugreporter::trackExpressionValue(N, E1, *BR);479 if (V2IsNull.isConstrainedTrue())480 bugreporter::trackExpressionValue(N, E2, *BR);481 for (const auto &R : Ranges)482 BR->addRange(R);483 484 C.emitReport(std::move(BR));485}486 487void ento::registerNullDereferenceChecker(CheckerManager &Mgr) {488 Mgr.getChecker<DereferenceChecker>()->NullDerefChecker.enable(Mgr);489}490 491bool ento::shouldRegisterNullDereferenceChecker(const CheckerManager &) {492 return true;493}494 495void ento::registerFixedAddressDereferenceChecker(CheckerManager &Mgr) {496 Mgr.getChecker<DereferenceChecker>()->FixedDerefChecker.enable(Mgr);497}498 499bool ento::shouldRegisterFixedAddressDereferenceChecker(500 const CheckerManager &) {501 return true;502}503 504void ento::registerNullPointerArithmChecker(CheckerManager &Mgr) {505 Mgr.getChecker<DereferenceChecker>()->NullPointerArithmChecker.enable(Mgr);506}507 508bool ento::shouldRegisterNullPointerArithmChecker(const CheckerManager &) {509 return true;510}511