1 //=== PointerSubChecker.cpp - Pointer subtraction checker ------*- C++ -*--===//
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-exception
7 //===----------------------------------------------------------------------===//
9 // This files defines PointerSubChecker, a builtin checker that checks for
10 // pointer subtractions on two pointers pointing to different memory chunks.
11 // This check corresponds to CWE-469.
13 //===----------------------------------------------------------------------===//
15 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
16 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
17 #include "clang/StaticAnalyzer/Core/Checker.h"
18 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
21 #include "llvm/ADT/StringRef.h"
22 #include "llvm/Support/FormatVariadic.h"
24 using namespace clang
;
28 class PointerSubChecker
29 : public Checker
< check::PreStmt
<BinaryOperator
> > {
30 const BugType BT
{this, "Pointer subtraction"};
31 const llvm::StringLiteral Msg_MemRegionDifferent
=
32 "Subtraction of two pointers that do not point into the same array "
33 "is undefined behavior.";
36 void checkPreStmt(const BinaryOperator
*B
, CheckerContext
&C
) const;
40 void PointerSubChecker::checkPreStmt(const BinaryOperator
*B
,
41 CheckerContext
&C
) const {
42 // When doing pointer subtraction, if the two pointers do not point to the
43 // same array, emit a warning.
44 if (B
->getOpcode() != BO_Sub
)
47 SVal LV
= C
.getSVal(B
->getLHS());
48 SVal RV
= C
.getSVal(B
->getRHS());
50 const MemRegion
*LR
= LV
.getAsRegion();
51 const MemRegion
*RR
= RV
.getAsRegion();
55 // Allow subtraction of identical pointers.
59 // No warning if one operand is unknown or resides in a region that could be
60 // equal to the other.
61 if (LR
->getSymbolicBase() || RR
->getSymbolicBase())
64 if (!B
->getLHS()->getType()->isPointerType() ||
65 !B
->getRHS()->getType()->isPointerType())
68 const auto *ElemLR
= dyn_cast
<ElementRegion
>(LR
);
69 const auto *ElemRR
= dyn_cast
<ElementRegion
>(RR
);
71 // Allow cases like "(&x + 1) - &x".
72 if (ElemLR
&& ElemLR
->getSuperRegion() == RR
)
74 // Allow cases like "&x - (&x + 1)".
75 if (ElemRR
&& ElemRR
->getSuperRegion() == LR
)
78 const ValueDecl
*DiffDeclL
= nullptr;
79 const ValueDecl
*DiffDeclR
= nullptr;
81 if (ElemLR
&& ElemRR
) {
82 const MemRegion
*SuperLR
= ElemLR
->getSuperRegion();
83 const MemRegion
*SuperRR
= ElemRR
->getSuperRegion();
84 if (SuperLR
== SuperRR
)
86 // Allow arithmetic on different symbolic regions.
87 if (isa
<SymbolicRegion
>(SuperLR
) || isa
<SymbolicRegion
>(SuperRR
))
89 if (const auto *SuperDLR
= dyn_cast
<DeclRegion
>(SuperLR
))
90 DiffDeclL
= SuperDLR
->getDecl();
91 if (const auto *SuperDRR
= dyn_cast
<DeclRegion
>(SuperRR
))
92 DiffDeclR
= SuperDRR
->getDecl();
95 if (ExplodedNode
*N
= C
.generateNonFatalErrorNode()) {
97 std::make_unique
<PathSensitiveBugReport
>(BT
, Msg_MemRegionDifferent
, N
);
98 R
->addRange(B
->getSourceRange());
99 // The declarations may be identical even if the regions are different:
100 // struct { int array[10]; } a, b;
101 // do_something(&a.array[5] - &b.array[5]);
102 // In this case don't emit notes.
103 if (DiffDeclL
!= DiffDeclR
) {
104 auto AddNote
= [&R
, &C
](const ValueDecl
*D
, StringRef SideStr
) {
106 std::string Msg
= llvm::formatv(
107 "{0} at the {1}-hand side of subtraction",
108 D
->getType()->isArrayType() ? "Array" : "Object", SideStr
);
109 R
->addNote(Msg
, {D
, C
.getSourceManager()});
112 AddNote(DiffDeclL
, "left");
113 AddNote(DiffDeclR
, "right");
115 C
.emitReport(std::move(R
));
119 void ento::registerPointerSubChecker(CheckerManager
&mgr
) {
120 mgr
.registerChecker
<PointerSubChecker
>();
123 bool ento::shouldRegisterPointerSubChecker(const CheckerManager
&mgr
) {