Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 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 <memory> | |
| 6 #include <stdio.h> | |
| 7 | |
| 8 #include "clang/ASTMatchers/ASTMatchFinder.h" | |
| 9 #include "clang/ASTMatchers/ASTMatchers.h" | |
| 10 #include "clang/Basic/SourceManager.h" | |
| 11 #include "clang/Frontend/FrontendActions.h" | |
| 12 #include "clang/Lex/Lexer.h" | |
| 13 #include "clang/Tooling/CommonOptionsParser.h" | |
| 14 #include "clang/Tooling/Refactoring.h" | |
| 15 #include "clang/Tooling/Tooling.h" | |
| 16 #include "llvm/Support/CommandLine.h" | |
| 17 //--> TODO: #include "../../traffic_annotation/traffic_annotation.pb.h" | |
|
battre
2016/10/26 14:29:18
remove?
Ramin Halavati
2016/10/27 09:40:08
I think if we want to add protobuf to clang tool i
| |
| 18 | |
| 19 using namespace clang::ast_matchers; | |
| 20 using clang::tooling::CommonOptionsParser; | |
| 21 | |
| 22 namespace { | |
| 23 | |
| 24 // Structure to collect instances. | |
|
battre
2016/10/26 14:29:19
instances of what? Can you add more comments to th
Ramin Halavati
2016/10/27 09:40:08
Done.
| |
| 25 typedef struct Collector { | |
|
battre
2016/10/26 14:29:18
I think that in C++ we don't need to "typedef stru
Ramin Halavati
2016/10/27 09:40:10
Done.
| |
| 26 typedef struct Instance { | |
| 27 Instance() { | |
|
battre
2016/10/26 14:29:18
Can you use an initializer list?
Instance() : is_
Ramin Halavati
2016/10/27 09:40:08
Done.
| |
| 28 is_direct_call = false; | |
| 29 transitive_parameter = false; | |
| 30 variable_reference = NULL; | |
|
battre
2016/10/26 14:29:19
nullptr
Ramin Halavati
2016/10/27 09:40:09
Done.
| |
| 31 } | |
| 32 | |
| 33 // Information about where this annotation or call has happened. | |
| 34 struct Location { | |
| 35 std::string file_name; | |
| 36 int line_number; | |
|
battre
2016/10/26 14:29:19
line number is not default initialized.
Ramin Halavati
2016/10/27 09:40:09
Done.
| |
| 37 // Name of the function including this instance. | |
| 38 std::string function_name; | |
| 39 // Name of the variable that contains annotation or the function called | |
| 40 // with annotation. | |
| 41 std::string object_name; | |
| 42 }; | |
| 43 | |
| 44 // Annotation content | |
|
battre
2016/10/26 14:29:18
Comments should end with period. (please also chec
Ramin Halavati
2016/10/27 09:40:09
Done.
| |
| 45 struct Annotation { | |
| 46 std::string unique_id; | |
| 47 std::string text; | |
| 48 }; | |
| 49 | |
| 50 Location location; | |
| 51 Annotation annotation; | |
| 52 | |
| 53 // Possible error (empty if no error) | |
| 54 std::string error; | |
| 55 // A reference to the variable containing annotation. Null if not available. | |
| 56 const clang::NamedDecl* variable_reference; | |
| 57 // Flag stating that parameter is directly passed to annotate function here | |
| 58 // or it's through a variable. | |
| 59 bool is_direct_call; | |
| 60 // Flag stating that a variable is a parameter received by upper level | |
| 61 // function. | |
| 62 bool transitive_parameter; | |
| 63 } Instance; | |
| 64 | |
| 65 std::vector<Instance> variable_definitions, calls; | |
|
battre
2016/10/26 14:29:19
one variable definition per line.
Ramin Halavati
2016/10/27 09:40:09
Done.
| |
| 66 } Collector; | |
| 67 | |
| 68 // Returns the function that includes the given clang::Decl. | |
| 69 std::string GetCoveringFunction(const clang::Decl* token, | |
| 70 const MatchFinder::MatchResult& result); | |
| 71 | |
| 72 // Checks if a token matches a name, with or without net:: namespace | |
| 73 bool net_match(const std::string& token, const std::string& name) { | |
| 74 return token == name || token == (std::string("net::") + name); | |
| 75 } | |
| 76 | |
| 77 // Returns the text of a given statement or subclass. | |
| 78 std::string GetStmtText(const clang::Stmt* token, | |
| 79 const clang::SourceManager& source_manager) { | |
| 80 clang::LangOptions lopt; | |
| 81 // Get text range | |
| 82 clang::SourceLocation start = token->getLocStart(); | |
| 83 clang::SourceLocation end = token->getLocEnd(); | |
| 84 | |
| 85 // If it's a macro, go to definition. | |
| 86 if (start.isMacroID()) | |
| 87 start = source_manager.getSpellingLoc(start); | |
| 88 if (end.isMacroID()) | |
| 89 end = source_manager.getSpellingLoc(end); | |
| 90 | |
| 91 // Get the real end of the token. | |
| 92 end = clang::Lexer::getLocForEndOfToken(end, 0, source_manager, lopt); | |
| 93 | |
| 94 // Extract text. | |
| 95 std::string output = std::string(source_manager.getCharacterData(start), | |
| 96 source_manager.getCharacterData(end) - | |
| 97 source_manager.getCharacterData(start)); | |
|
battre
2016/10/26 14:29:19
can you also write
std::string output(source_manag
Ramin Halavati
2016/10/27 09:40:09
Done.
| |
| 98 | |
| 99 // Raw string? | |
| 100 if (output == "R") | |
|
battre
2016/10/26 14:29:19
{} around multiline blocks. Same in the next line.
Ramin Halavati
2016/10/27 09:40:09
Done.
| |
| 101 if (auto* c1 = clang::dyn_cast<clang::ImplicitCastExpr>(token)) | |
| 102 if (const clang::StringLiteral* c2 = | |
| 103 clang::dyn_cast<clang::StringLiteral>( | |
| 104 c1->getSubExprAsWritten())) { | |
| 105 output = c2->getString(); | |
| 106 } | |
| 107 | |
| 108 return output; | |
| 109 } | |
| 110 | |
| 111 // Returns annotation text of a call to "DefineNetworkTrafficAnnotation" | |
| 112 // function. | |
| 113 void GetAnnotationText(const clang::CallExpr* call_expr, | |
| 114 const clang::SourceManager& source_manager, | |
| 115 Collector::Instance* instance) { | |
| 116 if (net_match(GetStmtText(call_expr->getCallee(), source_manager), | |
| 117 "DefineNetworkTrafficAnnotation") && | |
| 118 call_expr->getNumArgs() == 2) { | |
| 119 instance->annotation.unique_id = | |
| 120 GetStmtText(call_expr->getArgs()[0], source_manager); | |
| 121 instance->annotation.text = | |
| 122 GetStmtText(call_expr->getArgs()[1], source_manager); | |
| 123 instance->error = ""; | |
| 124 } else { | |
| 125 instance->annotation.unique_id = ""; | |
| 126 instance->annotation.text = ""; | |
| 127 instance->error = "Unexpected function."; | |
| 128 } | |
| 129 } | |
| 130 | |
| 131 // Returns the function that includes the given clang::Stmt. | |
| 132 std::string GetCoveringFunction(const clang::Stmt* token, | |
| 133 const MatchFinder::MatchResult& result) { | |
| 134 auto parents = result.Context->getParents(*token); | |
| 135 if (parents.size() == 1) { | |
| 136 if (const clang::Stmt* s = parents[0].get<clang::Stmt>()) | |
| 137 return GetCoveringFunction(s, result); | |
| 138 else if (const clang::Decl* d = parents[0].get<clang::Decl>()) | |
| 139 return GetCoveringFunction(d, result); | |
| 140 } | |
| 141 return "Unknown"; | |
| 142 } | |
| 143 | |
| 144 // Returns the function that includes the given clang::Decl. | |
| 145 std::string GetCoveringFunction(const clang::Decl* token, | |
| 146 const MatchFinder::MatchResult& result) { | |
| 147 if (auto f = clang::dyn_cast<clang::FunctionDecl>(token)) | |
| 148 return f->getQualifiedNameAsString(); | |
| 149 | |
| 150 auto parents = result.Context->getParents(*token); | |
| 151 if (parents.size() == 1) { | |
|
battre
2016/10/26 14:29:18
Add a comment what having a single parent means?
Ramin Halavati
2016/10/27 09:40:09
I did not find a conclusive description on this an
| |
| 152 if (const clang::Stmt* s = parents[0].get<clang::Stmt>()) | |
| 153 return GetCoveringFunction(s, result); | |
| 154 else if (const clang::Decl* d = parents[0].get<clang::Decl>()) | |
| 155 return GetCoveringFunction(d, result); | |
| 156 } | |
| 157 return "Unknown"; | |
| 158 } | |
| 159 | |
| 160 // Returns the file name and line number of the given token. | |
| 161 template <class T> | |
| 162 void GetOccuranceLocation(const T* token, | |
|
battre
2016/10/26 14:29:19
typo: occurr*e*nce
What's the difference between
Ramin Halavati
2016/10/27 09:40:09
Renamed it to GetLocation as it can be called gene
| |
| 163 const MatchFinder::MatchResult& result, | |
| 164 Collector::Instance::Location* location) { | |
| 165 clang::SourceLocation source_location = token->getLocStart(); | |
| 166 location->file_name = result.SourceManager->getFilename(source_location); | |
| 167 location->line_number = | |
| 168 result.SourceManager->getSpellingLineNumber(source_location); | |
| 169 } | |
| 170 | |
| 171 class TheCallback : public MatchFinder::MatchCallback { | |
|
battre
2016/10/26 14:29:19
Description of the responsibility of this class?
Ramin Halavati
2016/10/27 09:40:08
Done.
| |
| 172 public: | |
| 173 explicit TheCallback(Collector* collector) : collector_(collector) {} | |
|
battre
2016/10/26 14:29:18
~TheCallback() override = default;
Ramin Halavati
2016/10/27 09:40:09
Done.
| |
| 174 | |
| 175 virtual void run(const MatchFinder::MatchResult& result) override { | |
| 176 if (const clang::VarDecl* var_decl = | |
| 177 result.Nodes.getNodeAs<clang::VarDecl>("annotation_variable")) | |
|
battre
2016/10/26 14:29:19
also {} if the is statement spans more than one li
Ramin Halavati
2016/10/27 09:40:09
Done.
| |
| 178 AddVariable(var_decl, result); | |
| 179 else if (const clang::CallExpr* call_expr = | |
| 180 result.Nodes.getNodeAs<clang::CallExpr>("user_function")) | |
| 181 AddFunction(call_expr, result); | |
| 182 } | |
| 183 | |
| 184 // Stores an annotation variable defintion in the Collector. | |
| 185 void AddVariable(const clang::VarDecl* var_decl, | |
| 186 const MatchFinder::MatchResult& result) { | |
| 187 Collector::Instance instance; | |
| 188 | |
| 189 GetOccuranceLocation(var_decl, result, &instance.location); | |
| 190 instance.location.object_name = var_decl->getQualifiedNameAsString(); | |
| 191 instance.variable_reference = clang::dyn_cast<clang::NamedDecl>(var_decl); | |
| 192 | |
| 193 // Get annotation text. | |
| 194 // If it doesn't have initialization, but it's a parameter, store it. | |
| 195 if (!var_decl->hasInit() && var_decl->isLocalVarDeclOrParm() && | |
| 196 !var_decl->isLocalVarDecl()) { | |
| 197 instance.transitive_parameter = true; | |
| 198 } else if (auto* init_expr = var_decl->getInit()) { | |
| 199 if (auto* call_expr = clang::dyn_cast<clang::CallExpr>(init_expr)) | |
| 200 GetAnnotationText(call_expr, *result.SourceManager, &instance); | |
| 201 } | |
| 202 // If nothing is set, issue an error. | |
| 203 if (!instance.transitive_parameter && | |
| 204 instance.annotation.unique_id.empty() && instance.error.empty()) | |
|
battre
2016/10/26 14:29:19
{}
Ramin Halavati
2016/10/27 09:40:09
Done.
| |
| 205 instance.error = "Could not resolve variable initialization."; | |
| 206 | |
| 207 collector_->variable_definitions.push_back(instance); | |
| 208 } | |
| 209 | |
| 210 // Stores a function call that should be monitored. | |
| 211 void AddFunction(const clang::CallExpr* call_expr, | |
| 212 const MatchFinder::MatchResult& result) { | |
| 213 Collector::Instance instance; | |
| 214 | |
| 215 GetOccuranceLocation(call_expr, result, &instance.location); | |
| 216 instance.location.function_name = | |
| 217 GetCoveringFunction(clang::dyn_cast<clang::Stmt>(call_expr), result); | |
| 218 instance.location.object_name = | |
| 219 call_expr->getDirectCallee()->getQualifiedNameAsString(); | |
| 220 | |
| 221 // Get annotation text. | |
| 222 const clang::FunctionDecl* function_decl = call_expr->getDirectCallee(); | |
| 223 unsigned params_count = function_decl->getNumParams(); | |
| 224 unsigned args_count = call_expr->getNumArgs(); | |
| 225 | |
| 226 for (unsigned i = 0; i < params_count; i++) { | |
| 227 if (net_match(clang::QualType::getAsString( | |
| 228 function_decl->getParamDecl(i)->getType().split()), | |
| 229 "NetworkTrafficAnnotationTag")) { | |
| 230 if (i >= args_count) { | |
| 231 instance.error = "Function missing annotation argument."; | |
| 232 } else { | |
| 233 // Get the argument. | |
| 234 const clang::Expr* arg = call_expr->getArgs()[i]; | |
| 235 | |
| 236 // Is it a call to annotate function? | |
| 237 if (auto* inner_call_expr = clang::dyn_cast<clang::CallExpr>(arg)) { | |
| 238 instance.is_direct_call = true; | |
| 239 GetAnnotationText(inner_call_expr, *result.SourceManager, | |
| 240 &instance); | |
| 241 instance.error = ""; | |
| 242 } else { | |
| 243 // Then it's a variable. | |
| 244 instance.is_direct_call = false; | |
| 245 if (auto* pure_arg = | |
| 246 clang::dyn_cast<clang::DeclRefExpr>(arg->IgnoreCasts())) { | |
| 247 instance.variable_reference = pure_arg->getFoundDecl(); | |
| 248 instance.error = ""; | |
| 249 } else { | |
| 250 instance.error = "Unknwon parameter type."; | |
| 251 } | |
| 252 } | |
| 253 } | |
| 254 collector_->calls.push_back(instance); | |
| 255 } | |
| 256 } | |
| 257 } | |
| 258 | |
| 259 private: | |
| 260 // TODO store pointer | |
|
battre
2016/10/26 14:29:19
Delete the todo?
Ramin Halavati
2016/10/27 09:40:09
Done.
| |
| 261 Collector* collector_; | |
| 262 }; | |
| 263 | |
| 264 // NRA class keeps the call back function and sets the matchers. | |
|
battre
2016/10/26 14:29:18
Please don't use abbreviations.
Ramin Halavati
2016/10/27 09:40:08
Done. The class is totally removed and replaced by
| |
| 265 class NetworkRequestAuditor { | |
|
battre
2016/10/26 14:29:19
Rename this to NetworkAnnotationTagExtractor? Then
Ramin Halavati
2016/10/27 09:40:09
The class is totally removed and replaced by a sin
| |
| 266 public: | |
| 267 explicit NetworkRequestAuditor(Collector* collector) | |
| 268 : the_callback_(collector) {} | |
| 269 | |
| 270 void SetupMatchers(MatchFinder* match_finder) { | |
| 271 // Find variables defined as Annotation | |
|
battre
2016/10/26 14:29:19
.
What does "Find variables defined as Annotation
Ramin Halavati
2016/10/27 09:40:08
Comments changed.
| |
| 272 match_finder->addMatcher( | |
| 273 varDecl(anyOf(hasType(asString("NetworkTrafficAnnotationTag")), | |
| 274 hasType(asString("net::NetworkTrafficAnnotationTag")))) | |
| 275 .bind("annotation_variable"), | |
| 276 &the_callback_); | |
| 277 | |
| 278 // Find instances of functions that have a parameter of type Annotate. | |
| 279 match_finder->addMatcher( | |
| 280 callExpr(hasDeclaration(functionDecl(hasAnyParameter(anyOf( | |
| 281 hasType(asString("NetworkTrafficAnnotationTag")), | |
| 282 hasType(asString("net::NetworkTrafficAnnotationTag"))))))) | |
| 283 .bind("user_function"), | |
| 284 &the_callback_); | |
| 285 } | |
| 286 | |
| 287 private: | |
| 288 TheCallback the_callback_; | |
| 289 }; | |
| 290 | |
| 291 } // namespace | |
| 292 | |
| 293 int main(int argc, const char* argv[]) { | |
| 294 llvm::cl::OptionCategory category("Network Request Audit Extractor Tool"); | |
| 295 CommonOptionsParser options(argc, argv, category); | |
| 296 clang::tooling::ClangTool tool(options.getCompilations(), | |
| 297 options.getSourcePathList()); | |
| 298 | |
| 299 Collector collector; | |
| 300 NetworkRequestAuditor auditor(&collector); | |
| 301 MatchFinder match_finder; | |
| 302 auditor.SetupMatchers(&match_finder); | |
| 303 | |
| 304 // Find output folder | |
| 305 const std::string kOutputSpecifier("output_dir="); | |
| 306 std::string output_dir; | |
| 307 for (int i = 0; i < argc; i++) { | |
| 308 if (!strncmp(argv[i], kOutputSpecifier.c_str(), | |
| 309 kOutputSpecifier.length())) { | |
|
battre
2016/10/26 14:29:19
if (argv[i] == kOutputSpecifier) does the same thi
Ramin Halavati
2016/10/27 09:40:08
argv[i] parameter includes the specifier and the p
| |
| 310 output_dir = argv[i] + kOutputSpecifier.length(); | |
| 311 break; | |
| 312 } | |
| 313 } | |
| 314 | |
| 315 if (output_dir == "") { | |
| 316 llvm::errs() << "Temporary files directory is not specified."; | |
| 317 return 1; | |
| 318 } | |
| 319 | |
| 320 std::unique_ptr<clang::tooling::FrontendActionFactory> frontend_factory = | |
| 321 clang::tooling::newFrontendActionFactory(&match_finder); | |
| 322 int result = tool.run(frontend_factory.get()); | |
| 323 | |
| 324 if (result != 0) | |
| 325 return result; | |
| 326 | |
| 327 llvm::outs() << "==== BEGIN EDITS ====\n"; | |
| 328 llvm::outs() << "==== END EDITS ====\n"; | |
| 329 | |
| 330 // For each call, if the parameter is not generated by a direct call to | |
| 331 // "DefineNetworkTrafficAnnotation", find the variable that holds the value. | |
| 332 for (auto& c : collector.calls) { | |
|
battre
2016/10/26 14:29:19
const auto& c?
Ramin Halavati
2016/10/27 09:40:09
the annotation field of iterators may be changed 5
| |
| 333 if (!c.is_direct_call) { | |
| 334 // Find the variable. | |
| 335 for (auto& v : collector.variable_definitions) | |
|
battre
2016/10/26 14:29:19
{}
const auto& ?
Ramin Halavati
2016/10/27 09:40:09
Done.
| |
| 336 if (v.variable_reference == c.variable_reference) { | |
| 337 c.annotation = v.annotation; | |
| 338 c.transitive_parameter = v.transitive_parameter; | |
| 339 c.error = c.error + (c.error.length() ? "\n+" : "") + v.error; | |
| 340 break; | |
| 341 } | |
| 342 if (!c.annotation.unique_id.length()) | |
| 343 c.error = "Variable not found."; | |
| 344 } | |
| 345 | |
| 346 // If the function just receives the variable and passes it to another | |
| 347 // function, ignore it, otherwise write it to file. | |
| 348 if (!c.transitive_parameter) { | |
| 349 std::string s = c.location.file_name; | |
| 350 std::replace(s.begin(), s.end(), '/', '_'); | |
| 351 std::replace(s.begin(), s.end(), '.', '_'); | |
| 352 char file_name[1000]; | |
| 353 | |
| 354 snprintf(file_name, sizeof(file_name), "%s/%s(%i).txt", | |
| 355 output_dir.c_str(), s.c_str(), c.location.line_number); | |
|
battre
2016/10/26 14:29:18
std::string filename = output_dir + "/" + s + "("
Ramin Halavati
2016/10/27 09:40:09
Done.
| |
| 356 | |
| 357 FILE* file = fopen(file_name, "wt"); | |
|
battre
2016/10/26 14:29:18
how about staying C++ here and using std::ofstream
Ramin Halavati
2016/10/27 09:40:08
Done.
| |
| 358 if (file) { | |
| 359 fprintf(file, "%s\n", c.location.file_name.c_str()); | |
| 360 fprintf(file, "%s\n", c.location.function_name.c_str()); | |
| 361 fprintf(file, "%i\n", c.location.line_number); | |
| 362 fprintf(file, "%s\n", c.location.object_name.c_str()); | |
| 363 fprintf(file, "%s\n", c.error.c_str()); | |
| 364 fprintf(file, "%s\n", c.annotation.unique_id.c_str()); | |
| 365 fprintf(file, "%s", c.annotation.text.c_str()); | |
| 366 fclose(file); | |
| 367 } else { | |
| 368 llvm::errs() << "Could not write to file: " << file_name << " because " | |
| 369 << strerror(errno) << "\n"; | |
| 370 return 1; | |
| 371 } | |
| 372 } | |
| 373 } | |
| 374 | |
| 375 return 0; | |
| 376 } | |
| OLD | NEW |