@@ -49,6 +49,7 @@ struct ChecksFilter {
4949 DefaultBool check_vfork;
5050 DefaultBool check_FloatLoopCounter;
5151 DefaultBool check_UncheckedReturn;
52+ DefaultBool check_decodeValueOfObjCType;
5253
5354 CheckerNameRef checkName_bcmp;
5455 CheckerNameRef checkName_bcopy;
@@ -63,6 +64,7 @@ struct ChecksFilter {
6364 CheckerNameRef checkName_vfork;
6465 CheckerNameRef checkName_FloatLoopCounter;
6566 CheckerNameRef checkName_UncheckedReturn;
67+ CheckerNameRef checkName_decodeValueOfObjCType;
6668};
6769
6870class WalkAST : public StmtVisitor <WalkAST> {
@@ -83,6 +85,7 @@ class WalkAST : public StmtVisitor<WalkAST> {
8385
8486 // Statement visitor methods.
8587 void VisitCallExpr (CallExpr *CE);
88+ void VisitObjCMessageExpr (ObjCMessageExpr *CE);
8689 void VisitForStmt (ForStmt *S);
8790 void VisitCompoundStmt (CompoundStmt *S);
8891 void VisitStmt (Stmt *S) { VisitChildren (S); }
@@ -93,6 +96,7 @@ class WalkAST : public StmtVisitor<WalkAST> {
9396 bool checkCall_strCommon (const CallExpr *CE, const FunctionDecl *FD);
9497
9598 typedef void (WalkAST::*FnCheck)(const CallExpr *, const FunctionDecl *);
99+ typedef void (WalkAST::*MsgCheck)(const ObjCMessageExpr *);
96100
97101 // Checker-specific methods.
98102 void checkLoopConditionForFloat (const ForStmt *FS);
@@ -110,6 +114,7 @@ class WalkAST : public StmtVisitor<WalkAST> {
110114 void checkCall_rand (const CallExpr *CE, const FunctionDecl *FD);
111115 void checkCall_random (const CallExpr *CE, const FunctionDecl *FD);
112116 void checkCall_vfork (const CallExpr *CE, const FunctionDecl *FD);
117+ void checkMsg_decodeValueOfObjCType (const ObjCMessageExpr *ME);
113118 void checkUncheckedReturnValue (CallExpr *CE);
114119};
115120} // end anonymous namespace
@@ -182,6 +187,20 @@ void WalkAST::VisitCallExpr(CallExpr *CE) {
182187 VisitChildren (CE);
183188}
184189
190+ void WalkAST::VisitObjCMessageExpr (ObjCMessageExpr *ME) {
191+ MsgCheck evalFunction =
192+ llvm::StringSwitch<MsgCheck>(ME->getSelector ().getAsString ())
193+ .Case (" decodeValueOfObjCType:at:" ,
194+ &WalkAST::checkMsg_decodeValueOfObjCType)
195+ .Default (nullptr );
196+
197+ if (evalFunction)
198+ (this ->*evalFunction)(ME);
199+
200+ // Recurse and check children.
201+ VisitChildren (ME);
202+ }
203+
185204void WalkAST::VisitCompoundStmt (CompoundStmt *S) {
186205 for (Stmt *Child : S->children ())
187206 if (Child) {
@@ -923,6 +942,54 @@ void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) {
923942 CELoc, CE->getCallee ()->getSourceRange ());
924943}
925944
945+ // ===----------------------------------------------------------------------===//
946+ // Check: '-decodeValueOfObjCType:at:' should not be used.
947+ // It is deprecated in favor of '-decodeValueOfObjCType:at:size:' due to
948+ // likelihood of buffer overflows.
949+ // ===----------------------------------------------------------------------===//
950+
951+ void WalkAST::checkMsg_decodeValueOfObjCType (const ObjCMessageExpr *ME) {
952+ if (!filter.check_decodeValueOfObjCType )
953+ return ;
954+
955+ // Check availability of the secure alternative:
956+ // iOS 11+, macOS 10.13+, tvOS 11+, and watchOS 4.0+
957+ // FIXME: We probably shouldn't register the check if it's not available.
958+ const TargetInfo &TI = AC->getASTContext ().getTargetInfo ();
959+ const llvm::Triple &T = TI.getTriple ();
960+ const VersionTuple &VT = TI.getPlatformMinVersion ();
961+ switch (T.getOS ()) {
962+ case llvm::Triple::IOS:
963+ if (VT < VersionTuple (11 , 0 ))
964+ return ;
965+ break ;
966+ case llvm::Triple::MacOSX:
967+ if (VT < VersionTuple (10 , 13 ))
968+ return ;
969+ break ;
970+ case llvm::Triple::WatchOS:
971+ if (VT < VersionTuple (4 , 0 ))
972+ return ;
973+ break ;
974+ case llvm::Triple::TvOS:
975+ if (VT < VersionTuple (11 , 0 ))
976+ return ;
977+ break ;
978+ default :
979+ return ;
980+ }
981+
982+ PathDiagnosticLocation MELoc =
983+ PathDiagnosticLocation::createBegin (ME, BR.getSourceManager (), AC);
984+ BR.EmitBasicReport (
985+ AC->getDecl (), filter.checkName_decodeValueOfObjCType ,
986+ " Potential buffer overflow in '-decodeValueOfObjCType:at:'" , " Security" ,
987+ " Deprecated method '-decodeValueOfObjCType:at:' is insecure "
988+ " as it can lead to potential buffer overflows. Use the safer "
989+ " '-decodeValueOfObjCType:at:size:' method." ,
990+ MELoc, ME->getSourceRange ());
991+ }
992+
926993// ===----------------------------------------------------------------------===//
927994// Check: Should check whether privileges are dropped successfully.
928995// Originally: <rdar://problem/6337132>
@@ -1035,3 +1102,4 @@ REGISTER_CHECKER(vfork)
10351102REGISTER_CHECKER (FloatLoopCounter)
10361103REGISTER_CHECKER (UncheckedReturn)
10371104REGISTER_CHECKER (DeprecatedOrUnsafeBufferHandling)
1105+ REGISTER_CHECKER (decodeValueOfObjCType)
0 commit comments