Source code

Revision control

Other Tools

1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
* License, v. 2.0. If a copy of the MPL was not distributed with this
3
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
#include "SprintfLiteralChecker.h"
6
#include "CustomMatchers.h"
7
8
void SprintfLiteralChecker::registerMatchers(MatchFinder *AstMatcher) {
9
AstMatcher->addMatcher(
10
callExpr(
11
isSnprintfLikeFunc(),
12
allOf(hasArgument(
13
0, ignoringParenImpCasts(declRefExpr().bind("buffer"))),
14
anyOf(hasArgument(1, sizeOfExpr(has(ignoringParenImpCasts(
15
declRefExpr().bind("size"))))),
16
hasArgument(1, integerLiteral().bind("immediate")),
17
hasArgument(1, declRefExpr(to(varDecl(
18
hasType(isConstQualified()),
19
hasInitializer(integerLiteral().bind(
20
"constant")))))))))
21
.bind("funcCall"),
22
this);
23
}
24
25
void SprintfLiteralChecker::check(const MatchFinder::MatchResult &Result) {
26
if (!Result.Context->getLangOpts().CPlusPlus) {
27
// SprintfLiteral is not usable in C, so there is no point in issuing these
28
// warnings.
29
return;
30
}
31
32
const char *Error =
33
"Use %1 instead of %0 when writing into a character array.";
34
const char *Note =
35
"This will prevent passing in the wrong size to %0 accidentally.";
36
37
const CallExpr *D = Result.Nodes.getNodeAs<CallExpr>("funcCall");
38
39
StringRef Name = D->getDirectCallee()->getName();
40
const char *Replacement;
41
if (Name == "snprintf") {
42
Replacement = "SprintfLiteral";
43
} else {
44
assert(Name == "vsnprintf");
45
Replacement = "VsprintfLiteral";
46
}
47
48
const DeclRefExpr *Buffer = Result.Nodes.getNodeAs<DeclRefExpr>("buffer");
49
const DeclRefExpr *Size = Result.Nodes.getNodeAs<DeclRefExpr>("size");
50
if (Size) {
51
// Match calls like snprintf(x, sizeof(x), ...).
52
if (Buffer->getFoundDecl() != Size->getFoundDecl()) {
53
return;
54
}
55
56
diag(D->getBeginLoc(), Error, DiagnosticIDs::Error) << Name << Replacement;
57
diag(D->getBeginLoc(), Note, DiagnosticIDs::Note) << Name;
58
return;
59
}
60
61
const QualType QType = Buffer->getType();
62
const ConstantArrayType *Type =
63
dyn_cast<ConstantArrayType>(QType.getTypePtrOrNull());
64
if (Type) {
65
// Match calls like snprintf(x, 100, ...), where x is int[100];
66
const IntegerLiteral *Literal =
67
Result.Nodes.getNodeAs<IntegerLiteral>("immediate");
68
if (!Literal) {
69
// Match calls like: const int y = 100; snprintf(x, y, ...);
70
Literal = Result.Nodes.getNodeAs<IntegerLiteral>("constant");
71
}
72
73
// We're going to assume here that the bitwidth of both of these values fits
74
// within 64 bits. and zero-extend both values to 64-bits before comparing
75
// them.
76
uint64_t Size = Type->getSize().getZExtValue();
77
uint64_t Lit = Literal->getValue().getZExtValue();
78
if (Size <= Lit) {
79
diag(D->getBeginLoc(), Error, DiagnosticIDs::Error)
80
<< Name << Replacement;
81
diag(D->getBeginLoc(), Note, DiagnosticIDs::Note) << Name;
82
}
83
}
84
}