17
17
#include " clang/StaticAnalyzer/Core/Checker.h"
18
18
#include " clang/StaticAnalyzer/Core/CheckerManager.h"
19
19
#include " clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20
+ #include " clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
20
21
#include " llvm/ADT/StringRef.h"
21
22
22
23
using namespace clang ;
@@ -26,16 +27,88 @@ namespace {
26
27
class PointerSubChecker
27
28
: public Checker< check::PreStmt<BinaryOperator> > {
28
29
const BugType BT{this , " Pointer subtraction" };
30
+ const llvm::StringLiteral Msg_MemRegionDifferent =
31
+ " Subtraction of two pointers that do not point into the same array "
32
+ " is undefined behavior." ;
33
+ const llvm::StringLiteral Msg_LargeArrayIndex =
34
+ " Using an array index greater than the array size at pointer subtraction "
35
+ " is undefined behavior." ;
36
+ const llvm::StringLiteral Msg_NegativeArrayIndex =
37
+ " Using a negative array index at pointer subtraction "
38
+ " is undefined behavior." ;
39
+ const llvm::StringLiteral Msg_BadVarIndex =
40
+ " Indexing the address of a variable with other than 1 at this place "
41
+ " is undefined behavior." ;
42
+
43
+ bool checkArrayBounds (CheckerContext &C, const Expr *E,
44
+ const ElementRegion *ElemReg,
45
+ const MemRegion *Reg) const ;
46
+ void reportBug (CheckerContext &C, const Expr *E,
47
+ const llvm::StringLiteral &Msg) const ;
29
48
30
49
public:
31
50
void checkPreStmt (const BinaryOperator *B, CheckerContext &C) const ;
32
51
};
33
52
}
34
53
54
+ bool PointerSubChecker::checkArrayBounds (CheckerContext &C, const Expr *E,
55
+ const ElementRegion *ElemReg,
56
+ const MemRegion *Reg) const {
57
+ if (!ElemReg)
58
+ return true ;
59
+
60
+ ProgramStateRef State = C.getState ();
61
+ const MemRegion *SuperReg = ElemReg->getSuperRegion ();
62
+ SValBuilder &SVB = C.getSValBuilder ();
63
+
64
+ if (SuperReg == Reg) {
65
+ if (const llvm::APSInt *I = SVB.getKnownValue (State, ElemReg->getIndex ());
66
+ I && (!I->isOne () && !I->isZero ()))
67
+ reportBug (C, E, Msg_BadVarIndex);
68
+ return false ;
69
+ }
70
+
71
+ DefinedOrUnknownSVal ElemCount =
72
+ getDynamicElementCount (State, SuperReg, SVB, ElemReg->getElementType ());
73
+ auto IndexTooLarge = SVB.evalBinOp (C.getState (), BO_GT, ElemReg->getIndex (),
74
+ ElemCount, SVB.getConditionType ())
75
+ .getAs <DefinedOrUnknownSVal>();
76
+ if (IndexTooLarge) {
77
+ ProgramStateRef S1, S2;
78
+ std::tie (S1, S2) = C.getState ()->assume (*IndexTooLarge);
79
+ if (S1 && !S2) {
80
+ reportBug (C, E, Msg_LargeArrayIndex);
81
+ return false ;
82
+ }
83
+ }
84
+ auto IndexTooSmall = SVB.evalBinOp (State, BO_LT, ElemReg->getIndex (),
85
+ SVB.makeZeroVal (SVB.getArrayIndexType ()),
86
+ SVB.getConditionType ())
87
+ .getAs <DefinedOrUnknownSVal>();
88
+ if (IndexTooSmall) {
89
+ ProgramStateRef S1, S2;
90
+ std::tie (S1, S2) = State->assume (*IndexTooSmall);
91
+ if (S1 && !S2) {
92
+ reportBug (C, E, Msg_NegativeArrayIndex);
93
+ return false ;
94
+ }
95
+ }
96
+ return true ;
97
+ }
98
+
99
+ void PointerSubChecker::reportBug (CheckerContext &C, const Expr *E,
100
+ const llvm::StringLiteral &Msg) const {
101
+ if (ExplodedNode *N = C.generateNonFatalErrorNode ()) {
102
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
103
+ R->addRange (E->getSourceRange ());
104
+ C.emitReport (std::move (R));
105
+ }
106
+ }
107
+
35
108
void PointerSubChecker::checkPreStmt (const BinaryOperator *B,
36
109
CheckerContext &C) const {
37
110
// When doing pointer subtraction, if the two pointers do not point to the
38
- // same memory chunk , emit a warning.
111
+ // same array , emit a warning.
39
112
if (B->getOpcode () != BO_Sub)
40
113
return ;
41
114
@@ -44,28 +117,36 @@ void PointerSubChecker::checkPreStmt(const BinaryOperator *B,
44
117
45
118
const MemRegion *LR = LV.getAsRegion ();
46
119
const MemRegion *RR = RV.getAsRegion ();
47
-
48
- if (!(LR && RR))
120
+ if (!LR || !RR)
49
121
return ;
50
122
51
- const MemRegion *BaseLR = LR->getBaseRegion ();
52
- const MemRegion *BaseRR = RR->getBaseRegion ();
123
+ // Allow subtraction of identical pointers.
124
+ if (LR == RR)
125
+ return ;
53
126
54
- if (BaseLR == BaseRR)
127
+ // No warning if one operand is unknown.
128
+ if (isa<SymbolicRegion>(LR) || isa<SymbolicRegion>(RR))
55
129
return ;
56
130
57
- // Allow arithmetic on different symbolic regions.
58
- if (isa<SymbolicRegion>(BaseLR) || isa<SymbolicRegion>(BaseRR))
131
+ const auto *ElemLR = dyn_cast<ElementRegion>(LR);
132
+ const auto *ElemRR = dyn_cast<ElementRegion>(RR);
133
+
134
+ if (!checkArrayBounds (C, B->getLHS (), ElemLR, RR))
135
+ return ;
136
+ if (!checkArrayBounds (C, B->getRHS (), ElemRR, LR))
59
137
return ;
60
138
61
- if (ExplodedNode *N = C.generateNonFatalErrorNode ()) {
62
- constexpr llvm::StringLiteral Msg =
63
- " Subtraction of two pointers that do not point to the same memory "
64
- " chunk may cause incorrect result." ;
65
- auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
66
- R->addRange (B->getSourceRange ());
67
- C.emitReport (std::move (R));
139
+ if (ElemLR && ElemRR) {
140
+ const MemRegion *SuperLR = ElemLR->getSuperRegion ();
141
+ const MemRegion *SuperRR = ElemRR->getSuperRegion ();
142
+ if (SuperLR == SuperRR)
143
+ return ;
144
+ // Allow arithmetic on different symbolic regions.
145
+ if (isa<SymbolicRegion>(SuperLR) || isa<SymbolicRegion>(SuperRR))
146
+ return ;
68
147
}
148
+
149
+ reportBug (C, B, Msg_MemRegionDifferent);
69
150
}
70
151
71
152
void ento::registerPointerSubChecker (CheckerManager &mgr) {
0 commit comments