/* 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("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("buffer"); const DeclRefExpr *Size = Result.Nodes.getNodeAs("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(QType.getTypePtrOrNull()); if (Type) { // Match calls like snprintf(x, 100, ...), where x is int[100]; const IntegerLiteral *Literal = Result.Nodes.getNodeAs("immediate"); if (!Literal) { // Match calls like: const int y = 100; snprintf(x, y, ...); Literal = Result.Nodes.getNodeAs("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; } } }