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