Skip to content

Commit cc28200

Browse files
committed
Introduce analyzer config option for aggressive reporting of tainted offsets
1 parent acf21ba commit cc28200

File tree

4 files changed

+71
-14
lines changed

4 files changed

+71
-14
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -917,6 +917,14 @@ let ParentPackage = Security in {
917917

918918
def ArrayBoundChecker : Checker<"ArrayBound">,
919919
HelpText<"Warn about out of bounds access to memory">,
920+
CheckerOptions<[
921+
CmdLineOption<Boolean,
922+
"AggressiveReport",
923+
"Treat all unknown values as tainted and report a waning if"
924+
"such values are used as offsets to access array elements",
925+
"false",
926+
InAlpha>
927+
]>,
920928
Documentation<HasDocumentation>;
921929

922930
def FloatLoopCounter

clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ class ArrayBoundChecker : public Checker<check::PostStmt<ArraySubscriptExpr>,
177177
static bool isInAddressOf(const Stmt *S, ASTContext &AC);
178178

179179
public:
180+
// When this parameter is set to true, the checker treats all
181+
// unknown values as tainted and reports wanings if such values
182+
// are used as offsets to access array elements
183+
bool IsAggressive = false;
180184
void checkPostStmt(const ArraySubscriptExpr *E, CheckerContext &C) const {
181185
performCheck(E, C);
182186
}
@@ -478,6 +482,17 @@ static Messages getNonTaintMsgs(const ASTContext &ACtx,
478482
std::string(Buf)};
479483
}
480484

485+
static Messages getTaintMsgs(const MemSpaceRegion *Space,
486+
const SubRegion *Region, const char *OffsetName,
487+
bool AlsoMentionUnderflow) {
488+
std::string RegName = getRegionName(Space, Region);
489+
return {formatv("Potential out of bound access to {0} with tainted {1}",
490+
RegName, OffsetName),
491+
formatv("Access of {0} with a tainted {1} that may be {2}too large",
492+
RegName, OffsetName,
493+
AlsoMentionUnderflow ? "negative or " : "")};
494+
}
495+
481496
const NoteTag *StateUpdateReporter::createNoteTag(CheckerContext &C) const {
482497
// Don't create a note tag if we didn't assume anything:
483498
if (!AssumedNonNegative && !AssumedUpperBound)
@@ -675,15 +690,46 @@ void ArrayBoundChecker::performCheck(const Expr *E, CheckerContext &C) const {
675690
C.addTransition(ExceedsUpperBound, SUR.createNoteTag(C));
676691
return;
677692
}
693+
694+
BadOffsetKind Problem = AlsoMentionUnderflow
695+
? BadOffsetKind::Indeterminate
696+
: BadOffsetKind::Overflowing;
697+
Messages Msgs =
698+
getNonTaintMsgs(C.getASTContext(), Space, Reg, ByteOffset,
699+
*KnownSize, Location, Problem);
700+
reportOOB(C, ExceedsUpperBound, Msgs, ByteOffset, KnownSize);
701+
return;
678702
}
703+
if (!IsAggressive) {
704+
// ...and it can be valid as well...
705+
if (isTainted(State, ByteOffset)) {
706+
// ...but it's tainted, so report an error.
707+
708+
// Diagnostic detail: saying "tainted offset" is always correct, but
709+
// the common case is that 'idx' is tainted in 'arr[idx]' and then it's
710+
// nicer to say "tainted index".
711+
const char *OffsetName = "offset";
712+
if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E))
713+
if (isTainted(State, ASE->getIdx(), C.getLocationContext()))
714+
OffsetName = "index";
715+
716+
Messages Msgs =
717+
getTaintMsgs(Space, Reg, OffsetName, AlsoMentionUnderflow);
718+
reportOOB(C, ExceedsUpperBound, Msgs, ByteOffset, KnownSize,
719+
/*IsTaintBug=*/true);
720+
return;
721+
}
679722

680-
BadOffsetKind Problem = AlsoMentionUnderflow
681-
? BadOffsetKind::Indeterminate
682-
: BadOffsetKind::Overflowing;
683-
Messages Msgs = getNonTaintMsgs(C.getASTContext(), Space, Reg, ByteOffset,
684-
*KnownSize, Location, Problem);
685-
reportOOB(C, ExceedsUpperBound, Msgs, ByteOffset, KnownSize);
686-
return;
723+
// ...and it isn't tainted, so the checker will (optimistically) assume
724+
// that the offset is in bounds and mention this in the note tag.
725+
SUR.recordUpperBoundAssumption(*KnownSize);
726+
} else {
727+
Messages Msgs =
728+
getTaintMsgs(Space, Reg, "offset", AlsoMentionUnderflow);
729+
reportOOB(C, ExceedsUpperBound, Msgs, ByteOffset, KnownSize,
730+
/*IsTaintBug=*/true);
731+
return;
732+
}
687733
}
688734

689735
// Actually update the state. The "if" only fails in the extremely unlikely
@@ -725,7 +771,7 @@ void ArrayBoundChecker::reportOOB(CheckerContext &C, ProgramStateRef ErrorState,
725771
std::optional<NonLoc> Extent,
726772
bool IsTaintBug /*=false*/) const {
727773

728-
ExplodedNode *ErrorNode = C.generateNonFatalErrorNode(ErrorState);
774+
ExplodedNode *ErrorNode = C.generateErrorNode(ErrorState);
729775
if (!ErrorNode)
730776
return;
731777

@@ -804,7 +850,10 @@ bool ArrayBoundChecker::isIdiomaticPastTheEndPtr(const Expr *E,
804850
}
805851

806852
void ento::registerArrayBoundChecker(CheckerManager &mgr) {
807-
mgr.registerChecker<ArrayBoundChecker>();
853+
ArrayBoundChecker *checker = mgr.registerChecker<ArrayBoundChecker>();
854+
checker->IsAggressive =
855+
mgr.getAnalyzerOptions().getCheckerBooleanOption(
856+
checker, "AggressiveReport");
808857
}
809858

810859
bool ento::shouldRegisterArrayBoundChecker(const CheckerManager &mgr) {

clang/test/Analysis/ArrayBound/cplusplus-tainted-index.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_analyze_cc1 -std=c++11 -Wno-array-bounds -analyzer-config unroll-loops=true -analyzer-checker=unix,core,security.ArrayBound -verify %s
1+
// RUN: %clang_analyze_cc1 -std=c++11 -Wno-array-bounds -analyzer-config unroll-loops=true -analyzer-config security.ArrayBound:AggressiveReport=true -analyzer-checker=unix,core,security.ArrayBound -verify %s
22

33
// Test the interactions of `security.ArrayBound` with C++ features.
44

@@ -20,7 +20,7 @@ void test_tainted_index1(unsigned index) {
2020
int arr[10];
2121
if (index < 12)
2222
arr[index] = index;
23-
// expected-warning@-1{{Out of bound access to memory after the end of 'arr'}}
23+
// expected-warning@-1{{Potential out of bound access to 'arr' with tainted offset}}
2424
if (index == 12)
2525
arr[index] = index;
2626
// expected-warning@-1{{Out of bound access to memory after the end of 'arr'}}
@@ -30,7 +30,7 @@ void test_tainted_index2(unsigned index) {
3030
int arr[10];
3131
if (index < 12)
3232
arr[index] = index;
33-
// expected-warning@-1{{Out of bound access to memory after the end of 'arr'}}
33+
// expected-warning@-1{{Potential out of bound access to 'arr' with tainted offset}}
3434
}
3535

3636
unsigned strlen(const char *s);
@@ -46,7 +46,6 @@ void test_ex(int argc, char *argv[]) {
4646
if (offset <= 16) {
4747
strcpy(proc_name, argv[0]);
4848
proc_name[offset] = argv[0][16];
49-
// expected-warning@-1{{Out of bound access to memory after the end of the region}}
50-
// expected-warning@-2{{Out of bound access to memory after the end of 'proc_name'}}
49+
// expected-warning@-1{{Potential out of bound access to the region with tainted offset}}
5150
}
5251
}

clang/test/Analysis/analyzer-config.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@
120120
// CHECK-NEXT: region-store-small-array-limit = 5
121121
// CHECK-NEXT: region-store-small-struct-limit = 2
122122
// CHECK-NEXT: report-in-main-source-file = false
123+
// CHECK-NEXT: security.ArrayBound:AggressiveReport = false
123124
// CHECK-NEXT: security.cert.env.InvalidPtr:InvalidatingGetEnv = false
124125
// CHECK-NEXT: serialize-stats = false
125126
// CHECK-NEXT: silence-checkers = ""

0 commit comments

Comments
 (0)