Skip to content

Commit 6e17ed9

Browse files
authored
[analyzer] Consolidate array bound checkers (llvm#125534)
Before this commit, there were two alpha checkers that used different algorithms/logic for detecting out of bounds memory access: the old `alpha.security.ArrayBound` and the experimental, more complex `alpha.security.ArrayBoundV2`. After lots of quality improvement commits ArrayBoundV2 is now stable enough to be moved out of the alpha stage. As indexing (and dereference) are common operations, it still produces a significant amount of false positives, but not much more than e.g. `core.NullDereference` or `core.UndefinedBinaryOperatorResult`, so it should be acceptable as a non-`core` checker. At this point `alpha.security.ArrayBound` became obsolete (there is a better tool for the same task), so I'm removing it from the codebase. With this I can eliminate the ugly "V2" version mark almost everywhere and rename `alpha.security.ArrayBoundV2` to `security.ArrayBound`. (The version mark is preserved in the filename "ArrayBoundCheckerV2", to ensure a clear git history. I'll rename it to "ArrayBoundChecker.cpp" in a separate commit.) This commit adapts the unit tests of `alpha.security.ArrayBound` to testing the new `security.ArrayBound` (= old ArrayBoundV2). Currently the names of the test files are very haphazard, I'll probably create a separate followup commit that consolidates this.
1 parent 2e18c94 commit 6e17ed9

26 files changed

+178
-321
lines changed

clang/docs/ReleaseNotes.rst

+5
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,11 @@ Improvements
263263
Moved checkers
264264
^^^^^^^^^^^^^^
265265

266+
- After lots of improvements, the checker ``alpha.security.ArrayBoundV2`` is
267+
renamed to ``security.ArrayBound``. As this checker is stable now, the old
268+
checker ``alpha.security.ArrayBound`` (which was searching for the same kind
269+
of bugs with an different, simpler and less accurate algorithm) is removed.
270+
266271
.. _release-notes-sanitizers:
267272

268273
Sanitizers

clang/docs/analyzer/checkers.rst

+60-73
Original file line numberDiff line numberDiff line change
@@ -1332,10 +1332,69 @@ security
13321332
13331333
Security related checkers.
13341334
1335+
.. _security-ArrayBound:
1336+
1337+
security.ArrayBound (C, C++)
1338+
""""""""""""""""""""""""""""
1339+
Report out of bounds access to memory that is before the start or after the end
1340+
of the accessed region (array, heap-allocated region, string literal etc.).
1341+
This usually means incorrect indexing, but the checker also detects access via
1342+
the operators ``*`` and ``->``.
1343+
1344+
.. code-block:: c
1345+
1346+
void test_underflow(int x) {
1347+
int buf[100][100];
1348+
if (x < 0)
1349+
buf[0][x] = 1; // warn
1350+
}
1351+
1352+
void test_overflow() {
1353+
int buf[100];
1354+
int *p = buf + 100;
1355+
*p = 1; // warn
1356+
}
1357+
1358+
If checkers like :ref:`unix-Malloc` or :ref:`cplusplus-NewDelete` are enabled
1359+
to model the behavior of ``malloc()``, ``operator new`` and similar
1360+
allocators), then this checker can also reports out of bounds access to
1361+
dynamically allocated memory:
1362+
1363+
.. code-block:: cpp
1364+
1365+
int *test_dynamic() {
1366+
int *mem = new int[100];
1367+
mem[-1] = 42; // warn
1368+
return mem;
1369+
}
1370+
1371+
In uncertain situations (when the checker can neither prove nor disprove that
1372+
overflow occurs), the checker assumes that the the index (more precisely, the
1373+
memory offeset) is within bounds.
1374+
1375+
However, if :ref:`optin-taint-GenericTaint` is enabled and the index/offset is
1376+
tainted (i.e. it is influenced by an untrusted souce), then this checker
1377+
reports the potential out of bounds access:
1378+
1379+
.. code-block:: c
1380+
1381+
void test_with_tainted_index() {
1382+
char s[] = "abc";
1383+
int x = getchar();
1384+
char c = s[x]; // warn: potential out of bounds access with tainted index
1385+
}
1386+
1387+
.. note::
1388+
1389+
This checker is an improved and renamed version of the checker that was
1390+
previously known as ``alpha.security.ArrayBoundV2``. The old checker
1391+
``alpha.security.ArrayBound`` was removed when the (previously
1392+
"experimental") V2 variant became stable enough for regular use.
1393+
13351394
.. _security-cert-env-InvalidPtr:
13361395
13371396
security.cert.env.InvalidPtr
1338-
""""""""""""""""""""""""""""""""""
1397+
""""""""""""""""""""""""""""
13391398
13401399
Corresponds to SEI CERT Rules `ENV31-C <https://wiki.sei.cmu.edu/confluence/display/c/ENV31-C.+Do+not+rely+on+an+environment+pointer+following+an+operation+that+may+invalidate+it>`_ and `ENV34-C <https://wiki.sei.cmu.edu/confluence/display/c/ENV34-C.+Do+not+store+pointers+returned+by+certain+functions>`_.
13411400
@@ -3216,78 +3275,6 @@ Warns against using one vs. many plural pattern in code when generating localize
32163275
alpha.security
32173276
^^^^^^^^^^^^^^
32183277
3219-
.. _alpha-security-ArrayBound:
3220-
3221-
alpha.security.ArrayBound (C)
3222-
"""""""""""""""""""""""""""""
3223-
Warn about buffer overflows (older checker).
3224-
3225-
.. code-block:: c
3226-
3227-
void test() {
3228-
char *s = "";
3229-
char c = s[1]; // warn
3230-
}
3231-
3232-
struct seven_words {
3233-
int c[7];
3234-
};
3235-
3236-
void test() {
3237-
struct seven_words a, *p;
3238-
p = &a;
3239-
p[0] = a;
3240-
p[1] = a;
3241-
p[2] = a; // warn
3242-
}
3243-
3244-
// note: requires unix.Malloc or
3245-
// alpha.unix.MallocWithAnnotations checks enabled.
3246-
void test() {
3247-
int *p = malloc(12);
3248-
p[3] = 4; // warn
3249-
}
3250-
3251-
void test() {
3252-
char a[2];
3253-
int *b = (int*)a;
3254-
b[1] = 3; // warn
3255-
}
3256-
3257-
.. _alpha-security-ArrayBoundV2:
3258-
3259-
alpha.security.ArrayBoundV2 (C)
3260-
"""""""""""""""""""""""""""""""
3261-
Warn about buffer overflows (newer checker).
3262-
3263-
.. code-block:: c
3264-
3265-
void test() {
3266-
char *s = "";
3267-
char c = s[1]; // warn
3268-
}
3269-
3270-
void test() {
3271-
int buf[100];
3272-
int *p = buf;
3273-
p = p + 99;
3274-
p[1] = 1; // warn
3275-
}
3276-
3277-
// note: compiler has internal check for this.
3278-
// Use -Wno-array-bounds to suppress compiler warning.
3279-
void test() {
3280-
int buf[100][100];
3281-
buf[0][-1] = 1; // warn
3282-
}
3283-
3284-
// note: requires optin.taint check turned on.
3285-
void test() {
3286-
char s[] = "abc";
3287-
int x = getchar();
3288-
char c = s[x]; // warn: index is tainted
3289-
}
3290-
32913278
.. _alpha-security-ReturnPtrRange:
32923279
32933280
alpha.security.ReturnPtrRange (C)

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

+35-32
Original file line numberDiff line numberDiff line change
@@ -989,30 +989,41 @@ def decodeValueOfObjCType : Checker<"decodeValueOfObjCType">,
989989

990990
let ParentPackage = Security in {
991991

992-
def FloatLoopCounter : Checker<"FloatLoopCounter">,
993-
HelpText<"Warn on using a floating point value as a loop counter (CERT: "
994-
"FLP30-C, FLP30-CPP)">,
995-
Dependencies<[SecuritySyntaxChecker]>,
996-
Documentation<HasDocumentation>;
997-
998-
def MmapWriteExecChecker : Checker<"MmapWriteExec">,
999-
HelpText<"Warn on mmap() calls with both writable and executable access">,
1000-
Documentation<HasDocumentation>;
1001-
1002-
def PointerSubChecker : Checker<"PointerSub">,
1003-
HelpText<"Check for pointer subtractions on two pointers pointing to "
1004-
"different memory chunks">,
1005-
Documentation<HasDocumentation>;
1006-
1007-
def PutenvStackArray : Checker<"PutenvStackArray">,
1008-
HelpText<"Finds calls to the function 'putenv' which pass a pointer to "
1009-
"an automatic (stack-allocated) array as the argument.">,
1010-
Documentation<HasDocumentation>;
1011-
1012-
def SetgidSetuidOrderChecker : Checker<"SetgidSetuidOrder">,
1013-
HelpText<"Warn on possible reversed order of 'setgid(getgid()))' and "
1014-
"'setuid(getuid())' (CERT: POS36-C)">,
1015-
Documentation<HasDocumentation>;
992+
def ArrayBoundChecker : Checker<"ArrayBound">,
993+
HelpText<"Warn about out of bounds access to memory">,
994+
Documentation<HasDocumentation>;
995+
996+
def FloatLoopCounter
997+
: Checker<"FloatLoopCounter">,
998+
HelpText<
999+
"Warn on using a floating point value as a loop counter (CERT: "
1000+
"FLP30-C, FLP30-CPP)">,
1001+
Dependencies<[SecuritySyntaxChecker]>,
1002+
Documentation<HasDocumentation>;
1003+
1004+
def MmapWriteExecChecker
1005+
: Checker<"MmapWriteExec">,
1006+
HelpText<
1007+
"Warn on mmap() calls with both writable and executable access">,
1008+
Documentation<HasDocumentation>;
1009+
1010+
def PointerSubChecker
1011+
: Checker<"PointerSub">,
1012+
HelpText<"Check for pointer subtractions on two pointers pointing to "
1013+
"different memory chunks">,
1014+
Documentation<HasDocumentation>;
1015+
1016+
def PutenvStackArray
1017+
: Checker<"PutenvStackArray">,
1018+
HelpText<"Finds calls to the function 'putenv' which pass a pointer to "
1019+
"an automatic (stack-allocated) array as the argument.">,
1020+
Documentation<HasDocumentation>;
1021+
1022+
def SetgidSetuidOrderChecker
1023+
: Checker<"SetgidSetuidOrder">,
1024+
HelpText<"Warn on possible reversed order of 'setgid(getgid()))' and "
1025+
"'setuid(getuid())' (CERT: POS36-C)">,
1026+
Documentation<HasDocumentation>;
10161027

10171028
} // end "security"
10181029

@@ -1035,14 +1046,6 @@ let ParentPackage = ENV in {
10351046

10361047
let ParentPackage = SecurityAlpha in {
10371048

1038-
def ArrayBoundChecker : Checker<"ArrayBound">,
1039-
HelpText<"Warn about buffer overflows (older checker)">,
1040-
Documentation<HasDocumentation>;
1041-
1042-
def ArrayBoundCheckerV2 : Checker<"ArrayBoundV2">,
1043-
HelpText<"Warn about buffer overflows (newer checker)">,
1044-
Documentation<HasDocumentation>;
1045-
10461049
def ReturnPointerRangeChecker : Checker<"ReturnPtrRange">,
10471050
HelpText<"Check for an out-of-bound pointer being returned to callers">,
10481051
Documentation<HasDocumentation>;

clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp

-92
This file was deleted.

0 commit comments

Comments
 (0)