// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "CheckIPCVisitor.h"
using namespace clang;
namespace chrome_checker {
namespace {
const char kWriteParamBadType[] =
"[chromium-ipc] IPC::WriteParam() is called on blocklisted type '%0'%1.";
const char kTupleBadType[] =
"[chromium-ipc] IPC tuple references banned type '%0'%1.";
const char kWriteParamBadSignature[] =
"[chromium-ipc] IPC::WriteParam() is expected to have two arguments.";
const char kNoteSeeHere[] =
"see here";
} // namespace
CheckIPCVisitor::CheckIPCVisitor(CompilerInstance& compiler)
: compiler_(compiler), context_(nullptr) {
auto& diagnostics = compiler_.getDiagnostics();
error_write_param_bad_type_ = diagnostics.getCustomDiagID(
DiagnosticsEngine::Error, kWriteParamBadType);
error_tuple_bad_type_ = diagnostics.getCustomDiagID(
DiagnosticsEngine::Error, kTupleBadType);
error_write_param_bad_signature_ = diagnostics.getCustomDiagID(
DiagnosticsEngine::Error, kWriteParamBadSignature);
note_see_here_ = diagnostics.getCustomDiagID(
DiagnosticsEngine::Note, kNoteSeeHere);
blocklisted_typedefs_ = llvm::StringSet<>({
"intmax_t",
"uintmax_t",
"intptr_t",
"uintptr_t",
"wint_t",
"size_t",
"rsize_t",
"ssize_t",
"ptrdiff_t",
"dev_t",
"off_t",
"clock_t",
"time_t",
"suseconds_t"
});
}
void CheckIPCVisitor::BeginDecl(Decl* decl) {
decl_stack_.push_back(decl);
}
void CheckIPCVisitor::EndDecl() {
decl_stack_.pop_back();
}
void CheckIPCVisitor::VisitTemplateSpecializationType(
TemplateSpecializationType* spec) {
ValidateCheckedTuple(spec);
}
void CheckIPCVisitor::VisitCallExpr(CallExpr* call_expr) {
ValidateWriteParam(call_expr);
}
bool CheckIPCVisitor::ValidateWriteParam(const CallExpr* call_expr) {
const FunctionDecl* callee_decl = call_expr->getDirectCallee();
if (!callee_decl ||
callee_decl->getQualifiedNameAsString() != "IPC::WriteParam") {
return true;
}
return ValidateWriteParamSignature(call_expr) &&
ValidateWriteParamArgument(call_expr->getArg(1));
}
// Checks that IPC::WriteParam() has expected signature.
bool CheckIPCVisitor::ValidateWriteParamSignature(
const CallExpr* call_expr) {
if (call_expr->getNumArgs() != 2) {
compiler_.getDiagnostics().Report(
call_expr->getExprLoc(), error_write_param_bad_signature_);
return false;
}
return true;
}
// Checks that IPC::WriteParam() argument type is allowed.
// See CheckType() for specifics.
bool CheckIPCVisitor::ValidateWriteParamArgument(const Expr* arg_expr) {
if (auto* parent_fn_decl = GetParentDecl<FunctionDecl>()) {
auto template_kind = parent_fn_decl->getTemplatedKind();
if (template_kind != FunctionDecl::TK_NonTemplate &&
template_kind != FunctionDecl::TK_FunctionTemplate) {
// Skip all specializations - we don't check WriteParam() on dependent
// types (typedef info gets lost), and we checked all non-dependent uses
// earlier (when we checked the template itself).
return true;
}
}
QualType arg_type;
arg_expr = arg_expr->IgnoreImplicit();
if (auto* cast_expr = dyn_cast<ExplicitCastExpr>(arg_expr)) {
arg_type = cast_expr->getTypeAsWritten();
} else {
arg_type = arg_expr->getType();
}
CheckDetails details;
if (CheckType(arg_type, &details)) {
return true;
}
ReportCheckError(details,
arg_expr->getExprLoc(),
error_write_param_bad_type_);
return false;
}
// Checks that IPC::CheckedTuple<> is specialized with allowed types.
// See CheckType() above for specifics.
bool CheckIPCVisitor::ValidateCheckedTuple(
const TemplateSpecializationType* spec) {
TemplateDecl* decl = spec->getTemplateName().getAsTemplateDecl();
if (!decl || decl->getQualifiedNameAsString() != "IPC::CheckedTuple") {
return true;
}
bool valid = true;
for (const TemplateArgument& arg : spec->template_arguments()) {
CheckDetails details;
if (CheckTemplateArgument(arg, &details)) {
continue;
}
valid = false;
auto* parent_decl = GetParentDecl<Decl>();
ReportCheckError(
details, parent_decl ? parent_decl->getBeginLoc() : SourceLocation(),
error_tuple_bad_type_);
}
return valid;
}
template <typename T>
const T* CheckIPCVisitor::GetParentDecl() const {
for (auto i = decl_stack_.rbegin(); i != decl_stack_.rend(); ++i) {
if (auto* parent = dyn_cast_or_null<T>(*i)) {
return parent;
}
}
return nullptr;
}
bool CheckIPCVisitor::IsBlacklistedType(QualType type) const {
return context_->hasSameUnqualifiedType(type, context_->LongTy) ||
context_->hasSameUnqualifiedType(type, context_->UnsignedLongTy);
}
bool CheckIPCVisitor::IsBlacklistedTypedef(const TypedefNameDecl* tdef) const {
return blocklisted_typedefs_.find(tdef->getName()) !=
blocklisted_typedefs_.end();
}
// Checks that integer type is allowed (not blocklisted).
bool CheckIPCVisitor::CheckIntegerType(QualType type,
CheckDetails* details) const {
bool seen_typedef = false;
while (true) {
details->exit_type = type;
if (auto* tdef = dyn_cast<TypedefType>(type)) {
if (IsBlacklistedTypedef(tdef->getDecl())) {
return false;
}
details->typedefs.push_back(tdef);
seen_typedef = true;
}
QualType desugared_type =
type->getLocallyUnqualifiedSingleStepDesugaredType();
if (desugared_type == type) {
break;
}
type = desugared_type;
}
return seen_typedef || !IsBlacklistedType(type);
}
// Checks that |type| is allowed (not blocklisted), recursively visiting
// template specializations.
bool CheckIPCVisitor::CheckType(QualType type, CheckDetails* details) const {
if (type->isReferenceType()) {
type = type->getPointeeType();
}
type = type.getLocalUnqualifiedType();
if (details->entry_type.isNull()) {
details->entry_type = type;
}
if (type->isIntegerType()) {
return CheckIntegerType(type, details);
}
while (true) {
if (auto* spec = dyn_cast<TemplateSpecializationType>(type)) {
for (const TemplateArgument& arg : spec->template_arguments()) {
if (!CheckTemplateArgument(arg, details)) {
return false;
}
}
return true;
}
if (auto* record = dyn_cast<RecordType>(type)) {
if (auto* spec = dyn_cast<ClassTemplateSpecializationDecl>(
record->getDecl())) {
const TemplateArgumentList& args = spec->getTemplateArgs();
for (unsigned i = 0; i != args.size(); ++i) {
if (!CheckTemplateArgument(args[i], details)) {
return false;
}
}
}
return true;
}
if (auto* tdef = dyn_cast<TypedefType>(type)) {
details->typedefs.push_back(tdef);
}
QualType desugared_type =
type->getLocallyUnqualifiedSingleStepDesugaredType();
if (desugared_type == type) {
break;
}
type = desugared_type;
}
return true;
}
bool CheckIPCVisitor::CheckTemplateArgument(const TemplateArgument& arg,
CheckDetails* details) const {
return arg.getKind() != TemplateArgument::Type ||
CheckType(arg.getAsType(), details);
}
void CheckIPCVisitor::ReportCheckError(const CheckDetails& details,
SourceLocation loc,
unsigned error) {
DiagnosticsEngine& diagnostics = compiler_.getDiagnostics();
std::string entry_type = details.entry_type.getAsString();
std::string exit_type = details.exit_type.getAsString();
std::string via;
if (entry_type != exit_type) {
via = " via '" + entry_type + "'";
}
diagnostics.Report(loc, error) << exit_type << via;
for (const TypedefType* tdef: details.typedefs) {
diagnostics.Report(tdef->getDecl()->getLocation(), note_see_here_);
}
}
} // namespace chrome_checker