1 | //=== PointerSubChecker.cpp - Pointer subtraction checker ------*- C++ -*--===// |
2 | // |
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 |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
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. |
12 | // |
13 | //===----------------------------------------------------------------------===// |
14 | |
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 "llvm/ADT/StringRef.h" |
21 | #include "llvm/Support/FormatVariadic.h" |
22 | |
23 | using namespace clang; |
24 | using namespace ento; |
25 | |
26 | namespace { |
27 | class PointerSubChecker |
28 | : public Checker< check::PreStmt<BinaryOperator> > { |
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 | |
34 | public: |
35 | void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; |
36 | }; |
37 | } |
38 | |
39 | void PointerSubChecker::checkPreStmt(const BinaryOperator *B, |
40 | CheckerContext &C) const { |
41 | // When doing pointer subtraction, if the two pointers do not point to the |
42 | // same array, emit a warning. |
43 | if (B->getOpcode() != BO_Sub) |
44 | return; |
45 | |
46 | SVal LV = C.getSVal(S: B->getLHS()); |
47 | SVal RV = C.getSVal(S: B->getRHS()); |
48 | |
49 | const MemRegion *LR = LV.getAsRegion(); |
50 | const MemRegion *RR = RV.getAsRegion(); |
51 | if (!LR || !RR) |
52 | return; |
53 | |
54 | // Allow subtraction of identical pointers. |
55 | if (LR == RR) |
56 | return; |
57 | |
58 | // No warning if one operand is unknown or resides in a region that could be |
59 | // equal to the other. |
60 | if (LR->getSymbolicBase() || RR->getSymbolicBase()) |
61 | return; |
62 | |
63 | if (!B->getLHS()->getType()->isPointerType() || |
64 | !B->getRHS()->getType()->isPointerType()) |
65 | return; |
66 | |
67 | const auto *ElemLR = dyn_cast<ElementRegion>(Val: LR); |
68 | const auto *ElemRR = dyn_cast<ElementRegion>(Val: RR); |
69 | |
70 | // Allow cases like "(&x + 1) - &x". |
71 | if (ElemLR && ElemLR->getSuperRegion() == RR) |
72 | return; |
73 | // Allow cases like "&x - (&x + 1)". |
74 | if (ElemRR && ElemRR->getSuperRegion() == LR) |
75 | return; |
76 | |
77 | const ValueDecl *DiffDeclL = nullptr; |
78 | const ValueDecl *DiffDeclR = nullptr; |
79 | |
80 | if (ElemLR && ElemRR) { |
81 | const MemRegion *SuperLR = ElemLR->getSuperRegion(); |
82 | const MemRegion *SuperRR = ElemRR->getSuperRegion(); |
83 | if (SuperLR == SuperRR) |
84 | return; |
85 | // Allow arithmetic on different symbolic regions. |
86 | if (isa<SymbolicRegion>(Val: SuperLR) || isa<SymbolicRegion>(Val: SuperRR)) |
87 | return; |
88 | if (const auto *SuperDLR = dyn_cast<DeclRegion>(Val: SuperLR)) |
89 | DiffDeclL = SuperDLR->getDecl(); |
90 | if (const auto *SuperDRR = dyn_cast<DeclRegion>(Val: SuperRR)) |
91 | DiffDeclR = SuperDRR->getDecl(); |
92 | } |
93 | |
94 | if (ExplodedNode *N = C.generateNonFatalErrorNode()) { |
95 | auto R = |
96 | std::make_unique<PathSensitiveBugReport>(args: BT, args: Msg_MemRegionDifferent, args&: N); |
97 | R->addRange(R: B->getSourceRange()); |
98 | // The declarations may be identical even if the regions are different: |
99 | // struct { int array[10]; } a, b; |
100 | // do_something(&a.array[5] - &b.array[5]); |
101 | // In this case don't emit notes. |
102 | if (DiffDeclL != DiffDeclR) { |
103 | auto AddNote = [&R, &C](const ValueDecl *D, StringRef SideStr) { |
104 | if (D) { |
105 | std::string Msg = llvm::formatv( |
106 | Fmt: "{0} at the {1}-hand side of subtraction" , |
107 | Vals: D->getType()->isArrayType() ? "Array" : "Object" , Vals&: SideStr); |
108 | R->addNote(Msg, Pos: {D, C.getSourceManager()}); |
109 | } |
110 | }; |
111 | AddNote(DiffDeclL, "left" ); |
112 | AddNote(DiffDeclR, "right" ); |
113 | } |
114 | C.emitReport(R: std::move(R)); |
115 | } |
116 | } |
117 | |
118 | void ento::registerPointerSubChecker(CheckerManager &mgr) { |
119 | mgr.registerChecker<PointerSubChecker>(); |
120 | } |
121 | |
122 | bool ento::shouldRegisterPointerSubChecker(const CheckerManager &mgr) { |
123 | return true; |
124 | } |
125 | |