1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
//=== StoreToImmutableChecker.cpp - Store to immutable memory ---*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines StoreToImmutableChecker, a checker that detects writes
// to immutable memory regions. This implements part of SEI CERT Rule ENV30-C.
//
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
using namespace clang;
using namespace ento;
namespace {
class StoreToImmutableChecker : public Checker<check::Bind> {
const BugType BT{this, "Write to immutable memory", "CERT Environment (ENV)"};
public:
void checkBind(SVal Loc, SVal Val, const Stmt *S, bool AtDeclInit,
CheckerContext &C) const;
};
} // end anonymous namespace
static bool isEffectivelyConstRegion(const MemRegion *MR, CheckerContext &C) {
if (isa<GlobalImmutableSpaceRegion>(MR))
return true;
// Check if this is a TypedRegion with a const-qualified type
if (const auto *TR = dyn_cast<TypedRegion>(MR)) {
QualType LocationType = TR->getDesugaredLocationType(C.getASTContext());
if (LocationType->isPointerOrReferenceType())
LocationType = LocationType->getPointeeType();
if (LocationType.isConstQualified())
return true;
}
// Check if this is a SymbolicRegion with a const-qualified pointee type
if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) {
QualType PointeeType = SR->getPointeeStaticType();
if (PointeeType.isConstQualified())
return true;
}
// NOTE: The above branches do not cover AllocaRegion. We do not need to check
// AllocaRegion, as it models untyped memory, that is allocated on the stack.
return false;
}
static const MemRegion *getInnermostConstRegion(const MemRegion *MR,
CheckerContext &C) {
while (true) {
if (isEffectivelyConstRegion(MR, C))
return MR;
if (auto *SR = dyn_cast<SubRegion>(MR))
MR = SR->getSuperRegion();
else
return nullptr;
}
}
static const DeclRegion *
getInnermostEnclosingConstDeclRegion(const MemRegion *MR, CheckerContext &C) {
while (true) {
if (const auto *DR = dyn_cast<DeclRegion>(MR)) {
const ValueDecl *D = DR->getDecl();
QualType DeclaredType = D->getType();
if (DeclaredType.isConstQualified())
return DR;
}
if (auto *SR = dyn_cast<SubRegion>(MR))
MR = SR->getSuperRegion();
else
return nullptr;
}
}
void StoreToImmutableChecker::checkBind(SVal Loc, SVal Val, const Stmt *S,
bool AtDeclInit,
CheckerContext &C) const {
// We are only interested in stores to memory regions
const MemRegion *MR = Loc.getAsRegion();
if (!MR)
return;
// Skip variable declarations and initializations - we only want to catch
// actual writes
if (AtDeclInit)
return;
// Check if the region is in the global immutable space
const MemSpaceRegion *MS = MR->getMemorySpace(C.getState());
const bool IsGlobalImmutableSpace = isa<GlobalImmutableSpaceRegion>(MS);
// Check if the region corresponds to a const variable
const MemRegion *InnermostConstRegion = getInnermostConstRegion(MR, C);
if (!IsGlobalImmutableSpace && !InnermostConstRegion)
return;
SmallString<64> WarningMessage{"Trying to write to immutable memory"};
if (IsGlobalImmutableSpace)
WarningMessage += " in global read-only storage";
// Generate the bug report
ExplodedNode *N = C.generateNonFatalErrorNode();
if (!N)
return;
auto R = std::make_unique<PathSensitiveBugReport>(BT, WarningMessage, N);
R->addRange(S->getSourceRange());
// Generate a note if the location that is being written to has a
// declaration or if it is a subregion of a const region with a declaration.
const DeclRegion *DR =
getInnermostEnclosingConstDeclRegion(InnermostConstRegion, C);
if (DR) {
const char *NoteMessage =
(DR != MR) ? "Enclosing memory region is declared as immutable here"
: "Memory region is declared as immutable here";
R->addNote(NoteMessage, PathDiagnosticLocation::create(
DR->getDecl(), C.getSourceManager()));
}
// For this checker, we are only interested in the value being written, no
// need to mark the value being assigned interesting.
C.emitReport(std::move(R));
}
void ento::registerStoreToImmutableChecker(CheckerManager &mgr) {
mgr.registerChecker<StoreToImmutableChecker>();
}
bool ento::shouldRegisterStoreToImmutableChecker(const CheckerManager &mgr) {
return true;
}
|