@@ -78,35 +78,30 @@ static QualType getCharPtrType(ASTContext &Ctx, CharKind CK) {
78
78
: Ctx.WideCharTy );
79
79
}
80
80
81
- class CStringChecker : public Checker < eval::Call,
82
- check::PreStmt<DeclStmt>,
83
- check::LiveSymbols,
84
- check::DeadSymbols,
85
- check::RegionChanges
86
- > {
87
- mutable std::unique_ptr<BugType> BT_Null, BT_Bounds, BT_Overlap,
88
- BT_NotCString, BT_AdditionOverflow, BT_UninitRead;
89
-
81
+ class CStringChecker
82
+ : public CheckerFamily<eval::Call, check::PreStmt<DeclStmt>,
83
+ check::LiveSymbols, check::DeadSymbols,
84
+ check::RegionChanges> {
90
85
mutable const char *CurrentFunctionDescription = nullptr ;
91
86
92
87
public:
93
- // / The filter is used to filter out the diagnostics which are not enabled by
94
- // / the user.
95
- struct CStringChecksFilter {
96
- bool CheckCStringNullArg = false ;
97
- bool CheckCStringOutOfBounds = false ;
98
- bool CheckCStringBufferOverlap = false ;
99
- bool CheckCStringNotNullTerm = false ;
100
- bool CheckCStringUninitializedRead = false ;
101
-
102
- CheckerNameRef CheckNameCStringNullArg;
103
- CheckerNameRef CheckNameCStringOutOfBounds ;
104
- CheckerNameRef CheckNameCStringBufferOverlap;
105
- CheckerNameRef CheckNameCStringNotNullTerm;
106
- CheckerNameRef CheckNameCStringUninitializedRead;
107
- };
108
-
109
- CStringChecksFilter Filter;
88
+ // FIXME: The bug types emitted by this checker family have confused garbage
89
+ // in their Description and Category fields (e.g. `categories::UnixAPI` is
90
+ // passed as the description in several cases and `uninitialized` is mistyped
91
+ // as `unitialized`). This should be cleaned up.
92
+ CheckerFrontendWithBugType NullArg{categories::UnixAPI} ;
93
+ CheckerFrontendWithBugType OutOfBounds{ " Out-of-bound array access " } ;
94
+ CheckerFrontendWithBugType BufferOverlap{categories::UnixAPI,
95
+ " Improper arguments " } ;
96
+ CheckerFrontendWithBugType NotNullTerm{categories::UnixAPI};
97
+ CheckerFrontendWithBugType UninitializedRead{
98
+ " Accessing unitialized/garbage values " } ;
99
+
100
+ // FIXME: This bug type should be removed because it is only emitted in a
101
+ // situation that is practically impossible.
102
+ const BugType AdditionOverflow{&OutOfBounds, " API " };
103
+
104
+ StringRef getDebugTag () const override { return " MallocChecker " ; }
110
105
111
106
static void *getTag () { static int tag; return &tag; }
112
107
@@ -384,7 +379,7 @@ ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C,
384
379
assumeZero (C, State, l, Arg.Expression ->getType ());
385
380
386
381
if (stateNull && !stateNonNull) {
387
- if (Filter. CheckCStringNullArg ) {
382
+ if (NullArg. isEnabled () ) {
388
383
SmallString<80 > buf;
389
384
llvm::raw_svector_ostream OS (buf);
390
385
assert (CurrentFunctionDescription);
@@ -468,7 +463,7 @@ ProgramStateRef CStringChecker::checkInit(CheckerContext &C,
468
463
return State;
469
464
470
465
// Ensure that we wouldn't read uninitialized value.
471
- if (Filter. CheckCStringUninitializedRead &&
466
+ if (UninitializedRead. isEnabled () &&
472
467
State->getSVal (*FirstElementVal).isUndef ()) {
473
468
llvm::SmallString<258 > Buf;
474
469
llvm::raw_svector_ostream OS (Buf);
@@ -524,7 +519,7 @@ ProgramStateRef CStringChecker::checkInit(CheckerContext &C,
524
519
if (!isa<Loc>(LastElementVal))
525
520
return State;
526
521
527
- if (Filter. CheckCStringUninitializedRead &&
522
+ if (UninitializedRead. isEnabled () &&
528
523
State->getSVal (LastElementVal.castAs <Loc>()).isUndef ()) {
529
524
const llvm::APSInt *IdxInt = LastIdx.getAsInteger ();
530
525
// If we can't get emit a sensible last element index, just bail out --
@@ -581,13 +576,9 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
581
576
582
577
auto [StInBound, StOutBound] = state->assumeInBoundDual (*Idx, Size);
583
578
if (StOutBound && !StInBound) {
584
- // These checks are either enabled by the CString out-of-bounds checker
585
- // explicitly or implicitly by the Malloc checker.
586
- // In the latter case we only do modeling but do not emit warning.
587
- if (!Filter.CheckCStringOutOfBounds )
579
+ if (!OutOfBounds.isEnabled ())
588
580
return nullptr ;
589
581
590
- // Emit a bug report.
591
582
ErrorMessage Message =
592
583
createOutOfBoundErrorMsg (CurrentFunctionDescription, Access);
593
584
emitOutOfBoundsBug (C, StOutBound, Buffer.Expression , Message);
@@ -620,7 +611,7 @@ CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State,
620
611
return nullptr ;
621
612
622
613
// If out-of-bounds checking is turned off, skip the rest.
623
- if (!Filter. CheckCStringOutOfBounds )
614
+ if (!OutOfBounds. isEnabled () )
624
615
return State;
625
616
626
617
SVal BufStart =
@@ -670,7 +661,7 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
670
661
SizeArgExpr Size, AnyArgExpr First,
671
662
AnyArgExpr Second,
672
663
CharKind CK) const {
673
- if (!Filter. CheckCStringBufferOverlap )
664
+ if (!BufferOverlap. isEnabled () )
674
665
return state;
675
666
676
667
// Do a simple check for overlap: if the two arguments are from the same
@@ -789,13 +780,9 @@ void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state,
789
780
if (!N)
790
781
return ;
791
782
792
- if (!BT_Overlap)
793
- BT_Overlap.reset (new BugType (Filter.CheckNameCStringBufferOverlap ,
794
- categories::UnixAPI, " Improper arguments" ));
795
-
796
783
// Generate a report for this bug.
797
784
auto report = std::make_unique<PathSensitiveBugReport>(
798
- *BT_Overlap , " Arguments must not be overlapping buffers" , N);
785
+ BufferOverlap , " Arguments must not be overlapping buffers" , N);
799
786
report->addRange (First->getSourceRange ());
800
787
report->addRange (Second->getSourceRange ());
801
788
@@ -805,15 +792,8 @@ void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state,
805
792
void CStringChecker::emitNullArgBug (CheckerContext &C, ProgramStateRef State,
806
793
const Stmt *S, StringRef WarningMsg) const {
807
794
if (ExplodedNode *N = C.generateErrorNode (State)) {
808
- if (!BT_Null) {
809
- // FIXME: This call uses the string constant 'categories::UnixAPI' as the
810
- // description of the bug; it should be replaced by a real description.
811
- BT_Null.reset (
812
- new BugType (Filter.CheckNameCStringNullArg , categories::UnixAPI));
813
- }
814
-
815
795
auto Report =
816
- std::make_unique<PathSensitiveBugReport>(*BT_Null , WarningMsg, N);
796
+ std::make_unique<PathSensitiveBugReport>(NullArg , WarningMsg, N);
817
797
Report->addRange (S->getSourceRange ());
818
798
if (const auto *Ex = dyn_cast<Expr>(S))
819
799
bugreporter::trackExpressionValue (N, Ex, *Report);
@@ -826,12 +806,8 @@ void CStringChecker::emitUninitializedReadBug(CheckerContext &C,
826
806
const Expr *E, const MemRegion *R,
827
807
StringRef Msg) const {
828
808
if (ExplodedNode *N = C.generateErrorNode (State)) {
829
- if (!BT_UninitRead)
830
- BT_UninitRead.reset (new BugType (Filter.CheckNameCStringUninitializedRead ,
831
- " Accessing unitialized/garbage values" ));
832
-
833
809
auto Report =
834
- std::make_unique<PathSensitiveBugReport>(*BT_UninitRead , Msg, N);
810
+ std::make_unique<PathSensitiveBugReport>(UninitializedRead , Msg, N);
835
811
Report->addNote (" Other elements might also be undefined" ,
836
812
Report->getLocation ());
837
813
Report->addRange (E->getSourceRange ());
@@ -845,17 +821,11 @@ void CStringChecker::emitOutOfBoundsBug(CheckerContext &C,
845
821
ProgramStateRef State, const Stmt *S,
846
822
StringRef WarningMsg) const {
847
823
if (ExplodedNode *N = C.generateErrorNode (State)) {
848
- if (!BT_Bounds)
849
- BT_Bounds.reset (new BugType (Filter.CheckCStringOutOfBounds
850
- ? Filter.CheckNameCStringOutOfBounds
851
- : Filter.CheckNameCStringNullArg ,
852
- " Out-of-bound array access" ));
853
-
854
824
// FIXME: It would be nice to eventually make this diagnostic more clear,
855
825
// e.g., by referencing the original declaration or by saying *why* this
856
826
// reference is outside the range.
857
827
auto Report =
858
- std::make_unique<PathSensitiveBugReport>(*BT_Bounds , WarningMsg, N);
828
+ std::make_unique<PathSensitiveBugReport>(OutOfBounds , WarningMsg, N);
859
829
Report->addRange (S->getSourceRange ());
860
830
C.emitReport (std::move (Report));
861
831
}
@@ -865,15 +835,8 @@ void CStringChecker::emitNotCStringBug(CheckerContext &C, ProgramStateRef State,
865
835
const Stmt *S,
866
836
StringRef WarningMsg) const {
867
837
if (ExplodedNode *N = C.generateNonFatalErrorNode (State)) {
868
- if (!BT_NotCString) {
869
- // FIXME: This call uses the string constant 'categories::UnixAPI' as the
870
- // description of the bug; it should be replaced by a real description.
871
- BT_NotCString.reset (
872
- new BugType (Filter.CheckNameCStringNotNullTerm , categories::UnixAPI));
873
- }
874
-
875
838
auto Report =
876
- std::make_unique<PathSensitiveBugReport>(*BT_NotCString , WarningMsg, N);
839
+ std::make_unique<PathSensitiveBugReport>(NotNullTerm , WarningMsg, N);
877
840
878
841
Report->addRange (S->getSourceRange ());
879
842
C.emitReport (std::move (Report));
@@ -883,22 +846,14 @@ void CStringChecker::emitNotCStringBug(CheckerContext &C, ProgramStateRef State,
883
846
void CStringChecker::emitAdditionOverflowBug (CheckerContext &C,
884
847
ProgramStateRef State) const {
885
848
if (ExplodedNode *N = C.generateErrorNode (State)) {
886
- if (!BT_AdditionOverflow) {
887
- // FIXME: This call uses the word "API" as the description of the bug;
888
- // it should be replaced by a better error message (if this unlikely
889
- // situation continues to exist as a separate bug type).
890
- BT_AdditionOverflow.reset (
891
- new BugType (Filter.CheckNameCStringOutOfBounds , " API" ));
892
- }
893
-
894
849
// This isn't a great error message, but this should never occur in real
895
850
// code anyway -- you'd have to create a buffer longer than a size_t can
896
851
// represent, which is sort of a contradiction.
897
852
const char *WarningMsg =
898
853
" This expression will create a string whose length is too big to "
899
854
" be represented as a size_t" ;
900
855
901
- auto Report = std::make_unique<PathSensitiveBugReport>(*BT_AdditionOverflow ,
856
+ auto Report = std::make_unique<PathSensitiveBugReport>(AdditionOverflow ,
902
857
WarningMsg, N);
903
858
C.emitReport (std::move (Report));
904
859
}
@@ -909,7 +864,7 @@ ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C,
909
864
NonLoc left,
910
865
NonLoc right) const {
911
866
// If out-of-bounds checking is turned off, skip the rest.
912
- if (!Filter. CheckCStringOutOfBounds )
867
+ if (!OutOfBounds. isEnabled () )
913
868
return state;
914
869
915
870
// If a previous check has failed, propagate the failure.
@@ -1048,7 +1003,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
1048
1003
// C string. In the context of locations, the only time we can issue such
1049
1004
// a warning is for labels.
1050
1005
if (std::optional<loc::GotoLabel> Label = Buf.getAs <loc::GotoLabel>()) {
1051
- if (Filter. CheckCStringNotNullTerm ) {
1006
+ if (NotNullTerm. isEnabled () ) {
1052
1007
SmallString<120 > buf;
1053
1008
llvm::raw_svector_ostream os (buf);
1054
1009
assert (CurrentFunctionDescription);
@@ -1110,7 +1065,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
1110
1065
// Other regions (mostly non-data) can't have a reliable C string length.
1111
1066
// In this case, an error is emitted and UndefinedVal is returned.
1112
1067
// The caller should always be prepared to handle this case.
1113
- if (Filter. CheckCStringNotNullTerm ) {
1068
+ if (NotNullTerm. isEnabled () ) {
1114
1069
SmallString<120 > buf;
1115
1070
llvm::raw_svector_ostream os (buf);
1116
1071
@@ -2873,24 +2828,27 @@ void CStringChecker::checkDeadSymbols(SymbolReaper &SR,
2873
2828
}
2874
2829
2875
2830
void ento::registerCStringModeling (CheckerManager &Mgr) {
2876
- Mgr.registerChecker <CStringChecker>();
2831
+ // Other checker relies on the modeling implemented in this checker family,
2832
+ // so this "modeling checker" can register the 'CStringChecker' backend for
2833
+ // its callbacks without enabling any of its frontends.
2834
+ Mgr.getChecker <CStringChecker>();
2877
2835
}
2878
2836
2879
- bool ento::shouldRegisterCStringModeling (const CheckerManager &mgr ) {
2837
+ bool ento::shouldRegisterCStringModeling (const CheckerManager &) {
2880
2838
return true ;
2881
2839
}
2882
2840
2883
- #define REGISTER_CHECKER (name ) \
2884
- void ento::register ##name(CheckerManager &mgr) { \
2885
- CStringChecker *checker = mgr.getChecker <CStringChecker>(); \
2886
- checker->Filter .Check ##name = true ; \
2887
- checker->Filter .CheckName ##name = mgr.getCurrentCheckerName (); \
2841
+ #define REGISTER_CHECKER (NAME ) \
2842
+ void ento::registerCString##NAME(CheckerManager &Mgr) { \
2843
+ Mgr.getChecker <CStringChecker>()->NAME .enable (Mgr); \
2888
2844
} \
2889
2845
\
2890
- bool ento::shouldRegister##name(const CheckerManager &mgr) { return true ; }
2846
+ bool ento::shouldRegisterCString##NAME(const CheckerManager &) { \
2847
+ return true ; \
2848
+ }
2891
2849
2892
- REGISTER_CHECKER (CStringNullArg )
2893
- REGISTER_CHECKER(CStringOutOfBounds )
2894
- REGISTER_CHECKER(CStringBufferOverlap )
2895
- REGISTER_CHECKER(CStringNotNullTerm )
2896
- REGISTER_CHECKER(CStringUninitializedRead )
2850
+ REGISTER_CHECKER (NullArg )
2851
+ REGISTER_CHECKER(OutOfBounds )
2852
+ REGISTER_CHECKER(BufferOverlap )
2853
+ REGISTER_CHECKER(NotNullTerm )
2854
+ REGISTER_CHECKER(UninitializedRead )
0 commit comments