build/clang-plugin/clang-plugin.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4 #include "clang/AST/ASTConsumer.h"
michael@0 5 #include "clang/AST/ASTContext.h"
michael@0 6 #include "clang/AST/RecursiveASTVisitor.h"
michael@0 7 #include "clang/ASTMatchers/ASTMatchers.h"
michael@0 8 #include "clang/ASTMatchers/ASTMatchFinder.h"
michael@0 9 #include "clang/Basic/Version.h"
michael@0 10 #include "clang/Frontend/CompilerInstance.h"
michael@0 11 #include "clang/Frontend/FrontendPluginRegistry.h"
michael@0 12 #include "clang/Frontend/MultiplexConsumer.h"
michael@0 13 #include "clang/Sema/Sema.h"
michael@0 14 #include "llvm/ADT/DenseMap.h"
michael@0 15
michael@0 16 #define CLANG_VERSION_FULL (CLANG_VERSION_MAJOR * 100 + CLANG_VERSION_MINOR)
michael@0 17
michael@0 18 using namespace llvm;
michael@0 19 using namespace clang;
michael@0 20
michael@0 21 namespace {
michael@0 22
michael@0 23 using namespace clang::ast_matchers;
michael@0 24 class DiagnosticsMatcher {
michael@0 25 public:
michael@0 26 DiagnosticsMatcher();
michael@0 27
michael@0 28 ASTConsumer *makeASTConsumer() {
michael@0 29 return astMatcher.newASTConsumer();
michael@0 30 }
michael@0 31
michael@0 32 private:
michael@0 33 class StackClassChecker : public MatchFinder::MatchCallback {
michael@0 34 public:
michael@0 35 virtual void run(const MatchFinder::MatchResult &Result);
michael@0 36 void noteInferred(QualType T, DiagnosticsEngine &Diag);
michael@0 37 };
michael@0 38
michael@0 39 class NonHeapClassChecker : public MatchFinder::MatchCallback {
michael@0 40 public:
michael@0 41 virtual void run(const MatchFinder::MatchResult &Result);
michael@0 42 void noteInferred(QualType T, DiagnosticsEngine &Diag);
michael@0 43 };
michael@0 44
michael@0 45 StackClassChecker stackClassChecker;
michael@0 46 NonHeapClassChecker nonheapClassChecker;
michael@0 47 MatchFinder astMatcher;
michael@0 48 };
michael@0 49
michael@0 50 class MozChecker : public ASTConsumer, public RecursiveASTVisitor<MozChecker> {
michael@0 51 DiagnosticsEngine &Diag;
michael@0 52 const CompilerInstance &CI;
michael@0 53 DiagnosticsMatcher matcher;
michael@0 54 public:
michael@0 55 MozChecker(const CompilerInstance &CI) : Diag(CI.getDiagnostics()), CI(CI) {}
michael@0 56
michael@0 57 ASTConsumer *getOtherConsumer() {
michael@0 58 return matcher.makeASTConsumer();
michael@0 59 }
michael@0 60
michael@0 61 virtual void HandleTranslationUnit(ASTContext &ctx) {
michael@0 62 TraverseDecl(ctx.getTranslationUnitDecl());
michael@0 63 }
michael@0 64
michael@0 65 static bool hasCustomAnnotation(const Decl *d, const char *spelling) {
michael@0 66 AnnotateAttr *attr = d->getAttr<AnnotateAttr>();
michael@0 67 if (!attr)
michael@0 68 return false;
michael@0 69
michael@0 70 return attr->getAnnotation() == spelling;
michael@0 71 }
michael@0 72
michael@0 73 bool VisitCXXRecordDecl(CXXRecordDecl *d) {
michael@0 74 // We need definitions, not declarations
michael@0 75 if (!d->isThisDeclarationADefinition()) return true;
michael@0 76
michael@0 77 // Look through all of our immediate bases to find methods that need to be
michael@0 78 // overridden
michael@0 79 typedef std::vector<CXXMethodDecl *> OverridesVector;
michael@0 80 OverridesVector must_overrides;
michael@0 81 for (CXXRecordDecl::base_class_iterator base = d->bases_begin(),
michael@0 82 e = d->bases_end(); base != e; ++base) {
michael@0 83 // The base is either a class (CXXRecordDecl) or it's a templated class...
michael@0 84 CXXRecordDecl *parent = base->getType()
michael@0 85 .getDesugaredType(d->getASTContext())->getAsCXXRecordDecl();
michael@0 86 // The parent might not be resolved to a type yet. In this case, we can't
michael@0 87 // do any checking here. For complete correctness, we should visit
michael@0 88 // template instantiations, but this case is likely to be rare, so we will
michael@0 89 // ignore it until it becomes important.
michael@0 90 if (!parent) {
michael@0 91 continue;
michael@0 92 }
michael@0 93 parent = parent->getDefinition();
michael@0 94 for (CXXRecordDecl::method_iterator M = parent->method_begin();
michael@0 95 M != parent->method_end(); ++M) {
michael@0 96 if (hasCustomAnnotation(*M, "moz_must_override"))
michael@0 97 must_overrides.push_back(*M);
michael@0 98 }
michael@0 99 }
michael@0 100
michael@0 101 for (OverridesVector::iterator it = must_overrides.begin();
michael@0 102 it != must_overrides.end(); ++it) {
michael@0 103 bool overridden = false;
michael@0 104 for (CXXRecordDecl::method_iterator M = d->method_begin();
michael@0 105 !overridden && M != d->method_end(); ++M) {
michael@0 106 // The way that Clang checks if a method M overrides its parent method
michael@0 107 // is if the method has the same name but would not overload.
michael@0 108 if (M->getName() == (*it)->getName() &&
michael@0 109 !CI.getSema().IsOverload(*M, (*it), false))
michael@0 110 overridden = true;
michael@0 111 }
michael@0 112 if (!overridden) {
michael@0 113 unsigned overrideID = Diag.getDiagnosticIDs()->getCustomDiagID(
michael@0 114 DiagnosticIDs::Error, "%0 must override %1");
michael@0 115 unsigned overrideNote = Diag.getDiagnosticIDs()->getCustomDiagID(
michael@0 116 DiagnosticIDs::Note, "function to override is here");
michael@0 117 Diag.Report(d->getLocation(), overrideID) << d->getDeclName() <<
michael@0 118 (*it)->getDeclName();
michael@0 119 Diag.Report((*it)->getLocation(), overrideNote);
michael@0 120 }
michael@0 121 }
michael@0 122 return true;
michael@0 123 }
michael@0 124 };
michael@0 125
michael@0 126 /**
michael@0 127 * Where classes may be allocated. Regular classes can be allocated anywhere,
michael@0 128 * non-heap classes on the stack or as static variables, and stack classes only
michael@0 129 * on the stack. Note that stack classes subsumes non-heap classes.
michael@0 130 */
michael@0 131 enum ClassAllocationNature {
michael@0 132 RegularClass = 0,
michael@0 133 NonHeapClass = 1,
michael@0 134 StackClass = 2
michael@0 135 };
michael@0 136
michael@0 137 /// A cached data of whether classes are stack classes, non-heap classes, or
michael@0 138 /// neither.
michael@0 139 DenseMap<const CXXRecordDecl *,
michael@0 140 std::pair<const Decl *, ClassAllocationNature> > inferredAllocCauses;
michael@0 141
michael@0 142 ClassAllocationNature getClassAttrs(QualType T);
michael@0 143
michael@0 144 ClassAllocationNature getClassAttrs(CXXRecordDecl *D) {
michael@0 145 // Normalize so that D points to the definition if it exists. If it doesn't,
michael@0 146 // then we can't allocate it anyways.
michael@0 147 if (!D->hasDefinition())
michael@0 148 return RegularClass;
michael@0 149 D = D->getDefinition();
michael@0 150 // Base class: anyone with this annotation is obviously a stack class
michael@0 151 if (MozChecker::hasCustomAnnotation(D, "moz_stack_class"))
michael@0 152 return StackClass;
michael@0 153
michael@0 154 // See if we cached the result.
michael@0 155 DenseMap<const CXXRecordDecl *,
michael@0 156 std::pair<const Decl *, ClassAllocationNature> >::iterator it =
michael@0 157 inferredAllocCauses.find(D);
michael@0 158 if (it != inferredAllocCauses.end()) {
michael@0 159 return it->second.second;
michael@0 160 }
michael@0 161
michael@0 162 // Continue looking, we might be a stack class yet. Even if we're a nonheap
michael@0 163 // class, it might be possible that we've inferred to be a stack class.
michael@0 164 ClassAllocationNature type = RegularClass;
michael@0 165 if (MozChecker::hasCustomAnnotation(D, "moz_nonheap_class")) {
michael@0 166 type = NonHeapClass;
michael@0 167 }
michael@0 168 inferredAllocCauses.insert(std::make_pair(D,
michael@0 169 std::make_pair((const Decl *)0, type)));
michael@0 170
michael@0 171 // Look through all base cases to figure out if the parent is a stack class or
michael@0 172 // a non-heap class. Since we might later infer to also be a stack class, keep
michael@0 173 // going.
michael@0 174 for (CXXRecordDecl::base_class_iterator base = D->bases_begin(),
michael@0 175 e = D->bases_end(); base != e; ++base) {
michael@0 176 ClassAllocationNature super = getClassAttrs(base->getType());
michael@0 177 if (super == StackClass) {
michael@0 178 inferredAllocCauses[D] = std::make_pair(
michael@0 179 base->getType()->getAsCXXRecordDecl(), StackClass);
michael@0 180 return StackClass;
michael@0 181 } else if (super == NonHeapClass) {
michael@0 182 inferredAllocCauses[D] = std::make_pair(
michael@0 183 base->getType()->getAsCXXRecordDecl(), NonHeapClass);
michael@0 184 type = NonHeapClass;
michael@0 185 }
michael@0 186 }
michael@0 187
michael@0 188 // Maybe it has a member which is a stack class.
michael@0 189 for (RecordDecl::field_iterator field = D->field_begin(), e = D->field_end();
michael@0 190 field != e; ++field) {
michael@0 191 ClassAllocationNature fieldType = getClassAttrs(field->getType());
michael@0 192 if (fieldType == StackClass) {
michael@0 193 inferredAllocCauses[D] = std::make_pair(*field, StackClass);
michael@0 194 return StackClass;
michael@0 195 } else if (fieldType == NonHeapClass) {
michael@0 196 inferredAllocCauses[D] = std::make_pair(*field, NonHeapClass);
michael@0 197 type = NonHeapClass;
michael@0 198 }
michael@0 199 }
michael@0 200
michael@0 201 return type;
michael@0 202 }
michael@0 203
michael@0 204 ClassAllocationNature getClassAttrs(QualType T) {
michael@0 205 while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
michael@0 206 T = arrTy->getElementType();
michael@0 207 CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
michael@0 208 return clazz ? getClassAttrs(clazz) : RegularClass;
michael@0 209 }
michael@0 210
michael@0 211 }
michael@0 212
michael@0 213 namespace clang {
michael@0 214 namespace ast_matchers {
michael@0 215
michael@0 216 /// This matcher will match any class with the stack class assertion or an
michael@0 217 /// array of such classes.
michael@0 218 AST_MATCHER(QualType, stackClassAggregate) {
michael@0 219 return getClassAttrs(Node) == StackClass;
michael@0 220 }
michael@0 221
michael@0 222 /// This matcher will match any class with the stack class assertion or an
michael@0 223 /// array of such classes.
michael@0 224 AST_MATCHER(QualType, nonheapClassAggregate) {
michael@0 225 return getClassAttrs(Node) == NonHeapClass;
michael@0 226 }
michael@0 227
michael@0 228 /// This matcher will match any function declaration that is declared as a heap
michael@0 229 /// allocator.
michael@0 230 AST_MATCHER(FunctionDecl, heapAllocator) {
michael@0 231 return MozChecker::hasCustomAnnotation(&Node, "moz_heap_allocator");
michael@0 232 }
michael@0 233 }
michael@0 234 }
michael@0 235
michael@0 236 namespace {
michael@0 237
michael@0 238 bool isPlacementNew(const CXXNewExpr *expr) {
michael@0 239 // Regular new expressions aren't placement new
michael@0 240 if (expr->getNumPlacementArgs() == 0)
michael@0 241 return false;
michael@0 242 if (MozChecker::hasCustomAnnotation(expr->getOperatorNew(),
michael@0 243 "moz_heap_allocator"))
michael@0 244 return false;
michael@0 245 return true;
michael@0 246 }
michael@0 247
michael@0 248 DiagnosticsMatcher::DiagnosticsMatcher() {
michael@0 249 // Stack class assertion: non-local variables of a stack class are forbidden
michael@0 250 // (non-localness checked in the callback)
michael@0 251 astMatcher.addMatcher(varDecl(hasType(stackClassAggregate())).bind("node"),
michael@0 252 &stackClassChecker);
michael@0 253 // Stack class assertion: new stack class is forbidden (unless placement new)
michael@0 254 astMatcher.addMatcher(newExpr(hasType(pointerType(
michael@0 255 pointee(stackClassAggregate())
michael@0 256 ))).bind("node"), &stackClassChecker);
michael@0 257 // Non-heap class assertion: new non-heap class is forbidden (unless placement
michael@0 258 // new)
michael@0 259 astMatcher.addMatcher(newExpr(hasType(pointerType(
michael@0 260 pointee(nonheapClassAggregate())
michael@0 261 ))).bind("node"), &nonheapClassChecker);
michael@0 262
michael@0 263 // Any heap allocation function that returns a non-heap or a stack class is
michael@0 264 // definitely doing something wrong
michael@0 265 astMatcher.addMatcher(callExpr(callee(functionDecl(allOf(heapAllocator(),
michael@0 266 returns(pointerType(pointee(nonheapClassAggregate()))))))).bind("node"),
michael@0 267 &nonheapClassChecker);
michael@0 268 astMatcher.addMatcher(callExpr(callee(functionDecl(allOf(heapAllocator(),
michael@0 269 returns(pointerType(pointee(stackClassAggregate()))))))).bind("node"),
michael@0 270 &stackClassChecker);
michael@0 271 }
michael@0 272
michael@0 273 void DiagnosticsMatcher::StackClassChecker::run(
michael@0 274 const MatchFinder::MatchResult &Result) {
michael@0 275 DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
michael@0 276 unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID(
michael@0 277 DiagnosticIDs::Error, "variable of type %0 only valid on the stack");
michael@0 278 if (const VarDecl *d = Result.Nodes.getNodeAs<VarDecl>("node")) {
michael@0 279 // Ignore the match if it's a local variable.
michael@0 280 if (d->hasLocalStorage())
michael@0 281 return;
michael@0 282
michael@0 283 Diag.Report(d->getLocation(), stackID) << d->getType();
michael@0 284 noteInferred(d->getType(), Diag);
michael@0 285 } else if (const CXXNewExpr *expr =
michael@0 286 Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
michael@0 287 // If it's placement new, then this match doesn't count.
michael@0 288 if (isPlacementNew(expr))
michael@0 289 return;
michael@0 290 Diag.Report(expr->getStartLoc(), stackID) << expr->getAllocatedType();
michael@0 291 noteInferred(expr->getAllocatedType(), Diag);
michael@0 292 } else if (const CallExpr *expr =
michael@0 293 Result.Nodes.getNodeAs<CallExpr>("node")) {
michael@0 294 QualType badType = expr->getCallReturnType()->getPointeeType();
michael@0 295 Diag.Report(expr->getLocStart(), stackID) << badType;
michael@0 296 noteInferred(badType, Diag);
michael@0 297 }
michael@0 298 }
michael@0 299
michael@0 300 void DiagnosticsMatcher::StackClassChecker::noteInferred(QualType T,
michael@0 301 DiagnosticsEngine &Diag) {
michael@0 302 unsigned inheritsID = Diag.getDiagnosticIDs()->getCustomDiagID(
michael@0 303 DiagnosticIDs::Note,
michael@0 304 "%0 is a stack class because it inherits from a stack class %1");
michael@0 305 unsigned memberID = Diag.getDiagnosticIDs()->getCustomDiagID(
michael@0 306 DiagnosticIDs::Note,
michael@0 307 "%0 is a stack class because member %1 is a stack class %2");
michael@0 308
michael@0 309 // Find the CXXRecordDecl that is the stack class of interest
michael@0 310 while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
michael@0 311 T = arrTy->getElementType();
michael@0 312 CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
michael@0 313
michael@0 314 // Direct result, we're done.
michael@0 315 if (MozChecker::hasCustomAnnotation(clazz, "moz_stack_class"))
michael@0 316 return;
michael@0 317
michael@0 318 const Decl *cause = inferredAllocCauses[clazz].first;
michael@0 319 if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(cause)) {
michael@0 320 Diag.Report(clazz->getLocation(), inheritsID) << T << CRD->getDeclName();
michael@0 321 } else if (const FieldDecl *FD = dyn_cast<FieldDecl>(cause)) {
michael@0 322 Diag.Report(FD->getLocation(), memberID) << T << FD << FD->getType();
michael@0 323 }
michael@0 324
michael@0 325 // Recursively follow this back.
michael@0 326 noteInferred(cast<ValueDecl>(cause)->getType(), Diag);
michael@0 327 }
michael@0 328
michael@0 329 void DiagnosticsMatcher::NonHeapClassChecker::run(
michael@0 330 const MatchFinder::MatchResult &Result) {
michael@0 331 DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
michael@0 332 unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID(
michael@0 333 DiagnosticIDs::Error, "variable of type %0 is not valid on the heap");
michael@0 334 if (const CXXNewExpr *expr = Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
michael@0 335 // If it's placement new, then this match doesn't count.
michael@0 336 if (isPlacementNew(expr))
michael@0 337 return;
michael@0 338 Diag.Report(expr->getStartLoc(), stackID) << expr->getAllocatedType();
michael@0 339 noteInferred(expr->getAllocatedType(), Diag);
michael@0 340 } else if (const CallExpr *expr = Result.Nodes.getNodeAs<CallExpr>("node")) {
michael@0 341 QualType badType = expr->getCallReturnType()->getPointeeType();
michael@0 342 Diag.Report(expr->getLocStart(), stackID) << badType;
michael@0 343 noteInferred(badType, Diag);
michael@0 344 }
michael@0 345 }
michael@0 346
michael@0 347 void DiagnosticsMatcher::NonHeapClassChecker::noteInferred(QualType T,
michael@0 348 DiagnosticsEngine &Diag) {
michael@0 349 unsigned inheritsID = Diag.getDiagnosticIDs()->getCustomDiagID(
michael@0 350 DiagnosticIDs::Note,
michael@0 351 "%0 is a non-heap class because it inherits from a non-heap class %1");
michael@0 352 unsigned memberID = Diag.getDiagnosticIDs()->getCustomDiagID(
michael@0 353 DiagnosticIDs::Note,
michael@0 354 "%0 is a non-heap class because member %1 is a non-heap class %2");
michael@0 355
michael@0 356 // Find the CXXRecordDecl that is the stack class of interest
michael@0 357 while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
michael@0 358 T = arrTy->getElementType();
michael@0 359 CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
michael@0 360
michael@0 361 // Direct result, we're done.
michael@0 362 if (MozChecker::hasCustomAnnotation(clazz, "moz_nonheap_class"))
michael@0 363 return;
michael@0 364
michael@0 365 const Decl *cause = inferredAllocCauses[clazz].first;
michael@0 366 if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(cause)) {
michael@0 367 Diag.Report(clazz->getLocation(), inheritsID) << T << CRD->getDeclName();
michael@0 368 } else if (const FieldDecl *FD = dyn_cast<FieldDecl>(cause)) {
michael@0 369 Diag.Report(FD->getLocation(), memberID) << T << FD << FD->getType();
michael@0 370 }
michael@0 371
michael@0 372 // Recursively follow this back.
michael@0 373 noteInferred(cast<ValueDecl>(cause)->getType(), Diag);
michael@0 374 }
michael@0 375
michael@0 376 class MozCheckAction : public PluginASTAction {
michael@0 377 public:
michael@0 378 ASTConsumer *CreateASTConsumer(CompilerInstance &CI, StringRef fileName) {
michael@0 379 MozChecker *checker = new MozChecker(CI);
michael@0 380
michael@0 381 ASTConsumer *consumers[] = { checker, checker->getOtherConsumer() };
michael@0 382 return new MultiplexConsumer(consumers);
michael@0 383 }
michael@0 384
michael@0 385 bool ParseArgs(const CompilerInstance &CI,
michael@0 386 const std::vector<std::string> &args) {
michael@0 387 return true;
michael@0 388 }
michael@0 389 };
michael@0 390 }
michael@0 391
michael@0 392 static FrontendPluginRegistry::Add<MozCheckAction>
michael@0 393 X("moz-check", "check moz action");

mercurial