Skip to content

Commit 1f2ad6d

Browse files
committed
[clang][analyzer] Add checker 'alpha.core.FixedAddressDereference'
1 parent a1345eb commit 1f2ad6d

File tree

5 files changed

+271
-22
lines changed

5 files changed

+271
-22
lines changed

clang/docs/analyzer/checkers.rst

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ The ``SuppressAddressSpaces`` option suppresses
129129
warnings for null dereferences of all pointers with address spaces. You can
130130
disable this behavior with the option
131131
``-analyzer-config core.NullDereference:SuppressAddressSpaces=false``.
132+
Value of this option is used for checker :ref:`_core-FixedAddressDereference`
133+
too.
132134
*Defaults to true*.
133135

134136
.. code-block:: objc
@@ -2919,6 +2921,42 @@ Check for assignment of a fixed address to a pointer.
29192921
p = (int *) 0x10000; // warn
29202922
}
29212923
2924+
.. _alpha-core-FixedAddressDereference:
2925+
2926+
alpha.core.FixedAddressDereference (C, C++, ObjC)
2927+
"""""""""""""""""""""""""""""""""""""""""""""""""
2928+
Check for dereferences of fixed values used as pointers.
2929+
2930+
Similarly as at :ref:`_core-NullDereference`, the checker specifically does
2931+
not report dereferences for x86 and x86-64 targets when the
2932+
address space is 256 (x86 GS Segment), 257 (x86 FS Segment), or 258 (x86 SS
2933+
segment). (See `X86/X86-64 Language Extensions
2934+
<https://clang.llvm.org/docs/LanguageExtensions.html#memory-references-to-specified-segments>`__
2935+
for reference.)
2936+
2937+
If you want to disable this behavior, set the ``SuppressAddressSpaces`` option
2938+
of checker ``core.NullDereference`` to false, like
2939+
``-analyzer-config core.NullDereference:SuppressAddressSpaces=false``. The value
2940+
of this option is used for both checkers.
2941+
2942+
.. code-block:: c
2943+
2944+
void test1() {
2945+
int *p = (int *)0x020;
2946+
int x = p[0]; // warn
2947+
}
2948+
2949+
void test2(int *p) {
2950+
if (p == (int *)-1)
2951+
*p = 0; // warn
2952+
}
2953+
2954+
void test3() {
2955+
int (*p_function)(char, char);
2956+
p_function = (int (*)(char, char))0x04080;
2957+
int x = (*p_function)('x', 'y'); // NO warning yet at functon pointer calls
2958+
}
2959+
29222960
.. _alpha-core-PointerArithm:
29232961
29242962
alpha.core.PointerArithm (C)

clang/include/clang/StaticAnalyzer/Checkers/Checkers.td

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -211,17 +211,15 @@ def DereferenceModeling : Checker<"DereferenceModeling">,
211211
Documentation<NotDocumented>,
212212
Hidden;
213213

214-
def NullDereferenceChecker : Checker<"NullDereference">,
215-
HelpText<"Check for dereferences of null pointers">,
216-
CheckerOptions<[
217-
CmdLineOption<Boolean,
218-
"SuppressAddressSpaces",
219-
"Suppresses warning when pointer dereferences an address space",
220-
"true",
221-
Released>
222-
]>,
223-
Documentation<HasDocumentation>,
224-
Dependencies<[DereferenceModeling]>;
214+
def NullDereferenceChecker
215+
: Checker<"NullDereference">,
216+
HelpText<"Check for dereferences of null pointers">,
217+
CheckerOptions<[CmdLineOption<
218+
Boolean, "SuppressAddressSpaces",
219+
"Suppresses warning when pointer dereferences an address space",
220+
"true", Released>]>,
221+
Documentation<HasDocumentation>,
222+
Dependencies<[DereferenceModeling]>;
225223

226224
def NonNullParamChecker : Checker<"NonNullParamChecker">,
227225
HelpText<"Check for null pointers passed as arguments to a function whose "
@@ -285,6 +283,12 @@ def FixedAddressChecker : Checker<"FixedAddr">,
285283
HelpText<"Check for assignment of a fixed address to a pointer">,
286284
Documentation<HasDocumentation>;
287285

286+
def FixedAddressDereferenceChecker
287+
: Checker<"FixedAddressDereference">,
288+
HelpText<"Check for dereferences of fixed values used as pointers">,
289+
Documentation<HasDocumentation>,
290+
Dependencies<[DereferenceModeling]>;
291+
288292
def PointerArithChecker : Checker<"PointerArithm">,
289293
HelpText<"Check for pointer arithmetic on locations other than array "
290294
"elements">,

clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,12 @@ class DereferenceChecker
3131
: public Checker< check::Location,
3232
check::Bind,
3333
EventDispatcher<ImplicitNullDerefEvent> > {
34-
enum DerefKind { NullPointer, UndefinedPointerValue, AddressOfLabel };
34+
enum DerefKind {
35+
NullPointer,
36+
UndefinedPointerValue,
37+
AddressOfLabel,
38+
FixedAddress
39+
};
3540

3641
void reportBug(DerefKind K, ProgramStateRef State, const Stmt *S,
3742
CheckerContext &C) const;
@@ -52,10 +57,12 @@ class DereferenceChecker
5257
bool SuppressAddressSpaces = false;
5358

5459
bool CheckNullDereference = false;
60+
bool CheckFixedDereference = false;
5561

5662
std::unique_ptr<BugType> BT_Null;
5763
std::unique_ptr<BugType> BT_Undef;
5864
std::unique_ptr<BugType> BT_Label;
65+
std::unique_ptr<BugType> BT_FixedAddress;
5966
};
6067
} // end anonymous namespace
6168

@@ -155,30 +162,47 @@ static bool isDeclRefExprToReference(const Expr *E) {
155162

156163
void DereferenceChecker::reportBug(DerefKind K, ProgramStateRef State,
157164
const Stmt *S, CheckerContext &C) const {
158-
if (!CheckNullDereference) {
159-
C.addSink();
160-
return;
161-
}
162-
163165
const BugType *BT = nullptr;
164166
llvm::StringRef DerefStr1;
165167
llvm::StringRef DerefStr2;
166168
switch (K) {
167169
case DerefKind::NullPointer:
170+
if (!CheckNullDereference) {
171+
C.addSink();
172+
return;
173+
}
168174
BT = BT_Null.get();
169175
DerefStr1 = " results in a null pointer dereference";
170176
DerefStr2 = " results in a dereference of a null pointer";
171177
break;
172178
case DerefKind::UndefinedPointerValue:
179+
if (!CheckNullDereference) {
180+
C.addSink();
181+
return;
182+
}
173183
BT = BT_Undef.get();
174184
DerefStr1 = " results in an undefined pointer dereference";
175185
DerefStr2 = " results in a dereference of an undefined pointer value";
176186
break;
177187
case DerefKind::AddressOfLabel:
188+
if (!CheckNullDereference) {
189+
C.addSink();
190+
return;
191+
}
178192
BT = BT_Label.get();
179193
DerefStr1 = " results in an undefined pointer dereference";
180194
DerefStr2 = " results in a dereference of an address of a label";
181195
break;
196+
case DerefKind::FixedAddress:
197+
// Deliberately don't add a sink node if check is disabled.
198+
// This situation may be valid in special cases.
199+
if (!CheckFixedDereference)
200+
return;
201+
202+
BT = BT_FixedAddress.get();
203+
DerefStr1 = " results in a dereference of a fixed address";
204+
DerefStr2 = " results in a dereference of a fixed address";
205+
break;
182206
};
183207

184208
// Generate an error node.
@@ -289,6 +313,13 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
289313
}
290314
}
291315

316+
if (location.isConstant()) {
317+
const Expr *DerefExpr = getDereferenceExpr(S, isLoad);
318+
if (!suppressReport(C, DerefExpr))
319+
reportBug(DerefKind::FixedAddress, notNullState, DerefExpr, C);
320+
return;
321+
}
322+
292323
// From this point forward, we know that the location is not null.
293324
C.addTransition(notNullState);
294325
}
@@ -337,6 +368,13 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
337368
}
338369
}
339370

371+
if (V.isConstant()) {
372+
const Expr *DerefExpr = getDereferenceExpr(S, true);
373+
if (!suppressReport(C, DerefExpr))
374+
reportBug(DerefKind::FixedAddress, State, DerefExpr, C);
375+
return;
376+
}
377+
340378
// Unlike a regular null dereference, initializing a reference with a
341379
// dereferenced null pointer does not actually cause a runtime exception in
342380
// Clang's implementation of references.
@@ -383,3 +421,16 @@ void ento::registerNullDereferenceChecker(CheckerManager &Mgr) {
383421
bool ento::shouldRegisterNullDereferenceChecker(const CheckerManager &) {
384422
return true;
385423
}
424+
425+
void ento::registerFixedAddressDereferenceChecker(CheckerManager &Mgr) {
426+
auto *Chk = Mgr.getChecker<DereferenceChecker>();
427+
Chk->CheckFixedDereference = true;
428+
Chk->BT_FixedAddress.reset(new BugType(Mgr.getCurrentCheckerName(),
429+
"Dereference of a fixed address",
430+
categories::LogicError));
431+
}
432+
433+
bool ento::shouldRegisterFixedAddressDereferenceChecker(
434+
const CheckerManager &) {
435+
return true;
436+
}
Lines changed: 159 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,163 @@
11
// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core -verify %s
2-
// expected-no-diagnostics
32

4-
void foo(void) {
3+
extern void __assert_fail (__const char *__assertion, __const char *__file,
4+
unsigned int __line, __const char *__function)
5+
__attribute__ ((__noreturn__));
6+
7+
#define assert(expr) \
8+
((expr) ? (void)(0) : __assert_fail (#expr, __FILE__, __LINE__, __func__))
9+
10+
typedef unsigned long uintptr_t;
11+
12+
void f0(void) {
513
int *p = (int*) 0x10000; // Should not crash here.
6-
*p = 3;
14+
*p = 3; // expected-warning{{Dereference of a fixed address}}
15+
}
16+
17+
void f1(int *p) {
18+
if (p != (int *)-1)
19+
*p = 1;
20+
else
21+
*p = 0; // expected-warning{{Dereference of a fixed address}}
22+
}
23+
24+
struct f2_struct {
25+
int x;
26+
};
27+
28+
int f2(struct f2_struct* p) {
29+
30+
if (p != (struct f2_struct *)1)
31+
p->x = 1;
32+
33+
return p->x++; // expected-warning{{Access to field 'x' results in a dereference of a fixed address (loaded from variable 'p')}}
34+
}
35+
36+
int f3_1(char* x) {
37+
int i = 2;
38+
39+
if (x != (char *)1)
40+
return x[i - 1];
41+
42+
return x[i+1]; // expected-warning{{Array access (from variable 'x') results in a dereference of a fixed address}}
43+
}
44+
45+
int f3_2(char* x) {
46+
int i = 2;
47+
48+
if (x != (char *)1)
49+
return x[i - 1];
50+
51+
return x[i+1]++; // expected-warning{{Array access (from variable 'x') results in a dereference of a fixed address}}
52+
}
53+
54+
int f4_1(int *p) {
55+
uintptr_t x = (uintptr_t) p;
56+
57+
if (x != (uintptr_t)1)
58+
return 1;
59+
60+
int *q = (int*) x;
61+
return *q; // expected-warning{{Dereference of a fixed address (loaded from variable 'q')}}
62+
}
63+
64+
int f4_2(void) {
65+
short array[2];
66+
uintptr_t x = (uintptr_t)array;
67+
short *p = (short *)x;
68+
69+
// The following branch should be infeasible.
70+
if (!(p == &array[0])) {
71+
p = (short *)1;
72+
*p = 1; // no-warning
73+
}
74+
75+
if (p != (short *)1) {
76+
*p = 5; // no-warning
77+
p = (short *)1; // expected-warning {{Using a fixed address is not portable}}
78+
}
79+
else return 1;
80+
81+
*p += 10; // expected-warning{{Dereference of a fixed}}
82+
return 0;
83+
}
84+
85+
int f5(void) {
86+
char *s = "hello world";
87+
return s[0]; // no-warning
88+
}
89+
90+
void f6(int *p, int *q) {
91+
if (p != (int *)1)
92+
if (p == (int *)1)
93+
*p = 1; // no-warning
94+
95+
if (q == (int *)1)
96+
if (q != (int *)1)
97+
*q = 1; // no-warning
98+
}
99+
100+
int* qux(int);
101+
102+
int f7_1(unsigned len) {
103+
assert (len != 0);
104+
int *p = (int *)1;
105+
unsigned i;
106+
107+
for (i = 0; i < len; ++i)
108+
p = qux(i);
109+
110+
return *p++; // no-warning
111+
}
112+
113+
int f7_2(unsigned len) {
114+
assert (len > 0); // note use of '>'
115+
int *p = (int *)1;
116+
unsigned i;
117+
118+
for (i = 0; i < len; ++i)
119+
p = qux(i);
120+
121+
return *p++; // no-warning
122+
}
123+
124+
struct f8_s {
125+
int x;
126+
int y[2];
127+
};
128+
129+
void f8(struct f8_s *s, int coin) {
130+
if (s != (struct f8_s *)7)
131+
return;
132+
133+
if (coin)
134+
s->x = 5; // expected-warning{{Access to field 'x' results in a dereference of a fixed address (loaded from variable 's')}}
135+
else
136+
s->y[1] = 6; // expected-warning{{Array access (via field 'y') results in a dereference of a fixed address}}
137+
}
138+
139+
void f9() {
140+
int (*p_function) (char, char) = (int (*)(char, char))0x04040; // FIXME: warn at this initialization
141+
p_function = (int (*)(char, char))0x04080; // expected-warning {{Using a fixed address is not portable}}
142+
// FIXME: there should be a warning from calling the function pointer with fixed address
143+
int x = (*p_function) ('x', 'y');
144+
}
145+
146+
#define AS_ATTRIBUTE volatile __attribute__((address_space(256)))
147+
#define _get_base() ((void * AS_ATTRIBUTE *)0x10)
148+
149+
void* test_address_space_array(unsigned long slot) {
150+
return _get_base()[slot]; // no-warning
151+
}
152+
void test_address_space_condition(int AS_ATTRIBUTE *cpu_data) {
153+
if (cpu_data == (int *)0x10) {
154+
*cpu_data = 3; // no-warning
155+
}
156+
}
157+
struct X { int member; };
158+
int test_address_space_member(void) {
159+
struct X AS_ATTRIBUTE *data = (struct X AS_ATTRIBUTE *)0x10UL;
160+
int ret;
161+
ret = data->member; // no-warning
162+
return ret;
7163
}

clang/test/Analysis/misc-ps.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// NOTE: Use '-fobjc-gc' to test the analysis being run twice, and multiple reports are not issued.
2-
// RUN: %clang_analyze_cc1 -triple i386-apple-darwin10 -analyzer-checker=core,alpha.core,osx.cocoa.AtSync -Wno-strict-prototypes -Wno-pointer-to-int-cast -verify -fblocks -Wno-unreachable-code -Wno-null-dereference -Wno-objc-root-class %s
3-
// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -analyzer-checker=core,alpha.core,osx.cocoa.AtSync -Wno-strict-prototypes -Wno-pointer-to-int-cast -verify -fblocks -Wno-unreachable-code -Wno-null-dereference -Wno-objc-root-class %s
2+
// RUN: %clang_analyze_cc1 -triple i386-apple-darwin10 -analyzer-checker=core,alpha.core,osx.cocoa.AtSync -analyzer-disable-checker=alpha.core.FixedAddressDereference -Wno-strict-prototypes -Wno-pointer-to-int-cast -verify -fblocks -Wno-unreachable-code -Wno-null-dereference -Wno-objc-root-class %s
3+
// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -analyzer-checker=core,alpha.core,osx.cocoa.AtSync -analyzer-disable-checker=alpha.core.FixedAddressDereference -Wno-strict-prototypes -Wno-pointer-to-int-cast -verify -fblocks -Wno-unreachable-code -Wno-null-dereference -Wno-objc-root-class %s
44

55
#ifndef __clang_analyzer__
66
#error __clang_analyzer__ not defined

0 commit comments

Comments
 (0)