Avi Drissman | dfd88085 | 2022-09-15 20:11:09 | [diff] [blame] | 1 | // Copyright 2016 The Chromium Authors |
dskiba | 2bc8946 | 2016-04-06 15:51:06 | [diff] [blame] | 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[] = |
Peter Boström | bcda49b | 2022-11-22 20:02:29 | [diff] [blame] | 14 | "[chromium-ipc] IPC::WriteParam() is called on blocklisted type '%0'%1."; |
dskiba | 2bc8946 | 2016-04-06 15:51:06 | [diff] [blame] | 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 | |
Peter Boström | bcda49b | 2022-11-22 20:02:29 | [diff] [blame] | 39 | blocklisted_typedefs_ = llvm::StringSet<>({ |
dskiba | 2bc8946 | 2016-04-06 15:51:06 | [diff] [blame] | 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; |
Nico Weber | 69c5268 | 2022-10-25 16:03:09 | [diff] [blame] | 141 | for (const TemplateArgument& arg : spec->template_arguments()) { |
dskiba | 2bc8946 | 2016-04-06 15:51:06 | [diff] [blame] | 142 | CheckDetails details; |
| 143 | if (CheckTemplateArgument(arg, &details)) { |
| 144 | continue; |
| 145 | } |
| 146 | |
| 147 | valid = false; |
| 148 | |
| 149 | auto* parent_decl = GetParentDecl<Decl>(); |
| 150 | ReportCheckError( |
Reid Kleckner | b4bebe1a | 2018-09-06 21:33:56 | [diff] [blame] | 151 | details, parent_decl ? parent_decl->getBeginLoc() : SourceLocation(), |
dskiba | 2bc8946 | 2016-04-06 15:51:06 | [diff] [blame] | 152 | error_tuple_bad_type_); |
| 153 | } |
| 154 | |
| 155 | return valid; |
| 156 | } |
| 157 | |
| 158 | template <typename T> |
| 159 | const T* CheckIPCVisitor::GetParentDecl() const { |
| 160 | for (auto i = decl_stack_.rbegin(); i != decl_stack_.rend(); ++i) { |
| 161 | if (auto* parent = dyn_cast_or_null<T>(*i)) { |
| 162 | return parent; |
| 163 | } |
| 164 | } |
| 165 | return nullptr; |
| 166 | } |
| 167 | |
| 168 | |
| 169 | bool CheckIPCVisitor::IsBlacklistedType(QualType type) const { |
| 170 | return context_->hasSameUnqualifiedType(type, context_->LongTy) || |
| 171 | context_->hasSameUnqualifiedType(type, context_->UnsignedLongTy); |
| 172 | } |
| 173 | |
| 174 | bool CheckIPCVisitor::IsBlacklistedTypedef(const TypedefNameDecl* tdef) const { |
Peter Boström | bcda49b | 2022-11-22 20:02:29 | [diff] [blame] | 175 | return blocklisted_typedefs_.find(tdef->getName()) != |
| 176 | blocklisted_typedefs_.end(); |
dskiba | 2bc8946 | 2016-04-06 15:51:06 | [diff] [blame] | 177 | } |
| 178 | |
Peter Boström | bcda49b | 2022-11-22 20:02:29 | [diff] [blame] | 179 | // Checks that integer type is allowed (not blocklisted). |
dskiba | 2bc8946 | 2016-04-06 15:51:06 | [diff] [blame] | 180 | bool CheckIPCVisitor::CheckIntegerType(QualType type, |
| 181 | CheckDetails* details) const { |
| 182 | bool seen_typedef = false; |
| 183 | while (true) { |
| 184 | details->exit_type = type; |
| 185 | |
| 186 | if (auto* tdef = dyn_cast<TypedefType>(type)) { |
| 187 | if (IsBlacklistedTypedef(tdef->getDecl())) { |
| 188 | return false; |
| 189 | } |
| 190 | details->typedefs.push_back(tdef); |
| 191 | seen_typedef = true; |
| 192 | } |
| 193 | |
| 194 | QualType desugared_type = |
| 195 | type->getLocallyUnqualifiedSingleStepDesugaredType(); |
| 196 | if (desugared_type == type) { |
| 197 | break; |
| 198 | } |
| 199 | |
| 200 | type = desugared_type; |
| 201 | } |
| 202 | |
| 203 | return seen_typedef || !IsBlacklistedType(type); |
| 204 | } |
| 205 | |
Peter Boström | bcda49b | 2022-11-22 20:02:29 | [diff] [blame] | 206 | // Checks that |type| is allowed (not blocklisted), recursively visiting |
dskiba | 2bc8946 | 2016-04-06 15:51:06 | [diff] [blame] | 207 | // template specializations. |
| 208 | bool CheckIPCVisitor::CheckType(QualType type, CheckDetails* details) const { |
| 209 | if (type->isReferenceType()) { |
| 210 | type = type->getPointeeType(); |
| 211 | } |
| 212 | type = type.getLocalUnqualifiedType(); |
| 213 | |
| 214 | if (details->entry_type.isNull()) { |
| 215 | details->entry_type = type; |
| 216 | } |
| 217 | |
| 218 | if (type->isIntegerType()) { |
| 219 | return CheckIntegerType(type, details); |
| 220 | } |
| 221 | |
| 222 | while (true) { |
| 223 | if (auto* spec = dyn_cast<TemplateSpecializationType>(type)) { |
Nico Weber | 69c5268 | 2022-10-25 16:03:09 | [diff] [blame] | 224 | for (const TemplateArgument& arg : spec->template_arguments()) { |
dskiba | 2bc8946 | 2016-04-06 15:51:06 | [diff] [blame] | 225 | if (!CheckTemplateArgument(arg, details)) { |
| 226 | return false; |
| 227 | } |
| 228 | } |
| 229 | return true; |
| 230 | } |
| 231 | |
| 232 | if (auto* record = dyn_cast<RecordType>(type)) { |
| 233 | if (auto* spec = dyn_cast<ClassTemplateSpecializationDecl>( |
Arthur Eubanks | 3ef1b38 | 2025-08-13 03:28:24 | [diff] [blame] | 234 | record->getOriginalDecl())) { |
dskiba | 2bc8946 | 2016-04-06 15:51:06 | [diff] [blame] | 235 | const TemplateArgumentList& args = spec->getTemplateArgs(); |
| 236 | for (unsigned i = 0; i != args.size(); ++i) { |
| 237 | if (!CheckTemplateArgument(args[i], details)) { |
| 238 | return false; |
| 239 | } |
| 240 | } |
| 241 | } |
| 242 | return true; |
| 243 | } |
| 244 | |
| 245 | if (auto* tdef = dyn_cast<TypedefType>(type)) { |
| 246 | details->typedefs.push_back(tdef); |
| 247 | } |
| 248 | |
| 249 | QualType desugared_type = |
| 250 | type->getLocallyUnqualifiedSingleStepDesugaredType(); |
| 251 | if (desugared_type == type) { |
| 252 | break; |
| 253 | } |
| 254 | |
| 255 | type = desugared_type; |
| 256 | } |
| 257 | |
| 258 | return true; |
| 259 | } |
| 260 | |
| 261 | bool CheckIPCVisitor::CheckTemplateArgument(const TemplateArgument& arg, |
| 262 | CheckDetails* details) const { |
| 263 | return arg.getKind() != TemplateArgument::Type || |
| 264 | CheckType(arg.getAsType(), details); |
| 265 | } |
| 266 | |
| 267 | void CheckIPCVisitor::ReportCheckError(const CheckDetails& details, |
| 268 | SourceLocation loc, |
| 269 | unsigned error) { |
| 270 | DiagnosticsEngine& diagnostics = compiler_.getDiagnostics(); |
| 271 | |
| 272 | std::string entry_type = details.entry_type.getAsString(); |
| 273 | std::string exit_type = details.exit_type.getAsString(); |
| 274 | |
| 275 | std::string via; |
| 276 | if (entry_type != exit_type) { |
| 277 | via = " via '" + entry_type + "'"; |
| 278 | } |
| 279 | diagnostics.Report(loc, error) << exit_type << via; |
| 280 | |
| 281 | for (const TypedefType* tdef: details.typedefs) { |
| 282 | diagnostics.Report(tdef->getDecl()->getLocation(), note_see_here_); |
| 283 | } |
| 284 | } |
| 285 | |
| 286 | } // namespace chrome_checker |