Home | History | Annotate | Download | only in plugins
      1 // Copyright (c) 2016 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "CheckIPCVisitor.h"
      6 
      7 using namespace clang;
      8 
      9 namespace chrome_checker {
     10 
     11 namespace {
     12 
     13 const char kWriteParamBadType[] =
     14     "[chromium-ipc] IPC::WriteParam() is called on blacklisted type '%0'%1.";
     15 
     16 const char kTupleBadType[] =
     17     "[chromium-ipc] IPC tuple references banned type '%0'%1.";
     18 
     19 const char kWriteParamBadSignature[] =
     20     "[chromium-ipc] IPC::WriteParam() is expected to have two arguments.";
     21 
     22 const char kNoteSeeHere[] =
     23     "see here";
     24 
     25 }  // namespace
     26 
     27 CheckIPCVisitor::CheckIPCVisitor(CompilerInstance& compiler)
     28   : compiler_(compiler), context_(nullptr) {
     29   auto& diagnostics = compiler_.getDiagnostics();
     30   error_write_param_bad_type_ = diagnostics.getCustomDiagID(
     31       DiagnosticsEngine::Error, kWriteParamBadType);
     32   error_tuple_bad_type_ = diagnostics.getCustomDiagID(
     33       DiagnosticsEngine::Error, kTupleBadType);
     34   error_write_param_bad_signature_ = diagnostics.getCustomDiagID(
     35       DiagnosticsEngine::Error, kWriteParamBadSignature);
     36   note_see_here_ = diagnostics.getCustomDiagID(
     37       DiagnosticsEngine::Note, kNoteSeeHere);
     38 
     39   blacklisted_typedefs_ = llvm::StringSet<>({
     40       "intmax_t",
     41       "uintmax_t",
     42       "intptr_t",
     43       "uintptr_t",
     44       "wint_t",
     45       "size_t",
     46       "rsize_t",
     47       "ssize_t",
     48       "ptrdiff_t",
     49       "dev_t",
     50       "off_t",
     51       "clock_t",
     52       "time_t",
     53       "suseconds_t"
     54   });
     55 }
     56 
     57 void CheckIPCVisitor::BeginDecl(Decl* decl) {
     58   decl_stack_.push_back(decl);
     59 }
     60 
     61 void CheckIPCVisitor::EndDecl() {
     62   decl_stack_.pop_back();
     63 }
     64 
     65 void CheckIPCVisitor::VisitTemplateSpecializationType(
     66     TemplateSpecializationType* spec) {
     67   ValidateCheckedTuple(spec);
     68 }
     69 
     70 void CheckIPCVisitor::VisitCallExpr(CallExpr* call_expr) {
     71   ValidateWriteParam(call_expr);
     72 }
     73 
     74 bool CheckIPCVisitor::ValidateWriteParam(const CallExpr* call_expr) {
     75   const FunctionDecl* callee_decl = call_expr->getDirectCallee();
     76   if (!callee_decl ||
     77       callee_decl->getQualifiedNameAsString() != "IPC::WriteParam") {
     78     return true;
     79   }
     80 
     81   return ValidateWriteParamSignature(call_expr) &&
     82       ValidateWriteParamArgument(call_expr->getArg(1));
     83 }
     84 
     85 // Checks that IPC::WriteParam() has expected signature.
     86 bool CheckIPCVisitor::ValidateWriteParamSignature(
     87     const CallExpr* call_expr) {
     88   if (call_expr->getNumArgs() != 2) {
     89     compiler_.getDiagnostics().Report(
     90         call_expr->getExprLoc(), error_write_param_bad_signature_);
     91     return false;
     92   }
     93   return true;
     94 }
     95 
     96 // Checks that IPC::WriteParam() argument type is allowed.
     97 // See CheckType() for specifics.
     98 bool CheckIPCVisitor::ValidateWriteParamArgument(const Expr* arg_expr) {
     99   if (auto* parent_fn_decl = GetParentDecl<FunctionDecl>()) {
    100     auto template_kind = parent_fn_decl->getTemplatedKind();
    101     if (template_kind != FunctionDecl::TK_NonTemplate &&
    102         template_kind != FunctionDecl::TK_FunctionTemplate) {
    103       // Skip all specializations - we don't check WriteParam() on dependent
    104       // types (typedef info gets lost), and we checked all non-dependent uses
    105       // earlier (when we checked the template itself).
    106       return true;
    107     }
    108   }
    109 
    110   QualType arg_type;
    111 
    112   arg_expr = arg_expr->IgnoreImplicit();
    113   if (auto* cast_expr = dyn_cast<ExplicitCastExpr>(arg_expr)) {
    114     arg_type = cast_expr->getTypeAsWritten();
    115   } else {
    116     arg_type = arg_expr->getType();
    117   }
    118 
    119   CheckDetails details;
    120   if (CheckType(arg_type, &details)) {
    121     return true;
    122   }
    123 
    124   ReportCheckError(details,
    125                    arg_expr->getExprLoc(),
    126                    error_write_param_bad_type_);
    127 
    128   return false;
    129 }
    130 
    131 // Checks that IPC::CheckedTuple<> is specialized with allowed types.
    132 // See CheckType() above for specifics.
    133 bool CheckIPCVisitor::ValidateCheckedTuple(
    134     const TemplateSpecializationType* spec) {
    135   TemplateDecl* decl = spec->getTemplateName().getAsTemplateDecl();
    136   if (!decl || decl->getQualifiedNameAsString() != "IPC::CheckedTuple") {
    137     return true;
    138   }
    139 
    140   bool valid = true;
    141   for (unsigned i = 0; i != spec->getNumArgs(); ++i) {
    142     const TemplateArgument& arg = spec->getArg(i);
    143     CheckDetails details;
    144     if (CheckTemplateArgument(arg, &details)) {
    145       continue;
    146     }
    147 
    148     valid = false;
    149 
    150     auto* parent_decl = GetParentDecl<Decl>();
    151     ReportCheckError(
    152         details,
    153         parent_decl ? parent_decl->getLocStart() : SourceLocation(),
    154         error_tuple_bad_type_);
    155   }
    156 
    157   return valid;
    158 }
    159 
    160 template <typename T>
    161 const T* CheckIPCVisitor::GetParentDecl() const {
    162   for (auto i = decl_stack_.rbegin(); i != decl_stack_.rend(); ++i) {
    163     if (auto* parent = dyn_cast_or_null<T>(*i)) {
    164       return parent;
    165     }
    166   }
    167   return nullptr;
    168 }
    169 
    170 
    171 bool CheckIPCVisitor::IsBlacklistedType(QualType type) const {
    172   return context_->hasSameUnqualifiedType(type, context_->LongTy) ||
    173       context_->hasSameUnqualifiedType(type, context_->UnsignedLongTy);
    174 }
    175 
    176 bool CheckIPCVisitor::IsBlacklistedTypedef(const TypedefNameDecl* tdef) const {
    177   return blacklisted_typedefs_.find(tdef->getName()) !=
    178       blacklisted_typedefs_.end();
    179 }
    180 
    181 // Checks that integer type is allowed (not blacklisted).
    182 bool CheckIPCVisitor::CheckIntegerType(QualType type,
    183                                        CheckDetails* details) const {
    184   bool seen_typedef = false;
    185   while (true) {
    186     details->exit_type = type;
    187 
    188     if (auto* tdef = dyn_cast<TypedefType>(type)) {
    189       if (IsBlacklistedTypedef(tdef->getDecl())) {
    190         return false;
    191       }
    192       details->typedefs.push_back(tdef);
    193       seen_typedef = true;
    194     }
    195 
    196     QualType desugared_type =
    197         type->getLocallyUnqualifiedSingleStepDesugaredType();
    198     if (desugared_type == type) {
    199       break;
    200     }
    201 
    202     type = desugared_type;
    203   }
    204 
    205   return seen_typedef || !IsBlacklistedType(type);
    206 }
    207 
    208 // Checks that |type| is allowed (not blacklisted), recursively visiting
    209 // template specializations.
    210 bool CheckIPCVisitor::CheckType(QualType type, CheckDetails* details) const {
    211   if (type->isReferenceType()) {
    212     type = type->getPointeeType();
    213   }
    214   type = type.getLocalUnqualifiedType();
    215 
    216   if (details->entry_type.isNull()) {
    217     details->entry_type = type;
    218   }
    219 
    220   if (type->isIntegerType()) {
    221     return CheckIntegerType(type, details);
    222   }
    223 
    224   while (true) {
    225     if (auto* spec = dyn_cast<TemplateSpecializationType>(type)) {
    226       for (const TemplateArgument& arg: *spec) {
    227         if (!CheckTemplateArgument(arg, details)) {
    228           return false;
    229         }
    230       }
    231       return true;
    232     }
    233 
    234     if (auto* record = dyn_cast<RecordType>(type)) {
    235       if (auto* spec = dyn_cast<ClassTemplateSpecializationDecl>(
    236               record->getDecl())) {
    237         const TemplateArgumentList& args = spec->getTemplateArgs();
    238         for (unsigned i = 0; i != args.size(); ++i) {
    239           if (!CheckTemplateArgument(args[i], details)) {
    240             return false;
    241           }
    242         }
    243       }
    244       return true;
    245     }
    246 
    247     if (auto* tdef = dyn_cast<TypedefType>(type)) {
    248       details->typedefs.push_back(tdef);
    249     }
    250 
    251     QualType desugared_type =
    252         type->getLocallyUnqualifiedSingleStepDesugaredType();
    253     if (desugared_type == type) {
    254       break;
    255     }
    256 
    257     type = desugared_type;
    258   }
    259 
    260   return true;
    261 }
    262 
    263 bool CheckIPCVisitor::CheckTemplateArgument(const TemplateArgument& arg,
    264                                             CheckDetails* details) const {
    265   return arg.getKind() != TemplateArgument::Type ||
    266       CheckType(arg.getAsType(), details);
    267 }
    268 
    269 void CheckIPCVisitor::ReportCheckError(const CheckDetails& details,
    270                                        SourceLocation loc,
    271                                        unsigned error) {
    272   DiagnosticsEngine& diagnostics = compiler_.getDiagnostics();
    273 
    274   std::string entry_type = details.entry_type.getAsString();
    275   std::string exit_type = details.exit_type.getAsString();
    276 
    277   std::string via;
    278   if (entry_type != exit_type) {
    279     via = " via '" + entry_type + "'";
    280   }
    281   diagnostics.Report(loc, error) << exit_type << via;
    282 
    283   for (const TypedefType* tdef: details.typedefs) {
    284     diagnostics.Report(tdef->getDecl()->getLocation(), note_see_here_);
    285   }
    286 }
    287 
    288 }  // namespace chrome_checker
    289