[clang] b284005 - [analyzer] Add a syntactic security check for ObjC NSCoder API.
Artem Dergachev via cfe-commits
cfe-commits at lists.llvm.org
Thu Dec 19 14:55:45 PST 2019
Author: Artem Dergachev
Date: 2019-12-19T14:54:29-08:00
New Revision: b284005072122fe4af879725e3c8090009f89ca0
URL: https://github.com/llvm/llvm-project/commit/b284005072122fe4af879725e3c8090009f89ca0
DIFF: https://github.com/llvm/llvm-project/commit/b284005072122fe4af879725e3c8090009f89ca0.diff
LOG: [analyzer] Add a syntactic security check for ObjC NSCoder API.
Method '-[NSCoder decodeValueOfObjCType:at:]' is not only deprecated
but also a security hazard, hence a loud check.
Differential Revision: https://reviews.llvm.org/D71728
Added:
clang/test/Analysis/security-syntax-checks-nscoder.m
Modified:
clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
clang/lib/Driver/ToolChains/Clang.cpp
clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
clang/www/analyzer/available_checks.html
Removed:
################################################################################
diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 43632e801d2b..5b23de418999 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -773,6 +773,11 @@ def DeprecatedOrUnsafeBufferHandling :
Dependencies<[SecuritySyntaxChecker]>,
Documentation<HasDocumentation>;
+def decodeValueOfObjCType : Checker<"decodeValueOfObjCType">,
+ HelpText<"Warn on uses of the '-decodeValueOfObjCType:at:' method">,
+ Dependencies<[SecuritySyntaxChecker]>,
+ Documentation<HasDocumentation>;
+
} // end "security.insecureAPI"
let ParentPackage = Security in {
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 6b93dc2939e1..1347c3de913e 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -2789,8 +2789,11 @@ static void RenderAnalyzerOptions(const ArgList &Args, ArgStringList &CmdArgs,
CmdArgs.push_back("-analyzer-disable-checker=unix.Vfork");
}
- if (Triple.isOSDarwin())
+ if (Triple.isOSDarwin()) {
CmdArgs.push_back("-analyzer-checker=osx");
+ CmdArgs.push_back(
+ "-analyzer-checker=security.insecureAPI.decodeValueOfObjCType");
+ }
CmdArgs.push_back("-analyzer-checker=deadcode");
diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
index 260a2896e78c..d9ffa562c0aa 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
@@ -49,6 +49,7 @@ struct ChecksFilter {
DefaultBool check_vfork;
DefaultBool check_FloatLoopCounter;
DefaultBool check_UncheckedReturn;
+ DefaultBool check_decodeValueOfObjCType;
CheckerNameRef checkName_bcmp;
CheckerNameRef checkName_bcopy;
@@ -63,6 +64,7 @@ struct ChecksFilter {
CheckerNameRef checkName_vfork;
CheckerNameRef checkName_FloatLoopCounter;
CheckerNameRef checkName_UncheckedReturn;
+ CheckerNameRef checkName_decodeValueOfObjCType;
};
class WalkAST : public StmtVisitor<WalkAST> {
@@ -83,6 +85,7 @@ class WalkAST : public StmtVisitor<WalkAST> {
// Statement visitor methods.
void VisitCallExpr(CallExpr *CE);
+ void VisitObjCMessageExpr(ObjCMessageExpr *CE);
void VisitForStmt(ForStmt *S);
void VisitCompoundStmt (CompoundStmt *S);
void VisitStmt(Stmt *S) { VisitChildren(S); }
@@ -93,6 +96,7 @@ class WalkAST : public StmtVisitor<WalkAST> {
bool checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD);
typedef void (WalkAST::*FnCheck)(const CallExpr *, const FunctionDecl *);
+ typedef void (WalkAST::*MsgCheck)(const ObjCMessageExpr *);
// Checker-specific methods.
void checkLoopConditionForFloat(const ForStmt *FS);
@@ -110,6 +114,7 @@ class WalkAST : public StmtVisitor<WalkAST> {
void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD);
void checkCall_random(const CallExpr *CE, const FunctionDecl *FD);
void checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD);
+ void checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME);
void checkUncheckedReturnValue(CallExpr *CE);
};
} // end anonymous namespace
@@ -182,6 +187,20 @@ void WalkAST::VisitCallExpr(CallExpr *CE) {
VisitChildren(CE);
}
+void WalkAST::VisitObjCMessageExpr(ObjCMessageExpr *ME) {
+ MsgCheck evalFunction =
+ llvm::StringSwitch<MsgCheck>(ME->getSelector().getAsString())
+ .Case("decodeValueOfObjCType:at:",
+ &WalkAST::checkMsg_decodeValueOfObjCType)
+ .Default(nullptr);
+
+ if (evalFunction)
+ (this->*evalFunction)(ME);
+
+ // Recurse and check children.
+ VisitChildren(ME);
+}
+
void WalkAST::VisitCompoundStmt(CompoundStmt *S) {
for (Stmt *Child : S->children())
if (Child) {
@@ -923,6 +942,54 @@ void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) {
CELoc, CE->getCallee()->getSourceRange());
}
+//===----------------------------------------------------------------------===//
+// Check: '-decodeValueOfObjCType:at:' should not be used.
+// It is deprecated in favor of '-decodeValueOfObjCType:at:size:' due to
+// likelihood of buffer overflows.
+//===----------------------------------------------------------------------===//
+
+void WalkAST::checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME) {
+ if (!filter.check_decodeValueOfObjCType)
+ return;
+
+ // Check availability of the secure alternative:
+ // iOS 11+, macOS 10.13+, tvOS 11+, and watchOS 4.0+
+ // FIXME: We probably shouldn't register the check if it's not available.
+ const TargetInfo &TI = AC->getASTContext().getTargetInfo();
+ const llvm::Triple &T = TI.getTriple();
+ const VersionTuple &VT = TI.getPlatformMinVersion();
+ switch (T.getOS()) {
+ case llvm::Triple::IOS:
+ if (VT < VersionTuple(11, 0))
+ return;
+ break;
+ case llvm::Triple::MacOSX:
+ if (VT < VersionTuple(10, 13))
+ return;
+ break;
+ case llvm::Triple::WatchOS:
+ if (VT < VersionTuple(4, 0))
+ return;
+ break;
+ case llvm::Triple::TvOS:
+ if (VT < VersionTuple(11, 0))
+ return;
+ break;
+ default:
+ return;
+ }
+
+ PathDiagnosticLocation MELoc =
+ PathDiagnosticLocation::createBegin(ME, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(
+ AC->getDecl(), filter.checkName_decodeValueOfObjCType,
+ "Potential buffer overflow in '-decodeValueOfObjCType:at:'", "Security",
+ "Deprecated method '-decodeValueOfObjCType:at:' is insecure "
+ "as it can lead to potential buffer overflows. Use the safer "
+ "'-decodeValueOfObjCType:at:size:' method.",
+ MELoc, ME->getSourceRange());
+}
+
//===----------------------------------------------------------------------===//
// Check: Should check whether privileges are dropped successfully.
// Originally: <rdar://problem/6337132>
@@ -1035,3 +1102,4 @@ REGISTER_CHECKER(vfork)
REGISTER_CHECKER(FloatLoopCounter)
REGISTER_CHECKER(UncheckedReturn)
REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling)
+REGISTER_CHECKER(decodeValueOfObjCType)
diff --git a/clang/test/Analysis/security-syntax-checks-nscoder.m b/clang/test/Analysis/security-syntax-checks-nscoder.m
new file mode 100644
index 000000000000..23aa95bedccd
--- /dev/null
+++ b/clang/test/Analysis/security-syntax-checks-nscoder.m
@@ -0,0 +1,36 @@
+// RUN: %clang_analyze_cc1 -triple thumbv7-apple-ios11.0 -verify=available \
+// RUN: -analyzer-checker=security.insecureAPI.decodeValueOfObjCType %s
+// RUN: %clang_analyze_cc1 -triple thumbv7-apple-ios10.0 -verify=notavailable \
+// RUN: -analyzer-checker=security.insecureAPI.decodeValueOfObjCType %s
+// RUN: %clang_analyze_cc1 -triple x86_64-apple-macos10.13 -verify=available \
+// RUN: -analyzer-checker=security.insecureAPI.decodeValueOfObjCType %s
+// RUN: %clang_analyze_cc1 -triple x86_64-apple-macos10.12 -verify=notavailable \
+// RUN: -analyzer-checker=security.insecureAPI.decodeValueOfObjCType %s
+// RUN: %clang_analyze_cc1 -triple thumbv7-apple-watchos4.0 -verify=available \
+// RUN: -analyzer-checker=security.insecureAPI.decodeValueOfObjCType %s
+// RUN: %clang_analyze_cc1 -triple thumbv7-apple-watchos3.0 -verify=notavailable \
+// RUN: -analyzer-checker=security.insecureAPI.decodeValueOfObjCType %s
+// RUN: %clang_analyze_cc1 -triple thumbv7-apple-tvos11.0 -verify=available \
+// RUN: -analyzer-checker=security.insecureAPI.decodeValueOfObjCType %s
+// RUN: %clang_analyze_cc1 -triple thumbv7-apple-tvos10.0 -verify=notavailable \
+// RUN: -analyzer-checker=security.insecureAPI.decodeValueOfObjCType %s
+
+// notavailable-no-diagnostics
+
+typedef unsigned long NSUInteger;
+
+ at interface NSCoder
+- (void)decodeValueOfObjCType:(const char *)type
+ at:(void *)data;
+- (void)decodeValueOfObjCType:(const char *)type
+ at:(void *)data
+ size:(NSUInteger)size;
+ at end
+
+void test(NSCoder *decoder) {
+ // This would be a vulnerability on 64-bit platforms
+ // but not on 32-bit platforms.
+ NSUInteger x;
+ [decoder decodeValueOfObjCType:"I" at:&x]; // available-warning{{Deprecated method '-decodeValueOfObjCType:at:' is insecure as it can lead to potential buffer overflows. Use the safer '-decodeValueOfObjCType:at:size:' method}}
+ [decoder decodeValueOfObjCType:"I" at:&x size:sizeof(x)]; // no-warning
+}
diff --git a/clang/www/analyzer/available_checks.html b/clang/www/analyzer/available_checks.html
index 12e66f11b118..501a2b306dfa 100644
--- a/clang/www/analyzer/available_checks.html
+++ b/clang/www/analyzer/available_checks.html
@@ -1446,6 +1446,22 @@ <h3 id="security_checkers">Security Checkers</h3>
}
</pre></div></div></td></tr>
+
+<tr><td><a id="security.insecureAPI.decodeValueOfObjCType"><div class="namedescr expandable"><span class="name">
+security.insecureAPI.decodeValueOfObjCType</span><span class="lang">
+(ObjC)</span><div class="descr">
+Warn on uses of the <code>-[NSCoder decodeValueOfObjCType:at:]</code> method.
+The safe alternative is <code>-[NSCoder decodeValueOfObjCType:at:size:]</code>.</div></div></a></td>
+<td><div class="exampleContainer expandable">
+<div class="example"><pre>
+void test(NSCoder *decoder) {
+ // This would be a vulnerability on 64-bit platforms
+ // but not on 32-bit platforms.
+ NSUInteger x;
+ [decoder decodeValueOfObjCType:"I" at:&x]; // warn
+}
+</pre></div></div></td></tr>
+
</tbody></table>
<!-- =========================== unix =========================== -->
More information about the cfe-commits
mailing list