OLD | NEW |
(Empty) | |
| 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 |
OLD | NEW |