Source code
Revision control
Copy as Markdown
Other Tools
/* 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 "NonTerminatedStringChecker.h"
#include "CustomMatchers.h"
void NonTerminatedStringChecker::registerMatchers(MatchFinder *AstMatcher) {
AstMatcher->addMatcher(callExpr().bind("call"), this);
AstMatcher->addMatcher(cxxConstructExpr().bind("construct"), this);
}
template <typename CallExprT>
static void CheckArgs(BaseCheck &Check, const FunctionDecl *Func,
const CallExprT *Call) {
if (!Func) {
return;
}
unsigned NonDefaultArgCount = 0;
for (unsigned I = 0; I < Call->getNumArgs(); I++) {
if (!isa<CXXDefaultArgExpr>(Call->getArg(I))) {
NonDefaultArgCount++;
}
}
const char *Kind = nullptr;
if (isa<CXXOperatorCallExpr>(Call)) {
Kind = "operator";
} else if (Func->hasAttr<FormatAttr>()) {
Kind = "printf-like";
} else if (NonDefaultArgCount == 1) {
auto *Ident = Func->getIdentifier();
if (Ident && (Ident->isStr("BitwiseCast") || Ident->isStr("bit_cast"))) {
return; // These are casts, and don't look at the string
}
Kind = "single-argument";
}
if (!Kind) {
return;
}
for (unsigned I = 0; I < Call->getNumArgs(); I++) {
const Expr *BareArg = Call->getArg(I);
const Expr *Arg = IgnoreTrivials(BareArg);
const CallExpr *ArgCall = dyn_cast<CallExpr>(Arg);
if (!ArgCall) {
continue;
}
const FunctionDecl *ArgCallee = ArgCall->getDirectCallee();
if (!ArgCallee) {
continue;
}
// We may have been cast to another type like `void*`, in whichc ase we
// shouldn't fire the diagnostic.
const clang::PointerType *PT =
BareArg->getType()->template getAs<clang::PointerType>();
if (!PT) {
continue;
}
QualType Pointee = PT->getPointeeType().getUnqualifiedType();
if (!Pointee->isCharType() && !Pointee->isChar16Type()) {
continue;
}
if (hasCustomAttribute<moz_non_terminated_string>(ArgCallee)) {
Check.diag(ArgCall->getBeginLoc(),
"MOZ_NON_TERMINATED_STRING return value from '%0' passed as "
"an argument to %1 function '%2'",
DiagnosticIDs::Error)
<< ArgCallee->getQualifiedNameAsString() << Kind
<< Func->getQualifiedNameAsString();
}
}
}
void NonTerminatedStringChecker::check(const MatchFinder::MatchResult &Result) {
if (auto *Call = Result.Nodes.getNodeAs<CallExpr>("call")) {
CheckArgs(*this, Call->getDirectCallee(), Call);
}
if (auto *Construct = Result.Nodes.getNodeAs<CXXConstructExpr>("construct")) {
CheckArgs(*this, Construct->getConstructor(), Construct);
}
}