@@ -130,6 +130,20 @@ struct Messages {
130
130
std::string Short, Full;
131
131
};
132
132
133
+ enum class BadOffsetKind { Negative, Overflowing, Indeterminate };
134
+
135
+ constexpr llvm::StringLiteral Adjectives[] = {" a negative" , " an overflowing" ,
136
+ " a negative or overflowing" };
137
+ static StringRef asAdjective (BadOffsetKind Problem) {
138
+ return Adjectives[static_cast <int >(Problem)];
139
+ }
140
+
141
+ constexpr llvm::StringLiteral Prepositions[] = {" preceding" , " after the end of" ,
142
+ " around" };
143
+ static StringRef asPreposition (BadOffsetKind Problem) {
144
+ return Prepositions[static_cast <int >(Problem)];
145
+ }
146
+
133
147
// NOTE: The `ArraySubscriptExpr` and `UnaryOperator` callbacks are `PostStmt`
134
148
// instead of `PreStmt` because the current implementation passes the whole
135
149
// expression to `CheckerContext::getSVal()` which only works after the
@@ -388,18 +402,6 @@ static std::optional<int64_t> getConcreteValue(std::optional<NonLoc> SV) {
388
402
return SV ? getConcreteValue (*SV) : std::nullopt ;
389
403
}
390
404
391
- static Messages getPrecedesMsgs (const MemSpaceRegion *Space,
392
- const SubRegion *Region, NonLoc Offset) {
393
- std::string RegName = getRegionName (Space, Region), OffsetStr = " " ;
394
-
395
- if (auto ConcreteOffset = getConcreteValue (Offset))
396
- OffsetStr = formatv (" {0}" , ConcreteOffset);
397
-
398
- return {
399
- formatv (" Out of bound access to memory preceding {0}" , RegName),
400
- formatv (" Access of {0} at negative byte offset{1}" , RegName, OffsetStr)};
401
- }
402
-
403
405
// / Try to divide `Val1` and `Val2` (in place) by `Divisor` and return true if
404
406
// / it can be performed (`Divisor` is nonzero and there is no remainder). The
405
407
// / values `Val1` and `Val2` may be nullopt and in that case the corresponding
@@ -419,10 +421,11 @@ static bool tryDividePair(std::optional<int64_t> &Val1,
419
421
return true ;
420
422
}
421
423
422
- static Messages getExceedsMsgs (ASTContext &ACtx, const MemSpaceRegion *Space,
423
- const SubRegion *Region, NonLoc Offset,
424
- NonLoc Extent, SVal Location,
425
- bool AlsoMentionUnderflow) {
424
+ static Messages getNonTaintMsgs (const ASTContext &ACtx,
425
+ const MemSpaceRegion *Space,
426
+ const SubRegion *Region, NonLoc Offset,
427
+ std::optional<NonLoc> Extent, SVal Location,
428
+ BadOffsetKind Problem) {
426
429
std::string RegName = getRegionName (Space, Region);
427
430
const auto *EReg = Location.getAsRegion ()->getAs <ElementRegion>();
428
431
assert (EReg && " this checker only handles element access" );
@@ -439,15 +442,21 @@ static Messages getExceedsMsgs(ASTContext &ACtx, const MemSpaceRegion *Space,
439
442
SmallString<256 > Buf;
440
443
llvm::raw_svector_ostream Out (Buf);
441
444
Out << " Access of " ;
442
- if (!ExtentN && !UseByteOffsets)
445
+ if (OffsetN && !ExtentN && !UseByteOffsets) {
446
+ // If the offset is reported as an index, then the report must mention the
447
+ // element type (because it is not always clear from the code). It's more
448
+ // natural to mention the element type later where the extent is described,
449
+ // but if the extent is unknown/irrelevant, then the element type can be
450
+ // inserted into the message at this point.
443
451
Out << " '" << ElemType.getAsString () << " ' element in " ;
452
+ }
444
453
Out << RegName << " at " ;
445
- if (AlsoMentionUnderflow ) {
446
- Out << " a negative or overflowing " << OffsetOrIndex;
447
- } else if (OffsetN) {
454
+ if (OffsetN ) {
455
+ if (Problem == BadOffsetKind::Negative)
456
+ Out << " negative " ;
448
457
Out << OffsetOrIndex << " " << *OffsetN;
449
458
} else {
450
- Out << " an overflowing " << OffsetOrIndex;
459
+ Out << asAdjective (Problem) << " " << OffsetOrIndex;
451
460
}
452
461
if (ExtentN) {
453
462
Out << " , while it holds only " ;
@@ -465,8 +474,7 @@ static Messages getExceedsMsgs(ASTContext &ACtx, const MemSpaceRegion *Space,
465
474
}
466
475
467
476
return {formatv (" Out of bound access to memory {0} {1}" ,
468
- AlsoMentionUnderflow ? " around" : " after the end of" ,
469
- RegName),
477
+ asPreposition (Problem), RegName),
470
478
std::string (Buf)};
471
479
}
472
480
@@ -635,7 +643,9 @@ void ArrayBoundChecker::performCheck(const Expr *E, CheckerContext &C) const {
635
643
} else {
636
644
if (!WithinLowerBound) {
637
645
// ...and it cannot be valid (>= 0), so report an error.
638
- Messages Msgs = getPrecedesMsgs (Space, Reg, ByteOffset);
646
+ Messages Msgs = getNonTaintMsgs (C.getASTContext (), Space, Reg,
647
+ ByteOffset, /* Extent=*/ std::nullopt ,
648
+ Location, BadOffsetKind::Negative);
639
649
reportOOB (C, PrecedesLowerBound, Msgs, ByteOffset, std::nullopt );
640
650
return ;
641
651
}
@@ -677,9 +687,12 @@ void ArrayBoundChecker::performCheck(const Expr *E, CheckerContext &C) const {
677
687
return ;
678
688
}
679
689
690
+ BadOffsetKind Problem = AlsoMentionUnderflow
691
+ ? BadOffsetKind::Indeterminate
692
+ : BadOffsetKind::Overflowing;
680
693
Messages Msgs =
681
- getExceedsMsgs (C.getASTContext (), Space, Reg, ByteOffset,
682
- *KnownSize, Location, AlsoMentionUnderflow );
694
+ getNonTaintMsgs (C.getASTContext (), Space, Reg, ByteOffset,
695
+ *KnownSize, Location, Problem );
683
696
reportOOB (C, ExceedsUpperBound, Msgs, ByteOffset, KnownSize);
684
697
return ;
685
698
}
0 commit comments