summaryrefslogtreecommitdiffstats
path: root/build/clang-plugin/SprintfLiteralChecker.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--build/clang-plugin/SprintfLiteralChecker.cpp84
1 files changed, 84 insertions, 0 deletions
diff --git a/build/clang-plugin/SprintfLiteralChecker.cpp b/build/clang-plugin/SprintfLiteralChecker.cpp
new file mode 100644
index 0000000000..94e8e2fd1b
--- /dev/null
+++ b/build/clang-plugin/SprintfLiteralChecker.cpp
@@ -0,0 +1,84 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SprintfLiteralChecker.h"
+#include "CustomMatchers.h"
+
+void SprintfLiteralChecker::registerMatchers(MatchFinder *AstMatcher) {
+ AstMatcher->addMatcher(
+ callExpr(
+ isSnprintfLikeFunc(),
+ allOf(hasArgument(
+ 0, ignoringParenImpCasts(declRefExpr().bind("buffer"))),
+ anyOf(hasArgument(1, sizeOfExpr(has(ignoringParenImpCasts(
+ declRefExpr().bind("size"))))),
+ hasArgument(1, integerLiteral().bind("immediate")),
+ hasArgument(1, declRefExpr(to(varDecl(
+ hasType(isConstQualified()),
+ hasInitializer(integerLiteral().bind(
+ "constant")))))))))
+ .bind("funcCall"),
+ this);
+}
+
+void SprintfLiteralChecker::check(const MatchFinder::MatchResult &Result) {
+ if (!Result.Context->getLangOpts().CPlusPlus) {
+ // SprintfLiteral is not usable in C, so there is no point in issuing these
+ // warnings.
+ return;
+ }
+
+ const char *Error =
+ "Use %1 instead of %0 when writing into a character array.";
+ const char *Note =
+ "This will prevent passing in the wrong size to %0 accidentally.";
+
+ const CallExpr *D = Result.Nodes.getNodeAs<CallExpr>("funcCall");
+
+ StringRef Name = D->getDirectCallee()->getName();
+ const char *Replacement;
+ if (Name == "snprintf") {
+ Replacement = "SprintfLiteral";
+ } else {
+ assert(Name == "vsnprintf");
+ Replacement = "VsprintfLiteral";
+ }
+
+ const DeclRefExpr *Buffer = Result.Nodes.getNodeAs<DeclRefExpr>("buffer");
+ const DeclRefExpr *Size = Result.Nodes.getNodeAs<DeclRefExpr>("size");
+ if (Size) {
+ // Match calls like snprintf(x, sizeof(x), ...).
+ if (Buffer->getFoundDecl() != Size->getFoundDecl()) {
+ return;
+ }
+
+ diag(D->getBeginLoc(), Error, DiagnosticIDs::Error) << Name << Replacement;
+ diag(D->getBeginLoc(), Note, DiagnosticIDs::Note) << Name;
+ return;
+ }
+
+ const QualType QType = Buffer->getType();
+ const ConstantArrayType *Type =
+ dyn_cast<ConstantArrayType>(QType.getTypePtrOrNull());
+ if (Type) {
+ // Match calls like snprintf(x, 100, ...), where x is int[100];
+ const IntegerLiteral *Literal =
+ Result.Nodes.getNodeAs<IntegerLiteral>("immediate");
+ if (!Literal) {
+ // Match calls like: const int y = 100; snprintf(x, y, ...);
+ Literal = Result.Nodes.getNodeAs<IntegerLiteral>("constant");
+ }
+
+ // We're going to assume here that the bitwidth of both of these values fits
+ // within 64 bits. and zero-extend both values to 64-bits before comparing
+ // them.
+ uint64_t Size = Type->getSize().getZExtValue();
+ uint64_t Lit = Literal->getValue().getZExtValue();
+ if (Size <= Lit) {
+ diag(D->getBeginLoc(), Error, DiagnosticIDs::Error)
+ << Name << Replacement;
+ diag(D->getBeginLoc(), Note, DiagnosticIDs::Note) << Name;
+ }
+ }
+}