1105 lines
34 KiB
HTML
1105 lines
34 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
|
|
"http://www.w3.org/TR/html4/strict.dtd">
|
|
<html>
|
|
<head>
|
|
<title>Alpha Checks</title>
|
|
<link type="text/css" rel="stylesheet" href="menu.css">
|
|
<link type="text/css" rel="stylesheet" href="content.css">
|
|
<script type="text/javascript" src="scripts/menu.js"></script>
|
|
<script type="text/javascript" src="scripts/expandcollapse.js"></script>
|
|
<style type="text/css">
|
|
tr:first-child { width:20%; }
|
|
</style>
|
|
</head>
|
|
<body onload="initExpandCollapse()">
|
|
|
|
<div id="page">
|
|
<!--#include virtual="menu.html.incl"-->
|
|
|
|
<div id="content">
|
|
<h1>Alpha Checkers</h1>
|
|
Experimental checkers in addition to the <a href = "available_checks.html">
|
|
Default Checkers</a>. These are checkers with known issues or limitations that
|
|
keep them from being on by default. They are likely to have false positives.
|
|
Bug reports are welcome but will likely not be investigated for some time.
|
|
Patches welcome!
|
|
<ul>
|
|
<li><a href="#clone_alpha_checkers">Clone Alpha Checkers</a></li>
|
|
<li><a href="#core_alpha_checkers">Core Alpha Checkers</a></li>
|
|
<li><a href="#cplusplus_alpha_checkers">C++ Alpha Checkers</a></li>
|
|
<li><a href="#llvm_alpha_checkers">LLVM Checkers</a></li>
|
|
<li><a href="#valist_alpha_checkers">Variable Argument Alpha Checkers</a></li>
|
|
<li><a href="#deadcode_alpha_checkers">Dead Code Alpha Checkers</a></li>
|
|
<li><a href="#osx_alpha_checkers">OS X Alpha Checkers</a></li>
|
|
<li><a href="#security_alpha_checkers">Security Alpha Checkers</a></li>
|
|
<li><a href="#unix_alpha_checkers">Unix Alpha Checkers</a></li>
|
|
<li><a href="#nondeterminism_alpha_checkers">Non-determinism Alpha Checkers</a></li>
|
|
</ul>
|
|
|
|
<!-- ============================= clone alpha ============================= -->
|
|
|
|
<h3 id="clone_alpha_checkers">Clone Alpha Checkers</h3>
|
|
<table class="checkers">
|
|
<colgroup><col class="namedescr"><col class="example"></colgroup>
|
|
<thead><tr><td>Name, Description</td><td>Example</td></tr></thead>
|
|
|
|
<tbody>
|
|
<tr><td><a id="alpha.clone.CloneChecker"><div class="namedescr expandable"><span class="name">
|
|
alpha.clone.CloneChecker</span><span class="lang">
|
|
(C, C++, ObjC)</span><div class="descr">
|
|
Reports similar pieces of code.</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void log();
|
|
|
|
int max(int a, int b) { // warn
|
|
log();
|
|
if (a > b)
|
|
return a;
|
|
return b;
|
|
}
|
|
|
|
int maxClone(int x, int y) { // similar code here
|
|
log();
|
|
if (x > y)
|
|
return x;
|
|
return y;
|
|
}
|
|
</pre></div></div></td></tr>
|
|
</tbody></table>
|
|
|
|
<!-- ============================= core alpha ============================= -->
|
|
<h3 id="core_alpha_checkers">Core Alpha Checkers</h3>
|
|
<table class="checkers">
|
|
<colgroup><col class="namedescr"><col class="example"></colgroup>
|
|
<thead><tr><td>Name, Description</td><td>Example</td></tr></thead>
|
|
|
|
<tbody>
|
|
<tr><td><a id="alpha.core.BoolAssignment"><div class="namedescr expandable"><span class="name">
|
|
alpha.core.BoolAssignment</span><span class="lang">
|
|
(ObjC)</span><div class="descr">
|
|
Warn about assigning non-{0,1} values to boolean variables.</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void test() {
|
|
BOOL b = -1; // warn
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.core.CallAndMessageUnInitRefArg"><div class="namedescr expandable"><span class="name">
|
|
alpha.core.CallAndMessageUnInitRefArg</span><span class="lang">
|
|
(C, C++)</span><div class="descr">
|
|
Check for uninitialized arguments in function calls and Objective-C
|
|
message expressions.</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void test(void) {
|
|
int t;
|
|
int &p = t;
|
|
int &s = p;
|
|
int &q = s;
|
|
foo(q); // warn
|
|
}
|
|
</pre></div><div class="separator"></div>
|
|
<div class="example"><pre>
|
|
void test(void) {
|
|
int x;
|
|
foo(&x); // warn
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.core.CastSize"><div class="namedescr expandable"><span class="name">
|
|
alpha.core.CastSize</span><span class="lang">
|
|
(C)</span><div class="descr">
|
|
Check when casting a malloc'ed type T, whether the size is a multiple of the
|
|
size of T (Works only with <span class="name">unix.Malloc</span>
|
|
or <span class="name">alpha.unix.MallocWithAnnotations</span>
|
|
checks enabled).</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void test() {
|
|
int *x = (int *)malloc(11); // warn
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.core.CastToStruct"><div class="namedescr expandable"><span class="name">
|
|
alpha.core.CastToStruct</span><span class="lang">
|
|
(C, C++)</span><div class="descr">
|
|
Check for cast from non-struct pointer to struct pointer.</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
// C
|
|
struct s {};
|
|
|
|
void test(int *p) {
|
|
struct s *ps = (struct s *) p; // warn
|
|
}
|
|
</pre></div><div class="separator"></div>
|
|
<div class="example"><pre>
|
|
// C++
|
|
class c {};
|
|
|
|
void test(int *p) {
|
|
c *pc = (c *) p; // warn
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.core.Conversion"><div class="namedescr expandable"><span class="name">
|
|
alpha.core.Conversion</span><span class="lang">
|
|
(C, C++, ObjC)</span><div class="descr">
|
|
Loss of sign or precision in implicit conversions</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void test(unsigned U, signed S) {
|
|
if (S > 10) {
|
|
if (U < S) {
|
|
}
|
|
}
|
|
if (S < -10) {
|
|
if (U < S) { // warn (loss of sign)
|
|
}
|
|
}
|
|
}
|
|
</pre></div><div class="separator"></div>
|
|
<div class="example"><pre>
|
|
void test() {
|
|
long long A = 1LL << 60;
|
|
short X = A; // warn (loss of precision)
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.core.DynamicTypeChecker"><div class="namedescr expandable"><span class="name">
|
|
alpha.core.DynamicTypeChecker</span><span class="lang">
|
|
(ObjC)</span><div class="descr">
|
|
Check for cases where the dynamic and the static type of an
|
|
object are unrelated.</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
id date = [NSDate date];
|
|
|
|
// Warning: Object has a dynamic type 'NSDate *' which is
|
|
// incompatible with static type 'NSNumber *'"
|
|
NSNumber *number = date;
|
|
[number doubleValue];
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.core.FixedAddr"><div class="namedescr expandable"><span class="name">
|
|
alpha.core.FixedAddr</span><span class="lang">
|
|
(C)</span><div class="descr">
|
|
Check for assignment of a fixed address to a pointer.</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void test() {
|
|
int *p;
|
|
p = (int *) 0x10000; // warn
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.core.IdenticalExpr"><div class="namedescr expandable"><span class="name">
|
|
alpha.core.IdenticalExpr</span><span class="lang">
|
|
(C, C++)</span><div class="descr">
|
|
Warn about suspicious uses of identical expressions.</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
// C
|
|
void test() {
|
|
int a = 5;
|
|
int b = a | 4 | a; // warn: identical expr on both sides
|
|
}
|
|
</pre></div><div class="separator"></div>
|
|
<div class="example"><pre>
|
|
// C++
|
|
bool f(void);
|
|
|
|
void test(bool b) {
|
|
int i = 10;
|
|
if (f()) { // warn: true and false branches are identical
|
|
do {
|
|
i--;
|
|
} while (f());
|
|
} else {
|
|
do {
|
|
i--;
|
|
} while (f());
|
|
}
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.core.PointerArithm"><div class="namedescr expandable"><span class="name">
|
|
alpha.core.PointerArithm</span><span class="lang">
|
|
(C)</span><div class="descr">
|
|
Check for pointer arithmetic on locations other than array
|
|
elements.</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void test() {
|
|
int x;
|
|
int *p;
|
|
p = &x + 1; // warn
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.core.PointerSub"><div class="namedescr expandable"><span class="name">
|
|
alpha.core.PointerSub</span><span class="lang">
|
|
(C)</span><div class="descr">
|
|
Check for pointer subtractions on two pointers pointing to different memory
|
|
chunks.</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void test() {
|
|
int x, y;
|
|
int d = &y - &x; // warn
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.core.SizeofPtr"><div class="namedescr expandable"><span class="name">
|
|
alpha.core.SizeofPtr</span><span class="lang">
|
|
(C)</span><div class="descr">
|
|
Warn about unintended use of <code>sizeof()</code> on pointer
|
|
expressions.</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
struct s {};
|
|
|
|
int test(struct s *p) {
|
|
return sizeof(p);
|
|
// warn: sizeof(ptr) can produce an unexpected result
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.core.StackAddressAsyncEscape"><div class="namedescr expandable"><span class="name">
|
|
alpha.core.StackAddressAsyncEscape</span><span class="lang">
|
|
(C)</span><div class="descr">
|
|
Check that addresses to stack memory do not escape the function that involves
|
|
<code>dispatch_after</code> or <code>dispatch_async</code>. This checker is
|
|
a part of core.StackAddressEscape, but is
|
|
<a href=https://reviews.llvm.org/D41042>temporarily disabled</a> until some
|
|
false positives are fixed.</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
dispatch_block_t test_block_inside_block_async_leak() {
|
|
int x = 123;
|
|
void (^inner)(void) = ^void(void) {
|
|
int y = x;
|
|
++y;
|
|
};
|
|
void (^outer)(void) = ^void(void) {
|
|
int z = x;
|
|
++z;
|
|
inner();
|
|
};
|
|
return outer; // warn: address of stack-allocated block is captured by a
|
|
// returned block
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.core.TestAfterDivZero"><div class="namedescr expandable"><span class="name">
|
|
alpha.core.TestAfterDivZero</span><span class="lang">
|
|
(C, C++, ObjC)</span><div class="descr">
|
|
Check for division by variable that is later compared against 0.
|
|
Either the comparison is useless or there is division by zero.
|
|
</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void test(int x) {
|
|
var = 77 / x;
|
|
if (x == 0) { } // warn
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
</tbody></table>
|
|
|
|
<!-- =========================== cplusplus alpha =========================== -->
|
|
<h3 id="cplusplus_alpha_checkers">C++ Alpha Checkers</h3>
|
|
<table class="checkers">
|
|
<colgroup><col class="namedescr"><col class="example"></colgroup>
|
|
<thead><tr><td>Name, Description</td><td>Example</td></tr></thead>
|
|
<tbody>
|
|
|
|
|
|
<tr><td><a id="alpha.cplusplus.DeleteWithNonVirtualDtor"><div class="namedescr expandable"><span class="name">
|
|
alpha.cplusplus.DeleteWithNonVirtualDtor</span><span class="lang">
|
|
(C++)</span><div class="descr">
|
|
Reports destructions of polymorphic objects with a non-virtual destructor in
|
|
their base class
|
|
</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
NonVirtual *create() {
|
|
NonVirtual *x = new NVDerived(); // note: conversion from derived to base
|
|
// happened here
|
|
return x;
|
|
}
|
|
|
|
void sink(NonVirtual *x) {
|
|
delete x; // warn: destruction of a polymorphic object with no virtual
|
|
// destructor
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
<tr><td><a id="alpha.cplusplus.EnumCastOutOfRange"><div class="namedescr expandable"><span class="name">
|
|
alpha.cplusplus.EnumCastOutOfRange</span><span class="lang">
|
|
(C++)</span><div class="descr">
|
|
Check for integer to enumeration casts that could result in undefined values.
|
|
</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
enum TestEnum {
|
|
A = 0
|
|
};
|
|
|
|
void foo() {
|
|
TestEnum t = static_cast<TestEnum>(-1);
|
|
// warn: the value provided to the cast expression is not in
|
|
the valid range of values for the enum
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.cplusplus.InvalidatedIterator"><div class="namedescr expandable"><span class="name">
|
|
alpha.cplusplus.InvalidatedIterator</span><span class="lang">
|
|
(C++)</span><div class="descr">
|
|
Check for use of invalidated iterators.
|
|
</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void bad_copy_assign_operator_list1(std::list<int> &L1,
|
|
const std::list<int> &L2) {
|
|
auto i0 = L1.cbegin();
|
|
L1 = L2;
|
|
*i0; // warn: invalidated iterator accessed
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.cplusplus.IteratorRange"><div class="namedescr expandable"><span class="name">
|
|
alpha.cplusplus.IteratorRange</span><span class="lang">
|
|
(C++)</span><div class="descr">
|
|
Check for iterators used outside their valid ranges.
|
|
</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void simple_bad_end(const std::vector<int> &v) {
|
|
auto i = v.end();
|
|
*i; // warn: iterator accessed outside of its range
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.cplusplus.MismatchedIterator"><div class="namedescr expandable"><span class="name">
|
|
alpha.cplusplus.MismatchedIterator</span><span class="lang">
|
|
(C++)</span><div class="descr">
|
|
Check for use of iterators of different containers where iterators of the same
|
|
container are expected.
|
|
</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void bad_insert3(std::vector<int> &v1, std::vector<int> &v2) {
|
|
v2.insert(v1.cbegin(), v2.cbegin(), v2.cend()); // warn: container accessed
|
|
// using foreign
|
|
// iterator argument
|
|
v1.insert(v1.cbegin(), v1.cbegin(), v2.cend()); // warn: iterators of
|
|
// different containers
|
|
// used where the same
|
|
// container is
|
|
// expected
|
|
v1.insert(v1.cbegin(), v2.cbegin(), v1.cend()); // warn: iterators of
|
|
// different containers
|
|
// used where the same
|
|
// container is
|
|
// expected
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.cplusplus.Move"><div class="namedescr expandable"><span class="name">
|
|
alpha.cplusplus.Move</span><span class="lang">
|
|
(C++)</span><div class="descr">
|
|
Method calls on a moved-from object and copying a moved-from object will be
|
|
reported.
|
|
</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
struct A {
|
|
void foo() {}
|
|
};
|
|
|
|
void f() {
|
|
A a;
|
|
A b = std::move(a); // note: 'a' became 'moved-from' here
|
|
a.foo(); // warn: method call on a 'moved-from' object 'a'
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
</tbody></table>
|
|
|
|
|
|
<!-- =========================== dead code alpha =========================== -->
|
|
<h3 id="deadcode_alpha_checkers">Dead Code Alpha Checkers</h3>
|
|
<table class="checkers">
|
|
<colgroup><col class="namedescr"><col class="example"></colgroup>
|
|
<thead><tr><td>Name, Description</td><td>Example</td></tr></thead>
|
|
|
|
<tbody>
|
|
<tr><td><a id="alpha.deadcode.UnreachableCode"><div class="namedescr expandable"><span class="name">
|
|
alpha.deadcode.UnreachableCode</span><span class="lang">
|
|
(C, C++, ObjC)</span><div class="descr">
|
|
Check unreachable code.</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
// C
|
|
int test() {
|
|
int x = 1;
|
|
while(x);
|
|
return x; // warn
|
|
}
|
|
</pre></div><div class="separator"></div>
|
|
<div class="example"><pre>
|
|
// C++
|
|
void test() {
|
|
int a = 2;
|
|
|
|
while (a > 1)
|
|
a--;
|
|
|
|
if (a > 1)
|
|
a++; // warn
|
|
}
|
|
</pre></div><div class="separator"></div>
|
|
<div class="example"><pre>
|
|
// Objective-C
|
|
void test(id x) {
|
|
return;
|
|
[x retain]; // warn
|
|
}
|
|
</pre></div></div></td></tr>
|
|
</tbody></table>
|
|
|
|
<!-- =========================== llvm alpha =========================== -->
|
|
<h3 id="llvm_alpha_checkers">LLVM Checkers</h3>
|
|
<table class="checkers">
|
|
<colgroup><col class="namedescr"><col class="example"></colgroup>
|
|
<thead><tr><td>Name, Description</td><td>Example</td></tr></thead>
|
|
|
|
<tbody>
|
|
<tr><td><a id="alpha.llvm.Conventions"><div class="namedescr expandable"><span class="name">
|
|
alpha.llvm.Conventions</span><span class="lang">
|
|
(C)</span><div class="descr">
|
|
Check code for LLVM codebase conventions:
|
|
<ul>
|
|
<li>A <code>StringRef</code> should not be bound to a temporary std::string
|
|
whose lifetime is shorter than the <code>StringRef</code>'s.</li>
|
|
<li>Clang AST nodes should not have fields that can allocate memory.</li>
|
|
</ul>
|
|
</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
<!-- TODO: Add examples, as currently it's hard to get this checker working. -->
|
|
</pre></div></div></td></tr>
|
|
|
|
</tbody></table>
|
|
|
|
|
|
<!-- ============================== OS X alpha ============================== -->
|
|
<h3 id="osx_alpha_checkers">OS X Alpha Checkers</h3>
|
|
<table class="checkers">
|
|
<colgroup><col class="namedescr"><col class="example"></colgroup>
|
|
<thead><tr><td>Name, Description</td><td>Example</td></tr></thead>
|
|
|
|
<tbody>
|
|
<tr><td><a id="alpha.osx.cocoa.DirectIvarAssignment"><div class="namedescr expandable"><span class="name">
|
|
alpha.osx.cocoa.DirectIvarAssignment</span><span class="lang">
|
|
(ObjC)</span><div class="descr">
|
|
Check that Objective C properties follow the following rule: the property
|
|
should be set with the setter, not though a direct assignment.</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
@interface MyClass : NSObject {}
|
|
@property (readonly) id A;
|
|
- (void) foo;
|
|
@end
|
|
|
|
@implementation MyClass
|
|
- (void) foo {
|
|
_A = 0; // warn
|
|
}
|
|
@end
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.osx.cocoa.DirectIvarAssignmentForAnnotatedFunctions"><div class="namedescr expandable"><span class="name">
|
|
alpha.osx.cocoa.DirectIvarAssignmentForAnnotatedFunctions</span><span class="lang">
|
|
(ObjC)</span><div class="descr">
|
|
Check for direct assignments to instance variables in the methods annotated
|
|
with <code>objc_no_direct_instance_variable_assignment</code>.</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
@interface MyClass : NSObject {}
|
|
@property (readonly) id A;
|
|
- (void) fAnnotated __attribute__((
|
|
annotate("objc_no_direct_instance_variable_assignment")));
|
|
- (void) fNotAnnotated;
|
|
@end
|
|
|
|
@implementation MyClass
|
|
- (void) fAnnotated {
|
|
_A = 0; // warn
|
|
}
|
|
- (void) fNotAnnotated {
|
|
_A = 0; // no warn
|
|
}
|
|
@end
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.osx.cocoa.InstanceVariableInvalidation"><div class="namedescr expandable"><span class="name">
|
|
alpha.osx.cocoa.InstanceVariableInvalidation</span><span class="lang">
|
|
(ObjC)</span><div class="descr">
|
|
Check that the invalidatable instance variables are invalidated in the methods
|
|
annotated with <code>objc_instance_variable_invalidator</code>.</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
@protocol Invalidation <NSObject>
|
|
- (void) invalidate
|
|
__attribute__((annotate("objc_instance_variable_invalidator")));
|
|
@end
|
|
|
|
@interface InvalidationImpObj : NSObject <Invalidation>
|
|
@end
|
|
|
|
@interface SubclassInvalidationImpObj : InvalidationImpObj {
|
|
InvalidationImpObj *var;
|
|
}
|
|
- (void)invalidate;
|
|
@end
|
|
|
|
@implementation SubclassInvalidationImpObj
|
|
- (void) invalidate {}
|
|
@end
|
|
// warn: var needs to be invalidated or set to nil
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.osx.cocoa.MissingInvalidationMethod"><div class="namedescr expandable"><span class="name">
|
|
alpha.osx.cocoa.MissingInvalidationMethod</span><span class="lang">
|
|
(ObjC)</span><div class="descr">
|
|
Check that the invalidation methods are present in classes that contain
|
|
invalidatable instance variables.</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
@protocol Invalidation <NSObject>
|
|
- (void)invalidate
|
|
__attribute__((annotate("objc_instance_variable_invalidator")));
|
|
@end
|
|
|
|
@interface NeedInvalidation : NSObject <Invalidation>
|
|
@end
|
|
|
|
@interface MissingInvalidationMethodDecl : NSObject {
|
|
NeedInvalidation *Var; // warn
|
|
}
|
|
@end
|
|
|
|
@implementation MissingInvalidationMethodDecl
|
|
@end
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.osx.cocoa.localizability.PluralMisuseChecker"><div class="namedescr expandable"><span class="name">
|
|
alpha.osx.cocoa.localizability.PluralMisuseChecker</span><span class="lang">
|
|
(ObjC)</span><div class="descr">
|
|
Warns against using one vs. many plural pattern in code
|
|
when generating localized strings.
|
|
</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
NSString *reminderText =
|
|
NSLocalizedString(@"None", @"Indicates no reminders");
|
|
if (reminderCount == 1) {
|
|
// Warning: Plural cases are not supported across all languages.
|
|
// Use a .stringsdict file instead
|
|
reminderText =
|
|
NSLocalizedString(@"1 Reminder", @"Indicates single reminder");
|
|
} else if (reminderCount >= 2) {
|
|
// Warning: Plural cases are not supported across all languages.
|
|
// Use a .stringsdict file instead
|
|
reminderText =
|
|
[NSString stringWithFormat:
|
|
NSLocalizedString(@"%@ Reminders", @"Indicates multiple reminders"),
|
|
reminderCount];
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
</tbody></table>
|
|
|
|
<!-- =========================== security alpha =========================== -->
|
|
<h3 id="security_alpha_checkers">Security Alpha Checkers</h3>
|
|
<table class="checkers">
|
|
<colgroup><col class="namedescr"><col class="example"></colgroup>
|
|
<thead><tr><td>Name, Description</td><td>Example</td></tr></thead>
|
|
|
|
<tbody>
|
|
<tr><td><a id="alpha.security.ArrayBound"><div class="namedescr expandable"><span class="name">
|
|
alpha.security.ArrayBound</span><span class="lang">
|
|
(C)</span><div class="descr">
|
|
Warn about buffer overflows (older checker).</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void test() {
|
|
char *s = "";
|
|
char c = s[1]; // warn
|
|
}
|
|
</pre></div><div class="separator"></div>
|
|
<div class="example"><pre>
|
|
struct seven_words {
|
|
int c[7];
|
|
};
|
|
|
|
void test() {
|
|
struct seven_words a, *p;
|
|
p = &a;
|
|
p[0] = a;
|
|
p[1] = a;
|
|
p[2] = a; // warn
|
|
}
|
|
</pre></div><div class="separator"></div>
|
|
<div class="example"><pre>
|
|
// note: requires unix.Malloc or
|
|
// alpha.unix.MallocWithAnnotations checks enabled.
|
|
void test() {
|
|
int *p = malloc(12);
|
|
p[3] = 4; // warn
|
|
}
|
|
</pre></div><div class="separator"></div>
|
|
<div class="example"><pre>
|
|
void test() {
|
|
char a[2];
|
|
int *b = (int*)a;
|
|
b[1] = 3; // warn
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.security.ArrayBoundV2"><div class="namedescr expandable"><span class="name">
|
|
alpha.security.ArrayBoundV2</span><span class="lang">
|
|
(C)</span><div class="descr">
|
|
Warn about buffer overflows (newer checker).</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void test() {
|
|
char *s = "";
|
|
char c = s[1]; // warn
|
|
}
|
|
</pre></div><div class="separator"></div>
|
|
<div class="example"><pre>
|
|
void test() {
|
|
int buf[100];
|
|
int *p = buf;
|
|
p = p + 99;
|
|
p[1] = 1; // warn
|
|
}
|
|
</pre></div><div class="separator"></div>
|
|
<div class="example"><pre>
|
|
// note: compiler has internal check for this.
|
|
// Use -Wno-array-bounds to suppress compiler warning.
|
|
void test() {
|
|
int buf[100][100];
|
|
buf[0][-1] = 1; // warn
|
|
}
|
|
</pre></div><div class="separator"></div>
|
|
<div class="example"><pre>
|
|
// note: requires alpha.security.taint check turned on.
|
|
void test() {
|
|
char s[] = "abc";
|
|
int x = getchar();
|
|
char c = s[x]; // warn: index is tainted
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.security.MallocOverflow"><div class="namedescr expandable"><span class="name">
|
|
alpha.security.MallocOverflow</span><span class="lang">
|
|
(C)</span><div class="descr">
|
|
Check for overflows in the arguments to <code>malloc()</code>.</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void test(int n) {
|
|
void *p = malloc(n * sizeof(int)); // warn
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.security.MmapWriteExec"><div class="namedescr expandable"><span class="name">
|
|
alpha.security.MmapWriteExec</span><span class="lang">
|
|
(C)</span><div class="descr">
|
|
Warn on <code>mmap()<code> calls that are both writable and executable.
|
|
</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void test(int n) {
|
|
void *c = mmap(NULL, 32, PROT_READ | PROT_WRITE | PROT_EXEC,
|
|
MAP_PRIVATE | MAP_ANON, -1, 0);
|
|
// warn: Both PROT_WRITE and PROT_EXEC flags are set. This can lead to
|
|
// exploitable memory regions, which could be overwritten with malicious
|
|
// code
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.security.ReturnPtrRange"><div class="namedescr expandable"><span class="name">
|
|
alpha.security.ReturnPtrRange</span><span class="lang">
|
|
(C)</span><div class="descr">
|
|
Check for an out-of-bound pointer being returned to callers.</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
static int A[10];
|
|
|
|
int *test() {
|
|
int *p = A + 10;
|
|
return p; // warn
|
|
}
|
|
</pre></div><div class="separator"></div>
|
|
<div class="example"><pre>
|
|
int test(void) {
|
|
int x;
|
|
return x; // warn: undefined or garbage returned
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.security.taint.TaintPropagation"><div class="namedescr expandable"><span class="name">
|
|
alpha.security.taint.TaintPropagation</span><span class="lang">
|
|
(C)</span><div class="descr">
|
|
Generate taint information used by other checkers.</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void test() {
|
|
char x = getchar(); // 'x' marked as tainted
|
|
system(&x); // warn: untrusted data is passed to a system call
|
|
}
|
|
</pre></div><div class="separator"></div>
|
|
<div class="example"><pre>
|
|
// note: compiler internally checks if the second param to
|
|
// sprintf is a string literal or not.
|
|
// Use -Wno-format-security to suppress compiler warning.
|
|
void test() {
|
|
char s[10], buf[10];
|
|
fscanf(stdin, "%s", s); // 's' marked as tainted
|
|
|
|
sprintf(buf, s); // warn: untrusted data as a format string
|
|
}
|
|
</pre></div><div class="separator"></div>
|
|
<div class="example"><pre>
|
|
void test() {
|
|
size_t ts;
|
|
scanf("%zd", &ts); // 'ts' marked as tainted
|
|
int *p = (int *)malloc(ts * sizeof(int));
|
|
// warn: untrusted data as buffer size
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
</tbody></table>
|
|
|
|
<!-- ============================= unix alpha ============================= -->
|
|
<h3 id="unix_alpha_checkers">Unix Alpha Checkers</h3>
|
|
<table class="checkers">
|
|
<colgroup><col class="namedescr"><col class="example"></colgroup>
|
|
<thead><tr><td>Name, Description</td><td>Example</td></tr></thead>
|
|
<tbody>
|
|
|
|
|
|
<tr><td><a id="alpha.unix.BlockInCriticalSection"><div class="namedescr expandable"><span class="name">
|
|
alpha.unix.BlockInCriticalSection</span><span class="lang">
|
|
(C)</span><div class="descr">
|
|
Check for calls to blocking functions inside a critical section. Applies to:
|
|
<div class=functions>
|
|
lock<br>
|
|
unlock<br>
|
|
sleep<br>
|
|
getc<br>
|
|
fgets<br>
|
|
read<br>
|
|
revc<br>
|
|
pthread_mutex_lock<br>
|
|
pthread_mutex_unlock<br>
|
|
mtx_lock<br>
|
|
mtx_timedlock<br>
|
|
mtx_trylock<br>
|
|
mtx_unlock<br>
|
|
lock_guard<br>
|
|
unique_lock</div>
|
|
</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void test() {
|
|
std::mutex m;
|
|
m.lock();
|
|
sleep(3); // warn: a blocking function sleep is called inside a critical
|
|
// section
|
|
m.unlock();
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.unix.Chroot"><div class="namedescr expandable"><span class="name">
|
|
alpha.unix.Chroot</span><span class="lang">
|
|
(C)</span><div class="descr">
|
|
Check improper use of <code>chroot</code>.</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void f();
|
|
|
|
void test() {
|
|
chroot("/usr/local");
|
|
f(); // warn: no call of chdir("/") immediately after chroot
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.unix.PthreadLock"><div class="namedescr expandable"><span class="name">
|
|
alpha.unix.PthreadLock</span><span class="lang">
|
|
(C)</span><div class="descr">
|
|
Simple lock -> unlock checker; applies to:<div class=functions>
|
|
pthread_mutex_lock<br>
|
|
pthread_rwlock_rdlock<br>
|
|
pthread_rwlock_wrlock<br>
|
|
lck_mtx_lock<br>
|
|
lck_rw_lock_exclusive<br>
|
|
lck_rw_lock_shared<br>
|
|
pthread_mutex_trylock<br>
|
|
pthread_rwlock_tryrdlock<br>
|
|
pthread_rwlock_tryrwlock<br>
|
|
lck_mtx_try_lock<br>
|
|
lck_rw_try_lock_exclusive<br>
|
|
lck_rw_try_lock_shared<br>
|
|
pthread_mutex_unlock<br>
|
|
pthread_rwlock_unlock<br>
|
|
lck_mtx_unlock<br>
|
|
lck_rw_done</div></div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
pthread_mutex_t mtx;
|
|
|
|
void test() {
|
|
pthread_mutex_lock(&mtx);
|
|
pthread_mutex_lock(&mtx);
|
|
// warn: this lock has already been acquired
|
|
}
|
|
</pre></div><div class="separator"></div>
|
|
<div class="example"><pre>
|
|
lck_mtx_t lck1, lck2;
|
|
|
|
void test() {
|
|
lck_mtx_lock(&lck1);
|
|
lck_mtx_lock(&lck2);
|
|
lck_mtx_unlock(&lck1);
|
|
// warn: this was not the most recently acquired lock
|
|
}
|
|
</pre></div><div class="separator"></div>
|
|
<div class="example"><pre>
|
|
lck_mtx_t lck1, lck2;
|
|
|
|
void test() {
|
|
if (lck_mtx_try_lock(&lck1) == 0)
|
|
return;
|
|
|
|
lck_mtx_lock(&lck2);
|
|
lck_mtx_unlock(&lck1);
|
|
// warn: this was not the most recently acquired lock
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.unix.SimpleStream"><div class="namedescr expandable"><span class="name">
|
|
alpha.unix.SimpleStream</span><span class="lang">
|
|
(C)</span><div class="descr">
|
|
Check for misuses of stream APIs:<div class=functions>
|
|
fopen<br>
|
|
fclose</div>(demo checker, the subject of the demo
|
|
(<a href="https://llvm.org/devmtg/2012-11/Zaks-Rose-Checker24Hours.pdf">Slides</a>
|
|
,<a href="https://youtu.be/kdxlsP5QVPw">Video</a>)
|
|
by Anna Zaks and Jordan Rose presented at the <a href="https://llvm.org/devmtg/2012-11/">
|
|
2012 LLVM Developers' Meeting).</a></div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void test() {
|
|
FILE *F = fopen("myfile.txt", "w");
|
|
} // warn: opened file is never closed
|
|
</pre></div><div class="separator"></div>
|
|
<div class="example"><pre>
|
|
void test() {
|
|
FILE *F = fopen("myfile.txt", "w");
|
|
|
|
if (F)
|
|
fclose(F);
|
|
|
|
fclose(F); // warn: closing a previously closed file stream
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.unix.Stream"><div class="namedescr expandable"><span class="name">
|
|
alpha.unix.Stream</span><span class="lang">
|
|
(C)</span><div class="descr">
|
|
Check stream handling functions:<div class=functions>fopen<br>
|
|
tmpfile<br>
|
|
fclose<br>
|
|
fread<br>
|
|
fwrite<br>
|
|
fseek<br>
|
|
ftell<br>
|
|
rewind<br>
|
|
fgetpos<br>
|
|
fsetpos<br>
|
|
clearerr<br>
|
|
feof<br>
|
|
ferror<br>
|
|
fileno</div></div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void test() {
|
|
FILE *p = fopen("foo", "r");
|
|
} // warn: opened file is never closed
|
|
</pre></div><div class="separator"></div>
|
|
<div class="example"><pre>
|
|
void test() {
|
|
FILE *p = fopen("foo", "r");
|
|
fseek(p, 1, SEEK_SET); // warn: stream pointer might be NULL
|
|
fclose(p);
|
|
}
|
|
</pre></div><div class="separator"></div>
|
|
<div class="example"><pre>
|
|
void test() {
|
|
FILE *p = fopen("foo", "r");
|
|
|
|
if (p)
|
|
fseek(p, 1, 3);
|
|
// warn: third arg should be SEEK_SET, SEEK_END, or SEEK_CUR
|
|
|
|
fclose(p);
|
|
}
|
|
</pre></div><div class="separator"></div>
|
|
<div class="example"><pre>
|
|
void test() {
|
|
FILE *p = fopen("foo", "r");
|
|
fclose(p);
|
|
fclose(p); // warn: already closed
|
|
}
|
|
</pre></div><div class="separator"></div>
|
|
<div class="example"><pre>
|
|
void test() {
|
|
FILE *p = tmpfile();
|
|
ftell(p); // warn: stream pointer might be NULL
|
|
fclose(p);
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.unix.cstring.BufferOverlap"><div class="namedescr expandable"><span class="name">
|
|
alpha.unix.cstring.BufferOverlap</span><span class="lang">
|
|
(C)</span><div class="descr">
|
|
Checks for overlap in two buffer arguments; applies to:<div class=functions>
|
|
memcpy<br>
|
|
mempcpy</div></div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void test() {
|
|
int a[4] = {0};
|
|
memcpy(a + 2, a + 1, 8); // warn
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.unix.cstring.NotNullTerminated"><div class="namedescr expandable"><span class="name">
|
|
alpha.unix.cstring.NotNullTerminated</span><span class="lang">
|
|
(C)</span><div class="descr">
|
|
Check for arguments which are not null-terminated strings; applies
|
|
to:<div class=functions>
|
|
strlen<br>
|
|
strnlen<br>
|
|
strcpy<br>
|
|
strncpy<br>
|
|
strcat<br>
|
|
strncat</div></div></div></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void test() {
|
|
int y = strlen((char *)&test); // warn
|
|
}
|
|
</pre></div></div></a></td></tr>
|
|
|
|
|
|
<tr><td><a id="alpha.unix.cstring.OutOfBounds"><div class="namedescr expandable"><span class="name">
|
|
alpha.unix.cstring.OutOfBounds</span><span class="lang">
|
|
(C)</span><div class="descr">
|
|
Check for out-of-bounds access in string functions; applies
|
|
to:<div class=functions>
|
|
strncopy<br>
|
|
strncat</div></div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
void test(char *y) {
|
|
char x[4];
|
|
if (strlen(y) == 4)
|
|
strncpy(x, y, 5); // warn
|
|
}
|
|
</pre></div></div></td></tr>
|
|
|
|
</tbody></table>
|
|
|
|
<!-- =========================== nondeterminism alpha =========================== -->
|
|
<h3 id="nondeterminism_alpha_checkers">Non-determinism Alpha Checkers</h3>
|
|
<table class="checkers">
|
|
<colgroup><col class="namedescr"><col class="example"></colgroup>
|
|
<thead><tr><td>Name, Description</td><td>Example</td></tr></thead>
|
|
|
|
<tbody>
|
|
<tr><td><a id="alpha.nondeterminism.PointerIteration"><div class="namedescr expandable"><span class="name">
|
|
alpha.nondeterminism.PointerIteration</span><span class="lang">
|
|
(C++)</span><div class="descr">
|
|
Check for non-determinism caused by iterating unordered containers of pointers.</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
// C++
|
|
void test() {
|
|
int a = 1, b = 2;
|
|
std::unordered_set<int *> UnorderedPtrSet = {&a, &b};
|
|
|
|
for (auto i : UnorderedPtrSet) // warn
|
|
f(i);
|
|
}
|
|
</pre></div></div></td></tr>
|
|
<tr><td><a id="alpha.nondeterminism.PointerSorting"><div class="namedescr expandable"><span class="name">
|
|
alpha.nondeterminism.PointerSorting</span><span class="lang">
|
|
(C++)</span><div class="descr">
|
|
Check for non-determinism caused by sorting of pointers.</div></div></a></td>
|
|
<td><div class="exampleContainer expandable">
|
|
<div class="example"><pre>
|
|
// C++
|
|
void test() {
|
|
int a = 1, b = 2;
|
|
std::vector<int *> V = {&a, &b};
|
|
std::sort(V.begin(), V.end()); // warn
|
|
}
|
|
</pre></div></div></td></tr>
|
|
</tbody></table>
|
|
|
|
</div> <!-- page -->
|
|
</div> <!-- content -->
|
|
</body>
|
|
</html>
|