22#include "llvm/Support/FormatVariadic.h"
23#include "llvm/Support/raw_ostream.h"
30class DerefBugType :
public BugType {
31 StringRef ArrayMsg, FieldMsg;
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; }
41class DereferenceChecker
43 check::PreStmt<BinaryOperator>,
44 EventDispatcher<ImplicitNullDerefEvent>> {
46 const Stmt *S, CheckerContext &
C)
const;
48 bool suppressReport(CheckerContext &
C,
const Expr *E)
const;
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;
57 static void AddDerefSource(raw_ostream &os,
58 SmallVectorImpl<SourceRange> &Ranges,
59 const Expr *Ex,
const ProgramState *state,
60 const StackFrame *SF,
bool loadedFrom =
false);
62 CheckerFrontend NullDerefChecker, FixedDerefChecker, NullPointerArithmChecker;
63 const DerefBugType NullBug{&NullDerefChecker,
"Dereference of null pointer",
64 "a null pointer dereference",
65 "a dereference of a null pointer"};
66 const DerefBugType UndefBug{&NullDerefChecker,
67 "Dereference of undefined pointer value",
68 "an undefined pointer dereference",
69 "a dereference of an undefined pointer value"};
70 const DerefBugType LabelBug{&NullDerefChecker,
71 "Dereference of the address of a label",
72 "an undefined pointer dereference",
73 "a dereference of an address of a label"};
74 const DerefBugType FixedAddressBug{&FixedDerefChecker,
75 "Dereference of a fixed address",
76 "a dereference of a fixed address"};
77 const BugType NullPointerArithmBug{
78 &NullPointerArithmChecker,
79 "Possibly undefined arithmetic operation involving a null pointer"};
81 StringRef getDebugTag()
const override {
return "DereferenceChecker"; }
85 SmallVectorImpl<SourceRange> &Ranges;
87 const ProgramState *State;
90 ConditionTruthVal IsNull;
95void DereferenceChecker::AddDerefSource(raw_ostream &os,
104 case Stmt::DeclRefExprClass: {
106 if (
const VarDecl *VD = dyn_cast<VarDecl>(DR->
getDecl())) {
107 os <<
" (" << (loadedFrom ?
"loaded from" :
"from")
108 <<
" variable '" << VD->getName() <<
"')";
113 case Stmt::MemberExprClass: {
115 os <<
" (" << (loadedFrom ?
"loaded from" :
"via")
118 Ranges.push_back(SourceRange(L, L));
121 case Stmt::ObjCIvarRefExprClass: {
123 os <<
" (" << (loadedFrom ?
"loaded from" :
"via")
126 Ranges.push_back(SourceRange(L, L));
133 const Expr *E =
nullptr;
137 if (
const Expr *
expr = dyn_cast<Expr>(S))
138 E =
expr->IgnoreParenLValueCasts();
150bool DereferenceChecker::suppressReport(CheckerContext &
C,
151 const Expr *E)
const {
164 if (
C.getAnalysisManager()
165 .getAnalyzerOptions()
166 .ShouldSuppressAddressSpaceDereferences)
169 const llvm::Triple::ArchType
Arch =
170 C.getASTContext().getTargetInfo().getTriple().getArch();
172 if ((
Arch == llvm::Triple::x86) || (
Arch == llvm::Triple::x86_64)) {
184 if (
const auto *DRE = dyn_cast<DeclRefExpr>(E))
185 return DRE->getDecl()->getType()->isReferenceType();
189void DereferenceChecker::reportDerefBug(
const DerefBugType &BT,
191 CheckerContext &
C)
const {
192 if (&BT == &FixedAddressBug) {
205 ExplodedNode *N =
C.generateErrorNode(State);
209 SmallString<100> Buf;
210 llvm::raw_svector_ostream
Out(Buf);
212 SmallVector<SourceRange, 2> Ranges;
215 case Stmt::ArraySubscriptExprClass: {
216 Out <<
"Array access";
220 Out <<
" results in " << BT.getArrayMsg();
223 case Stmt::ArraySectionExprClass: {
224 Out <<
"Array access";
228 Out <<
" results in " << BT.getArrayMsg();
231 case Stmt::UnaryOperatorClass: {
232 Out << BT.getDescription();
238 case Stmt::MemberExprClass: {
248 case Stmt::ObjCIvarRefExprClass: {
250 Out <<
"Access to instance variable '" << *IV->
getDecl() <<
"' results in "
260 auto BR = std::make_unique<PathSensitiveBugReport>(
261 BT, Buf.empty() ? BT.getDescription() : Buf.str(), N);
265 for (
const auto &R : Ranges)
268 C.emitReport(std::move(BR));
271void DereferenceChecker::checkLocation(SVal l,
bool isLoad,
const Stmt* S,
272 CheckerContext &
C)
const {
276 if (!suppressReport(
C, DerefExpr))
277 reportDerefBug(UndefBug,
C.getState(), DerefExpr,
C);
281 DefinedOrUnknownSVal location = l.
castAs<DefinedOrUnknownSVal>();
290 std::tie(notNullState, nullState) = state->assume(location);
297 if (!suppressReport(
C,
expr)) {
298 reportDerefBug(NullBug, nullState,
expr,
C);
306 if (ExplodedNode *N =
C.generateSink(nullState,
C.getPredecessor())) {
307 ImplicitNullDerefEvent
event = {l, isLoad, N, &
C.getBugReporter(),
309 dispatchEvent(event);
316 !suppressReport(
C, DerefExpr))
317 reportDerefBug(FixedAddressBug, notNullState, DerefExpr,
C);
322 C.addTransition(notNullState);
325void DereferenceChecker::checkBind(SVal L, SVal
V,
const Stmt *S,
326 bool AtDeclInit, CheckerContext &
C)
const {
332 if (
auto Label = L.
getAs<loc::GotoLabel>()) {
333 reportDerefBug(LabelBug,
C.getState(), S,
C);
338 const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
348 std::tie(StNonNull, StNull) = State->assume(
V.castAs<DefinedOrUnknownSVal>());
353 if (!suppressReport(
C,
expr)) {
354 reportDerefBug(NullBug, StNull,
expr,
C);
361 if (ExplodedNode *N =
C.generateSink(StNull,
C.getPredecessor())) {
362 ImplicitNullDerefEvent
event = {
V,
true, N,
365 dispatchEvent(event);
369 if (
V.isConstant()) {
371 if (!suppressReport(
C, DerefExpr))
372 reportDerefBug(FixedAddressBug, State, DerefExpr,
C);
392 C.addTransition(State,
this);
396template <>
struct format_provider<ValueDescStr> {
397 static void format(
const ValueDescStr &
V, raw_ostream &Stream,
399 static const char *ValueStr[2][3] = {
400 {
"zero",
"nonzero integer value",
"probably nonzero integer value"},
401 {
"null pointer",
"non-null pointer",
"probably non-null pointer"},
404 << ValueStr[
V.IsPointer][
V.IsNull.isConstrainedTrue()
406 : (
V.IsNull.isConstrainedFalse() ? 1 : 2)];
407 DereferenceChecker::AddDerefSource(Stream,
V.Ranges,
V.Ex,
V.State,
V.SF,
413void DereferenceChecker::checkPreStmt(
const BinaryOperator *Op,
414 CheckerContext &
C)
const {
417 const Expr *E1 = Op->
getLHS();
418 const Expr *E2 = Op->
getRHS();
430 ConditionTruthVal V1IsNull = State->isNull(
C.getSVal(E1));
431 ConditionTruthVal V2IsNull = State->isNull(
C.getSVal(E2));
432 bool IsConstrained =
true;
435 if (T1IsPointer && !T2IsPointer) {
442 if (!T1IsPointer && T2IsPointer) {
449 if (T1IsPointer && T2IsPointer) {
458 SmallVector<SourceRange, 2> Ranges;
459 const char *OpcodeStr =
460 Op->
getOpcode() == BO_Add ?
"Addition" :
"Subtraction";
461 const char *ResultStr = IsConstrained ?
"results" :
"may result";
462 ValueDescStr DerefArg1{Ranges, E1, State.get(),
C.getStackFrame(),
463 T1IsPointer, V1IsNull};
464 ValueDescStr DerefArg2{Ranges, E2, State.get(),
C.getStackFrame(),
465 T2IsPointer, V2IsNull};
467 llvm::formatv(
"{0} of a {1} and a {2} {3} in undefined behavior",
468 OpcodeStr, DerefArg1, DerefArg2, ResultStr);
470 ExplodedNode *N =
C.generateErrorNode(State);
474 std::make_unique<PathSensitiveBugReport>(NullPointerArithmBug, Msg, N);
479 for (
const auto &R : Ranges)
482 C.emitReport(std::move(BR));
485void ento::registerNullDereferenceChecker(CheckerManager &Mgr) {
486 Mgr.
getChecker<DereferenceChecker>()->NullDerefChecker.enable(Mgr);
489bool ento::shouldRegisterNullDereferenceChecker(
const CheckerManager &) {
493void ento::registerFixedAddressDereferenceChecker(CheckerManager &Mgr) {
494 Mgr.
getChecker<DereferenceChecker>()->FixedDerefChecker.enable(Mgr);
497bool ento::shouldRegisterFixedAddressDereferenceChecker(
498 const CheckerManager &) {
502void ento::registerNullPointerArithmChecker(CheckerManager &Mgr) {
503 Mgr.
getChecker<DereferenceChecker>()->NullPointerArithmChecker.enable(Mgr);
506bool ento::shouldRegisterNullPointerArithmChecker(
const CheckerManager &) {
static const Expr * getDereferenceExpr(const Stmt *S, bool IsBind=false)
static bool isDeclRefExprToReference(const Expr *E)
Expr * getBase()
Get base of the array section.
static bool isAdditiveOp(Opcode Opc)
This represents one expression.
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Expr * IgnoreParenLValueCasts() LLVM_READONLY
Skip past any parentheses and lvalue casts which might surround this expression until reaching a fixe...
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
SourceLocation getMemberLoc() const
getMemberLoc - Return the location of the "member", in X->F, it is the location of 'F'.
DeclarationNameInfo getMemberNameInfo() const
Retrieve the member declaration name info.
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
SourceLocation getLocation() const
const Expr * getBase() const
bool isVolatileQualified() const
Determine whether this type is volatile-qualified.
LangAS getAddressSpace() const
Return the address space of this type.
QualType getCanonicalType() const
bool hasAddressSpace() const
Check if this type has any address space qualifier.
It represents a stack frame of the call stack.
Stmt - This represents one statement.
StmtClass getStmtClass() const
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
bool isPointerType() const
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
bool isReferenceType() const
Expr * getSubExpr() const
Represents a variable declaration or definition.
Checker families (where a single backend class implements multiple related frontends) should derive f...
CHECKER * getChecker(AT &&...Args)
If the the singleton instance of a checker class is not yet constructed, then construct it (with the ...
bool isConstrainedFalse() const
Return true if the constraint is perfectly constrained to 'false'.
bool isConstrainedTrue() const
Return true if the constraint is perfectly constrained to 'true'.
const StackFrame * getStackFrame() const
ProgramState - This class encapsulates:
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
const MemRegion * getAsRegion() const
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
virtual QualType getValueType() const =0
Defines the clang::TargetInfo interface.
const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr
Matches expressions.
const Expr * getDerefExpr(const Stmt *S)
Given that expression S represents a pointer that would be dereferenced, try to find a sub-expression...
bool trackExpressionValue(const ExplodedNode *N, const Expr *E, PathSensitiveBugReport &R, TrackingOptions Opts={})
Attempts to add visitors to track expression value back to its point of origin.
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
std::pair< const clang::VarDecl *, const clang::Expr * > parseAssignment(const Stmt *S)
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
unsigned toTargetAddressSpace(LangAS AS)
U cast(CodeGen::Address addr)