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 LocationContext *LCtx,
61 bool loadedFrom =
false);
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"};
82 StringRef getDebugTag()
const override {
return "DereferenceChecker"; }
86 SmallVectorImpl<SourceRange> &Ranges;
88 const ProgramState *State;
89 const LocationContext *LCtx;
91 ConditionTruthVal IsNull;
97DereferenceChecker::AddDerefSource(raw_ostream &os,
107 case Stmt::DeclRefExprClass: {
109 if (
const VarDecl *VD = dyn_cast<VarDecl>(DR->
getDecl())) {
110 os <<
" (" << (loadedFrom ?
"loaded from" :
"from")
111 <<
" variable '" << VD->getName() <<
"')";
116 case Stmt::MemberExprClass: {
118 os <<
" (" << (loadedFrom ?
"loaded from" :
"via")
121 Ranges.push_back(SourceRange(L, L));
124 case Stmt::ObjCIvarRefExprClass: {
126 os <<
" (" << (loadedFrom ?
"loaded from" :
"via")
129 Ranges.push_back(SourceRange(L, L));
136 const Expr *E =
nullptr;
140 if (
const Expr *
expr = dyn_cast<Expr>(S))
141 E =
expr->IgnoreParenLValueCasts();
153bool DereferenceChecker::suppressReport(CheckerContext &
C,
154 const Expr *E)
const {
167 if (
C.getAnalysisManager()
168 .getAnalyzerOptions()
169 .ShouldSuppressAddressSpaceDereferences)
172 const llvm::Triple::ArchType
Arch =
173 C.getASTContext().getTargetInfo().getTriple().getArch();
175 if ((
Arch == llvm::Triple::x86) || (
Arch == llvm::Triple::x86_64)) {
187 if (
const auto *DRE = dyn_cast<DeclRefExpr>(E))
188 return DRE->getDecl()->getType()->isReferenceType();
192void DereferenceChecker::reportDerefBug(
const DerefBugType &BT,
194 CheckerContext &
C)
const {
195 if (&BT == &FixedAddressBug) {
208 ExplodedNode *N =
C.generateErrorNode(State);
212 SmallString<100> Buf;
213 llvm::raw_svector_ostream
Out(Buf);
215 SmallVector<SourceRange, 2> Ranges;
218 case Stmt::ArraySubscriptExprClass: {
219 Out <<
"Array access";
223 Out <<
" results in " << BT.getArrayMsg();
226 case Stmt::ArraySectionExprClass: {
227 Out <<
"Array access";
231 Out <<
" results in " << BT.getArrayMsg();
234 case Stmt::UnaryOperatorClass: {
235 Out << BT.getDescription();
241 case Stmt::MemberExprClass: {
251 case Stmt::ObjCIvarRefExprClass: {
253 Out <<
"Access to instance variable '" << *IV->
getDecl() <<
"' results in "
263 auto BR = std::make_unique<PathSensitiveBugReport>(
264 BT, Buf.empty() ? BT.getDescription() : Buf.str(), N);
268 for (
const auto &R : Ranges)
271 C.emitReport(std::move(BR));
274void DereferenceChecker::checkLocation(SVal l,
bool isLoad,
const Stmt* S,
275 CheckerContext &
C)
const {
279 if (!suppressReport(
C, DerefExpr))
280 reportDerefBug(UndefBug,
C.getState(), DerefExpr,
C);
284 DefinedOrUnknownSVal location = l.
castAs<DefinedOrUnknownSVal>();
293 std::tie(notNullState, nullState) = state->assume(location);
300 if (!suppressReport(
C,
expr)) {
301 reportDerefBug(NullBug, nullState,
expr,
C);
309 if (ExplodedNode *N =
C.generateSink(nullState,
C.getPredecessor())) {
310 ImplicitNullDerefEvent
event = {l, isLoad, N, &
C.getBugReporter(),
312 dispatchEvent(event);
318 if (!suppressReport(
C, DerefExpr))
319 reportDerefBug(FixedAddressBug, notNullState, DerefExpr,
C);
324 C.addTransition(notNullState);
327void DereferenceChecker::checkBind(SVal L, SVal
V,
const Stmt *S,
328 bool AtDeclInit, CheckerContext &
C)
const {
334 if (
auto Label = L.
getAs<loc::GotoLabel>()) {
335 reportDerefBug(LabelBug,
C.getState(), S,
C);
340 const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
350 std::tie(StNonNull, StNull) = State->assume(
V.castAs<DefinedOrUnknownSVal>());
355 if (!suppressReport(
C,
expr)) {
356 reportDerefBug(NullBug, StNull,
expr,
C);
363 if (ExplodedNode *N =
C.generateSink(StNull,
C.getPredecessor())) {
364 ImplicitNullDerefEvent
event = {
V,
true, N,
367 dispatchEvent(event);
371 if (
V.isConstant()) {
373 if (!suppressReport(
C, DerefExpr))
374 reportDerefBug(FixedAddressBug, State, DerefExpr,
C);
394 C.addTransition(State,
this);
398template <>
struct format_provider<ValueDescStr> {
399 static void format(
const ValueDescStr &
V, raw_ostream &Stream,
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"},
406 << ValueStr[
V.IsPointer][
V.IsNull.isConstrainedTrue()
408 : (
V.IsNull.isConstrainedFalse() ? 1 : 2)];
409 DereferenceChecker::AddDerefSource(Stream,
V.Ranges,
V.Ex,
V.State,
V.LCtx,
415void DereferenceChecker::checkPreStmt(
const BinaryOperator *Op,
416 CheckerContext &
C)
const {
419 const Expr *E1 = Op->
getLHS();
420 const Expr *E2 = Op->
getRHS();
432 ConditionTruthVal V1IsNull = State->isNull(
C.getSVal(E1));
433 ConditionTruthVal V2IsNull = State->isNull(
C.getSVal(E2));
434 bool IsConstrained =
true;
437 if (T1IsPointer && !T2IsPointer) {
444 if (!T1IsPointer && T2IsPointer) {
451 if (T1IsPointer && T2IsPointer) {
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};
469 llvm::formatv(
"{0} of a {1} and a {2} {3} in undefined behavior",
470 OpcodeStr, DerefArg1, DerefArg2, ResultStr);
472 ExplodedNode *N =
C.generateErrorNode(State);
476 std::make_unique<PathSensitiveBugReport>(NullPointerArithmBug, Msg, N);
481 for (
const auto &R : Ranges)
484 C.emitReport(std::move(BR));
487void ento::registerNullDereferenceChecker(CheckerManager &Mgr) {
488 Mgr.
getChecker<DereferenceChecker>()->NullDerefChecker.enable(Mgr);
491bool ento::shouldRegisterNullDereferenceChecker(
const CheckerManager &) {
495void ento::registerFixedAddressDereferenceChecker(CheckerManager &Mgr) {
496 Mgr.
getChecker<DereferenceChecker>()->FixedDerefChecker.enable(Mgr);
499bool ento::shouldRegisterFixedAddressDereferenceChecker(
500 const CheckerManager &) {
504void ento::registerNullPointerArithmChecker(CheckerManager &Mgr) {
505 Mgr.
getChecker<DereferenceChecker>()->NullPointerArithmChecker.enable(Mgr);
508bool 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.
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
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
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.
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 LocationContext * getLocationContext() 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)