| // 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" |
| |
| usingnamespace clang; |
| |
| namespace chrome_checker{ |
| |
| namespace{ |
| |
| constchar kWriteParamBadType[]= |
| "[chromium-ipc] IPC::WriteParam() is called on blocklisted type '%0'%1."; |
| |
| constchar kTupleBadType[]= |
| "[chromium-ipc] IPC tuple references banned type '%0'%1."; |
| |
| constchar kWriteParamBadSignature[]= |
| "[chromium-ipc] IPC::WriteParam() is expected to have two arguments."; |
| |
| constchar 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" |
| }); |
| } |
| |
| voidCheckIPCVisitor::BeginDecl(Decl* decl){ |
| decl_stack_.push_back(decl); |
| } |
| |
| voidCheckIPCVisitor::EndDecl(){ |
| decl_stack_.pop_back(); |
| } |
| |
| voidCheckIPCVisitor::VisitTemplateSpecializationType( |
| TemplateSpecializationType* spec){ |
| ValidateCheckedTuple(spec); |
| } |
| |
| voidCheckIPCVisitor::VisitCallExpr(CallExpr* call_expr){ |
| ValidateWriteParam(call_expr); |
| } |
| |
| boolCheckIPCVisitor::ValidateWriteParam(constCallExpr* call_expr){ |
| constFunctionDecl* callee_decl= call_expr->getDirectCallee(); |
| if(!callee_decl|| |
| callee_decl->getQualifiedNameAsString()!="IPC::WriteParam"){ |
| returntrue; |
| } |
| |
| returnValidateWriteParamSignature(call_expr)&& |
| ValidateWriteParamArgument(call_expr->getArg(1)); |
| } |
| |
| // Checks that IPC::WriteParam() has expected signature. |
| boolCheckIPCVisitor::ValidateWriteParamSignature( |
| constCallExpr* call_expr){ |
| if(call_expr->getNumArgs()!=2){ |
| compiler_.getDiagnostics().Report( |
| call_expr->getExprLoc(), error_write_param_bad_signature_); |
| returnfalse; |
| } |
| returntrue; |
| } |
| |
| // Checks that IPC::WriteParam() argument type is allowed. |
| // See CheckType() for specifics. |
| boolCheckIPCVisitor::ValidateWriteParamArgument(constExpr* 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). |
| returntrue; |
| } |
| } |
| |
| 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)){ |
| returntrue; |
| } |
| |
| ReportCheckError(details, |
| arg_expr->getExprLoc(), |
| error_write_param_bad_type_); |
| |
| returnfalse; |
| } |
| |
| // Checks that IPC::CheckedTuple<> is specialized with allowed types. |
| // See CheckType() above for specifics. |
| boolCheckIPCVisitor::ValidateCheckedTuple( |
| constTemplateSpecializationType* spec){ |
| TemplateDecl* decl= spec->getTemplateName().getAsTemplateDecl(); |
| if(!decl|| decl->getQualifiedNameAsString()!="IPC::CheckedTuple"){ |
| returntrue; |
| } |
| |
| bool valid=true; |
| for(constTemplateArgument& 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; |
| } |
| } |
| returnnullptr; |
| } |
| |
| |
| boolCheckIPCVisitor::IsBlacklistedType(QualType type)const{ |
| return context_->hasSameUnqualifiedType(type, context_->LongTy)|| |
| context_->hasSameUnqualifiedType(type, context_->UnsignedLongTy); |
| } |
| |
| boolCheckIPCVisitor::IsBlacklistedTypedef(constTypedefNameDecl* tdef)const{ |
| return blocklisted_typedefs_.find(tdef->getName())!= |
| blocklisted_typedefs_.end(); |
| } |
| |
| // Checks that integer type is allowed (not blocklisted). |
| boolCheckIPCVisitor::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())){ |
| returnfalse; |
| } |
| 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. |
| boolCheckIPCVisitor::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()){ |
| returnCheckIntegerType(type, details); |
| } |
| |
| while(true){ |
| if(auto* spec= dyn_cast<TemplateSpecializationType>(type)){ |
| for(constTemplateArgument& arg: spec->template_arguments()){ |
| if(!CheckTemplateArgument(arg, details)){ |
| returnfalse; |
| } |
| } |
| returntrue; |
| } |
| |
| if(auto* record= dyn_cast<RecordType>(type)){ |
| if(auto* spec= dyn_cast<ClassTemplateSpecializationDecl>( |
| record->getDecl())){ |
| constTemplateArgumentList& args= spec->getTemplateArgs(); |
| for(unsigned i=0; i!= args.size();++i){ |
| if(!CheckTemplateArgument(args[i], details)){ |
| returnfalse; |
| } |
| } |
| } |
| returntrue; |
| } |
| |
| 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; |
| } |
| |
| returntrue; |
| } |
| |
| boolCheckIPCVisitor::CheckTemplateArgument(constTemplateArgument& arg, |
| CheckDetails* details)const{ |
| return arg.getKind()!=TemplateArgument::Type|| |
| CheckType(arg.getAsType(), details); |
| } |
| |
| voidCheckIPCVisitor::ReportCheckError(constCheckDetails& 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(constTypedefType* tdef: details.typedefs){ |
| diagnostics.Report(tdef->getDecl()->getLocation(), note_see_here_); |
| } |
| } |
| |
| }// namespace chrome_checker |