@@ -78,35 +78,30 @@ static QualType getCharPtrType(ASTContext &Ctx, CharKind CK) {
7878 : Ctx.WideCharTy );
7979}
8080
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> {
9085 mutable const char *CurrentFunctionDescription = nullptr ;
9186
9287public:
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 " ; }
110105
111106 static void *getTag () { static int tag; return &tag; }
112107
@@ -384,7 +379,7 @@ ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C,
384379 assumeZero (C, State, l, Arg.Expression ->getType ());
385380
386381 if (stateNull && !stateNonNull) {
387- if (Filter. CheckCStringNullArg ) {
382+ if (NullArg. isEnabled () ) {
388383 SmallString<80 > buf;
389384 llvm::raw_svector_ostream OS (buf);
390385 assert (CurrentFunctionDescription);
@@ -468,7 +463,7 @@ ProgramStateRef CStringChecker::checkInit(CheckerContext &C,
468463 return State;
469464
470465 // Ensure that we wouldn't read uninitialized value.
471- if (Filter. CheckCStringUninitializedRead &&
466+ if (UninitializedRead. isEnabled () &&
472467 State->getSVal (*FirstElementVal).isUndef ()) {
473468 llvm::SmallString<258 > Buf;
474469 llvm::raw_svector_ostream OS (Buf);
@@ -524,7 +519,7 @@ ProgramStateRef CStringChecker::checkInit(CheckerContext &C,
524519 if (!isa<Loc>(LastElementVal))
525520 return State;
526521
527- if (Filter. CheckCStringUninitializedRead &&
522+ if (UninitializedRead. isEnabled () &&
528523 State->getSVal (LastElementVal.castAs <Loc>()).isUndef ()) {
529524 const llvm::APSInt *IdxInt = LastIdx.getAsInteger ();
530525 // If we can't get emit a sensible last element index, just bail out --
@@ -581,13 +576,9 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
581576
582577 auto [StInBound, StOutBound] = state->assumeInBoundDual (*Idx, Size);
583578 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 ())
588580 return nullptr ;
589581
590- // Emit a bug report.
591582 ErrorMessage Message =
592583 createOutOfBoundErrorMsg (CurrentFunctionDescription, Access);
593584 emitOutOfBoundsBug (C, StOutBound, Buffer.Expression , Message);
@@ -620,7 +611,7 @@ CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State,
620611 return nullptr ;
621612
622613 // If out-of-bounds checking is turned off, skip the rest.
623- if (!Filter. CheckCStringOutOfBounds )
614+ if (!OutOfBounds. isEnabled () )
624615 return State;
625616
626617 SVal BufStart =
@@ -670,7 +661,7 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
670661 SizeArgExpr Size, AnyArgExpr First,
671662 AnyArgExpr Second,
672663 CharKind CK) const {
673- if (!Filter. CheckCStringBufferOverlap )
664+ if (!BufferOverlap. isEnabled () )
674665 return state;
675666
676667 // 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,
789780 if (!N)
790781 return ;
791782
792- if (!BT_Overlap)
793- BT_Overlap.reset (new BugType (Filter.CheckNameCStringBufferOverlap ,
794- categories::UnixAPI, " Improper arguments" ));
795-
796783 // Generate a report for this bug.
797784 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);
799786 report->addRange (First->getSourceRange ());
800787 report->addRange (Second->getSourceRange ());
801788
@@ -805,15 +792,8 @@ void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state,
805792void CStringChecker::emitNullArgBug (CheckerContext &C, ProgramStateRef State,
806793 const Stmt *S, StringRef WarningMsg) const {
807794 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-
815795 auto Report =
816- std::make_unique<PathSensitiveBugReport>(*BT_Null , WarningMsg, N);
796+ std::make_unique<PathSensitiveBugReport>(NullArg , WarningMsg, N);
817797 Report->addRange (S->getSourceRange ());
818798 if (const auto *Ex = dyn_cast<Expr>(S))
819799 bugreporter::trackExpressionValue (N, Ex, *Report);
@@ -826,12 +806,8 @@ void CStringChecker::emitUninitializedReadBug(CheckerContext &C,
826806 const Expr *E, const MemRegion *R,
827807 StringRef Msg) const {
828808 if (ExplodedNode *N = C.generateErrorNode (State)) {
829- if (!BT_UninitRead)
830- BT_UninitRead.reset (new BugType (Filter.CheckNameCStringUninitializedRead ,
831- " Accessing unitialized/garbage values" ));
832-
833809 auto Report =
834- std::make_unique<PathSensitiveBugReport>(*BT_UninitRead , Msg, N);
810+ std::make_unique<PathSensitiveBugReport>(UninitializedRead , Msg, N);
835811 Report->addNote (" Other elements might also be undefined" ,
836812 Report->getLocation ());
837813 Report->addRange (E->getSourceRange ());
@@ -845,17 +821,11 @@ void CStringChecker::emitOutOfBoundsBug(CheckerContext &C,
845821 ProgramStateRef State, const Stmt *S,
846822 StringRef WarningMsg) const {
847823 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-
854824 // FIXME: It would be nice to eventually make this diagnostic more clear,
855825 // e.g., by referencing the original declaration or by saying *why* this
856826 // reference is outside the range.
857827 auto Report =
858- std::make_unique<PathSensitiveBugReport>(*BT_Bounds , WarningMsg, N);
828+ std::make_unique<PathSensitiveBugReport>(OutOfBounds , WarningMsg, N);
859829 Report->addRange (S->getSourceRange ());
860830 C.emitReport (std::move (Report));
861831 }
@@ -865,15 +835,8 @@ void CStringChecker::emitNotCStringBug(CheckerContext &C, ProgramStateRef State,
865835 const Stmt *S,
866836 StringRef WarningMsg) const {
867837 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-
875838 auto Report =
876- std::make_unique<PathSensitiveBugReport>(*BT_NotCString , WarningMsg, N);
839+ std::make_unique<PathSensitiveBugReport>(NotNullTerm , WarningMsg, N);
877840
878841 Report->addRange (S->getSourceRange ());
879842 C.emitReport (std::move (Report));
@@ -883,22 +846,14 @@ void CStringChecker::emitNotCStringBug(CheckerContext &C, ProgramStateRef State,
883846void CStringChecker::emitAdditionOverflowBug (CheckerContext &C,
884847 ProgramStateRef State) const {
885848 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-
894849 // This isn't a great error message, but this should never occur in real
895850 // code anyway -- you'd have to create a buffer longer than a size_t can
896851 // represent, which is sort of a contradiction.
897852 const char *WarningMsg =
898853 " This expression will create a string whose length is too big to "
899854 " be represented as a size_t" ;
900855
901- auto Report = std::make_unique<PathSensitiveBugReport>(*BT_AdditionOverflow ,
856+ auto Report = std::make_unique<PathSensitiveBugReport>(AdditionOverflow ,
902857 WarningMsg, N);
903858 C.emitReport (std::move (Report));
904859 }
@@ -909,7 +864,7 @@ ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C,
909864 NonLoc left,
910865 NonLoc right) const {
911866 // If out-of-bounds checking is turned off, skip the rest.
912- if (!Filter. CheckCStringOutOfBounds )
867+ if (!OutOfBounds. isEnabled () )
913868 return state;
914869
915870 // If a previous check has failed, propagate the failure.
@@ -1048,7 +1003,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
10481003 // C string. In the context of locations, the only time we can issue such
10491004 // a warning is for labels.
10501005 if (std::optional<loc::GotoLabel> Label = Buf.getAs <loc::GotoLabel>()) {
1051- if (Filter. CheckCStringNotNullTerm ) {
1006+ if (NotNullTerm. isEnabled () ) {
10521007 SmallString<120 > buf;
10531008 llvm::raw_svector_ostream os (buf);
10541009 assert (CurrentFunctionDescription);
@@ -1110,7 +1065,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
11101065 // Other regions (mostly non-data) can't have a reliable C string length.
11111066 // In this case, an error is emitted and UndefinedVal is returned.
11121067 // The caller should always be prepared to handle this case.
1113- if (Filter. CheckCStringNotNullTerm ) {
1068+ if (NotNullTerm. isEnabled () ) {
11141069 SmallString<120 > buf;
11151070 llvm::raw_svector_ostream os (buf);
11161071
@@ -2873,24 +2828,27 @@ void CStringChecker::checkDeadSymbols(SymbolReaper &SR,
28732828}
28742829
28752830void 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>();
28772835}
28782836
2879- bool ento::shouldRegisterCStringModeling (const CheckerManager &mgr ) {
2837+ bool ento::shouldRegisterCStringModeling (const CheckerManager &) {
28802838 return true ;
28812839}
28822840
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); \
28882844 } \
28892845 \
2890- bool ento::shouldRegister##name(const CheckerManager &mgr) { return true ; }
2846+ bool ento::shouldRegisterCString##NAME(const CheckerManager &) { \
2847+ return true ; \
2848+ }
28912849
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