зеркало из https://github.com/mozilla/gecko-dev.git
85 строки
3.1 KiB
C++
85 строки
3.1 KiB
C++
/* 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;
|
|
}
|
|
}
|
|
}
|