xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp (revision e64bea71c21eb42e97aa615188ba91f6cce0d36d)
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(B->getLHS());
47   SVal RV = C.getSVal(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>(LR);
68   const auto *ElemRR = dyn_cast<ElementRegion>(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>(SuperLR) || isa<SymbolicRegion>(SuperRR))
87       return;
88     if (const auto *SuperDLR = dyn_cast<DeclRegion>(SuperLR))
89       DiffDeclL = SuperDLR->getDecl();
90     if (const auto *SuperDRR = dyn_cast<DeclRegion>(SuperRR))
91       DiffDeclR = SuperDRR->getDecl();
92   }
93 
94   if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
95     auto R =
96         std::make_unique<PathSensitiveBugReport>(BT, Msg_MemRegionDifferent, N);
97     R->addRange(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               "{0} at the {1}-hand side of subtraction",
107               D->getType()->isArrayType() ? "Array" : "Object", SideStr);
108           R->addNote(Msg, {D, C.getSourceManager()});
109         }
110       };
111       AddNote(DiffDeclL, "left");
112       AddNote(DiffDeclR, "right");
113     }
114     C.emitReport(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