22 using namespace clang;
26 enum class AllocKind {
36 static inline void Profile(AllocKind
X, FoldingSetNodeID &
ID) {
37 ID.AddInteger(
static_cast<int>(
X));
43 class PointerArithChecker
45 check::PreStmt<BinaryOperator>, check::PreStmt<UnaryOperator>,
46 check::PreStmt<ArraySubscriptExpr>, check::PreStmt<CastExpr>,
47 check::PostStmt<CastExpr>, check::PostStmt<CXXNewExpr>,
48 check::PostStmt<CallExpr>, check::DeadSymbols> {
50 const MemRegion *getArrayRegion(
const MemRegion *Region,
bool &Polymorphic,
51 AllocKind &AKind, CheckerContext &C)
const;
52 const MemRegion *getPointedRegion(
const MemRegion *Region,
53 CheckerContext &C)
const;
54 void reportPointerArithMisuse(
const Expr *E, CheckerContext &C,
55 bool PointedNeeded =
false)
const;
56 void initAllocIdentifiers(
ASTContext &C)
const;
58 mutable std::unique_ptr<BuiltinBug> BT_pointerArith;
59 mutable std::unique_ptr<BuiltinBug> BT_polyArray;
60 mutable llvm::SmallSet<IdentifierInfo *, 8> AllocFunctions;
63 void checkPreStmt(
const UnaryOperator *UOp, CheckerContext &C)
const;
64 void checkPreStmt(
const BinaryOperator *BOp, CheckerContext &C)
const;
66 void checkPreStmt(
const CastExpr *CE, CheckerContext &C)
const;
67 void checkPostStmt(
const CastExpr *CE, CheckerContext &C)
const;
68 void checkPostStmt(
const CXXNewExpr *
NE, CheckerContext &C)
const;
69 void checkPostStmt(
const CallExpr *CE, CheckerContext &C)
const;
70 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C)
const;
76 void PointerArithChecker::checkDeadSymbols(SymbolReaper &SR,
77 CheckerContext &C)
const {
90 AllocKind PointerArithChecker::getKindOfNewOp(
const CXXNewExpr *
NE,
94 if (isa<CXXMethodDecl>(FD))
99 return AllocKind::Array;
101 return AllocKind::SingleObject;
105 PointerArithChecker::getPointedRegion(
const MemRegion *Region,
106 CheckerContext &C)
const {
109 SVal S =
State->getSVal(Region);
110 return S.getAsRegion();
117 const MemRegion *PointerArithChecker::getArrayRegion(
const MemRegion *Region,
120 CheckerContext &C)
const {
122 while (
const auto *BaseRegion = dyn_cast<CXXBaseObjectRegion>(Region)) {
123 Region = BaseRegion->getSuperRegion();
126 if (
const auto *ElemRegion = dyn_cast<ElementRegion>(Region)) {
127 Region = ElemRegion->getSuperRegion();
131 if (
const AllocKind *
Kind =
State->get<RegionState>(Region)) {
133 if (*
Kind == AllocKind::Array)
140 if (isa<SymbolicRegion>(Region))
148 void PointerArithChecker::reportPointerArithMisuse(
const Expr *E,
150 bool PointedNeeded)
const {
156 const MemRegion *Region =
C.getSVal(E).getAsRegion();
160 Region = getPointedRegion(Region, C);
164 bool IsPolymorphic =
false;
166 if (
const MemRegion *ArrayRegion =
167 getArrayRegion(Region, IsPolymorphic,
Kind, C)) {
170 if (ExplodedNode *N =
C.generateNonFatalErrorNode()) {
172 BT_polyArray.reset(
new BuiltinBug(
173 this,
"Dangerous pointer arithmetic",
174 "Pointer arithmetic on a pointer to base class is dangerous "
175 "because derived and base class may have different size."));
176 auto R = std::make_unique<PathSensitiveBugReport>(
177 *BT_polyArray, BT_polyArray->getDescription(), N);
179 R->markInteresting(ArrayRegion);
180 C.emitReport(std::move(R));
185 if (
Kind == AllocKind::Reinterpreted)
189 if (
Kind != AllocKind::SingleObject &&
190 Region->getKind() == MemRegion::Kind::SymbolicRegionKind)
193 if (ExplodedNode *N =
C.generateNonFatalErrorNode()) {
194 if (!BT_pointerArith)
195 BT_pointerArith.reset(
new BuiltinBug(
this,
"Dangerous pointer arithmetic",
196 "Pointer arithmetic on non-array "
197 "variables relies on memory layout, "
198 "which is dangerous."));
199 auto R = std::make_unique<PathSensitiveBugReport>(
200 *BT_pointerArith, BT_pointerArith->getDescription(), N);
202 R->markInteresting(Region);
203 C.emitReport(std::move(R));
207 void PointerArithChecker::initAllocIdentifiers(
ASTContext &C)
const {
208 if (!AllocFunctions.empty())
210 AllocFunctions.insert(&
C.Idents.get(
"alloca"));
211 AllocFunctions.insert(&
C.Idents.get(
"malloc"));
212 AllocFunctions.insert(&
C.Idents.get(
"realloc"));
213 AllocFunctions.insert(&
C.Idents.get(
"calloc"));
214 AllocFunctions.insert(&
C.Idents.get(
"valloc"));
217 void PointerArithChecker::checkPostStmt(
const CallExpr *CE,
218 CheckerContext &C)
const {
224 initAllocIdentifiers(
C.getASTContext());
225 if (AllocFunctions.count(FunI) == 0)
228 SVal SV =
C.getSVal(CE);
229 const MemRegion *Region = SV.getAsRegion();
236 State =
State->set<RegionState>(Region, AllocKind::Array);
240 void PointerArithChecker::checkPostStmt(
const CXXNewExpr *
NE,
241 CheckerContext &C)
const {
246 AllocKind
Kind = getKindOfNewOp(
NE, FD);
249 SVal AllocedVal =
C.getSVal(
NE);
250 const MemRegion *Region = AllocedVal.getAsRegion();
257 void PointerArithChecker::checkPostStmt(
const CastExpr *CE,
258 CheckerContext &C)
const {
264 SVal CastedVal =
C.getSVal(CastedExpr);
266 const MemRegion *Region = CastedVal.getAsRegion();
271 State =
State->set<RegionState>(Region, AllocKind::Reinterpreted);
275 void PointerArithChecker::checkPreStmt(
const CastExpr *CE,
276 CheckerContext &C)
const {
277 if (CE->
getCastKind() != CastKind::CK_ArrayToPointerDecay)
282 SVal CastedVal =
C.getSVal(CastedExpr);
284 const MemRegion *Region = CastedVal.getAsRegion();
288 if (
const AllocKind *
Kind =
State->get<RegionState>(Region)) {
289 if (*
Kind == AllocKind::Array || *
Kind == AllocKind::Reinterpreted)
292 State =
State->set<RegionState>(Region, AllocKind::Array);
296 void PointerArithChecker::checkPreStmt(
const UnaryOperator *UOp,
297 CheckerContext &C)
const {
300 reportPointerArithMisuse(UOp->
getSubExpr(), C,
true);
304 CheckerContext &C)
const {
305 SVal Idx =
C.getSVal(SubsExpr->
getIdx());
308 if (Idx.isZeroConstant())
314 reportPointerArithMisuse(SubsExpr->
getBase(), C);
318 CheckerContext &C)
const {
320 if (!BOp->
isAdditiveOp() && OpKind != BO_AddAssign && OpKind != BO_SubAssign)
328 SVal RHSVal =
C.getSVal(Rhs);
329 if (
State->isNull(RHSVal).isConstrainedTrue())
335 SVal LHSVal =
C.getSVal(Lhs);
336 if (
State->isNull(LHSVal).isConstrainedTrue())
338 reportPointerArithMisuse(Rhs, C);
342 void ento::registerPointerArithChecker(CheckerManager &mgr) {
343 mgr.registerChecker<PointerArithChecker>();
346 bool ento::shouldRegisterPointerArithChecker(
const CheckerManager &mgr) {