@@ -49,6 +49,7 @@ struct ChecksFilter {
49
49
DefaultBool check_vfork;
50
50
DefaultBool check_FloatLoopCounter;
51
51
DefaultBool check_UncheckedReturn;
52
+ DefaultBool check_decodeValueOfObjCType;
52
53
53
54
CheckerNameRef checkName_bcmp;
54
55
CheckerNameRef checkName_bcopy;
@@ -63,6 +64,7 @@ struct ChecksFilter {
63
64
CheckerNameRef checkName_vfork;
64
65
CheckerNameRef checkName_FloatLoopCounter;
65
66
CheckerNameRef checkName_UncheckedReturn;
67
+ CheckerNameRef checkName_decodeValueOfObjCType;
66
68
};
67
69
68
70
class WalkAST : public StmtVisitor <WalkAST> {
@@ -83,6 +85,7 @@ class WalkAST : public StmtVisitor<WalkAST> {
83
85
84
86
// Statement visitor methods.
85
87
void VisitCallExpr (CallExpr *CE);
88
+ void VisitObjCMessageExpr (ObjCMessageExpr *CE);
86
89
void VisitForStmt (ForStmt *S);
87
90
void VisitCompoundStmt (CompoundStmt *S);
88
91
void VisitStmt (Stmt *S) { VisitChildren (S); }
@@ -93,6 +96,7 @@ class WalkAST : public StmtVisitor<WalkAST> {
93
96
bool checkCall_strCommon (const CallExpr *CE, const FunctionDecl *FD);
94
97
95
98
typedef void (WalkAST::*FnCheck)(const CallExpr *, const FunctionDecl *);
99
+ typedef void (WalkAST::*MsgCheck)(const ObjCMessageExpr *);
96
100
97
101
// Checker-specific methods.
98
102
void checkLoopConditionForFloat (const ForStmt *FS);
@@ -110,6 +114,7 @@ class WalkAST : public StmtVisitor<WalkAST> {
110
114
void checkCall_rand (const CallExpr *CE, const FunctionDecl *FD);
111
115
void checkCall_random (const CallExpr *CE, const FunctionDecl *FD);
112
116
void checkCall_vfork (const CallExpr *CE, const FunctionDecl *FD);
117
+ void checkMsg_decodeValueOfObjCType (const ObjCMessageExpr *ME);
113
118
void checkUncheckedReturnValue (CallExpr *CE);
114
119
};
115
120
} // end anonymous namespace
@@ -182,6 +187,20 @@ void WalkAST::VisitCallExpr(CallExpr *CE) {
182
187
VisitChildren (CE);
183
188
}
184
189
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
+
185
204
void WalkAST::VisitCompoundStmt (CompoundStmt *S) {
186
205
for (Stmt *Child : S->children ())
187
206
if (Child) {
@@ -923,6 +942,54 @@ void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) {
923
942
CELoc, CE->getCallee ()->getSourceRange ());
924
943
}
925
944
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
+
926
993
// ===----------------------------------------------------------------------===//
927
994
// Check: Should check whether privileges are dropped successfully.
928
995
// Originally: <rdar://problem/6337132>
@@ -1035,3 +1102,4 @@ REGISTER_CHECKER(vfork)
1035
1102
REGISTER_CHECKER (FloatLoopCounter)
1036
1103
REGISTER_CHECKER (UncheckedReturn)
1037
1104
REGISTER_CHECKER (DeprecatedOrUnsafeBufferHandling)
1105
+ REGISTER_CHECKER (decodeValueOfObjCType)
0 commit comments