├── .gitignore ├── .gitmodules ├── .syntastic_cpp_config ├── AnnotationInfo.cpp ├── AnnotationInfo.h ├── LICENSE ├── Makefile ├── README.md ├── TypeAnnotations.h ├── bin ├── c++ └── cc ├── cchelper.sh ├── common.mk ├── examples ├── nullness │ ├── Makefile │ ├── NullChecks.cpp │ ├── Nullness.cpp │ ├── nullness-c++ │ ├── nullness-cc │ └── test │ │ ├── codegen.c │ │ ├── lit.cfg │ │ ├── simple.c │ │ └── simple.cpp └── tainting │ ├── Makefile │ ├── TaintTracking.cpp │ ├── test │ ├── codegen_simple.c │ ├── cond.c │ ├── endorsement.c │ ├── funcs.c │ ├── lit.cfg │ ├── ok.c │ ├── simple.c │ └── simple.cpp │ ├── ttclang │ └── ttclang++ └── qτ.jpeg /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.dylib 3 | *.so 4 | build/ 5 | **/test/Output/ 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "clang"] 2 | path = clang 3 | url = https://github.com/sampsyo/clang-quala.git 4 | [submodule "llvm"] 5 | path = llvm 6 | url = http://llvm.org/git/llvm.git 7 | -------------------------------------------------------------------------------- /.syntastic_cpp_config: -------------------------------------------------------------------------------- 1 | -D__STDC_CONSTANT_MACROS 2 | -D__STDC_LIMIT_MACROS 3 | -Ibuild/built/include 4 | -I. 5 | -std=c++11 6 | -------------------------------------------------------------------------------- /AnnotationInfo.cpp: -------------------------------------------------------------------------------- 1 | #include "AnnotationInfo.h" 2 | #include 3 | 4 | using namespace llvm; 5 | 6 | AnnotationInfo::AnnotationInfo() : ModulePass(ID) {} 7 | 8 | bool AnnotationInfo::runOnModule(Module &M) { 9 | return false; 10 | } 11 | 12 | bool AnnotationInfo::hasAnnotation(Value *V, StringRef Ann, uint8_t level) { 13 | // Check instruction metadata. 14 | if (auto *I = dyn_cast(V)) { 15 | MDNode *MD = I->getMetadata("tyann"); 16 | if (MD) { 17 | auto *MDS = cast(MD->getOperand(0)); 18 | if (MDS->getString().equals(Ann)) { 19 | auto *CAM = cast(MD->getOperand(1)); 20 | auto *CI = cast(CAM->getValue()); 21 | if (CI->getValue() == level) { 22 | return true; 23 | } else { 24 | return false; 25 | } 26 | } 27 | } 28 | } 29 | 30 | // TODO: Check for annotations on globals, parameters. 31 | 32 | return false; 33 | } 34 | 35 | char AnnotationInfo::ID = 0; 36 | static RegisterPass X("annotation-info", 37 | "gather type annotations", 38 | false, 39 | true); 40 | -------------------------------------------------------------------------------- /AnnotationInfo.h: -------------------------------------------------------------------------------- 1 | #include "llvm/Pass.h" 2 | #include "llvm/IR/Function.h" 3 | #include "llvm/IR/Instructions.h" 4 | #include "llvm/IR/Metadata.h" 5 | #include "llvm/IR/LegacyPassManager.h" 6 | 7 | struct AnnotationInfo : public llvm::ModulePass { 8 | static char ID; 9 | AnnotationInfo(); 10 | virtual bool runOnModule(llvm::Module &M); 11 | bool hasAnnotation(llvm::Value *V, llvm::StringRef Ann, uint8_t level=0); 12 | }; 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2014 Adrian Sampson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BUILD := build 2 | BUILT := $(BUILD)/built 3 | CMAKE := cmake 4 | CMAKE_FLAGS := -G Ninja \ 5 | -DCMAKE_BUILD_TYPE:STRING=Debug \ 6 | -DCMAKE_INSTALL_PREFIX:PATH=$(abspath $(BUILT)) \ 7 | -DLLVM_EXTERNAL_CLANG_SOURCE_DIR:PATH=$(abspath clang) 8 | NINJA := ninja 9 | 10 | # On OS X, build Clang with a built-in search path for this platform. This 11 | # feels a little dirty, since the user can hypothetically change the XCode 12 | # "platform" later on, but I can't find a way to tell Clang to just look for 13 | # *the current platform*. This is the next best thing. 14 | ifeq ($(shell uname -s),Darwin) 15 | CMAKE_FLAGS += \ 16 | -DC_INCLUDE_DIRS:STRING=$(shell xcrun --show-sdk-path)/usr/include 17 | endif 18 | 19 | # Convenient setup stuff for getting LLVM and Clang ready and in place. 20 | .PHONY: llvm 21 | 22 | # Build LLVM using CMake and install it under the build/built/ prefix. 23 | llvm: 24 | mkdir -p $(BUILD)/$@ 25 | cd $(BUILD)/$@ ; $(CMAKE) $(CMAKE_FLAGS) ../../llvm 26 | cd $(BUILD)/$@ ; $(NINJA) install 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Quala: Type Qualifiers for LLVM/Clang 2 | ===================================== 3 | 4 | qτ the koala 5 | 6 | This is an experiment in adding overlay type systems to LLVM and Clang, inspired by Java 8's [JSR-308][] and the [Checker Framework][]. 7 | 8 | [Checker Framework]: http://types.cs.washington.edu/checker-framework/ 9 | [JSR-308]: http://www.jcp.org/en/jsr/detail?id=308 10 | 11 | User-customizable type systems make it possible to add optional checks to a language without hacking the compiler. The world is full of great ideas for one-off type systems that help identify specific problems—SQL injection, say—but it's infeasible to expect all of these to be integrated into a language spec or a compiler. Who would want to deal with hundreds of type system extensions they're not really using? 12 | 13 | Java's JSR-308 invented a clever solution to this problem: make type systems *pluggable*. Add support to the language for arbitrary type annotations and then let users load in libraries that provide typing rules for whatever system they can dream up. 14 | 15 | I want to port this idea to C and C++ with a twist: I need custom type qualifiers to be visible in an intermediate representation so they're available to heavyweight compiler machinery. This is an attempt to permit type qualifiers and custom type checkers in Clang that record their types as metadata in the resultant LLVM IR. 16 | 17 | 18 | ## Building 19 | 20 | This repository includes LLVM and Clang as submodules to make building against the right version easy. Clone with `--recurse-submodules` and type `make llvm` to build Clang itself. This Make target uses [CMake][] and [Ninja][], which is my favorite route to a working toolchain. 21 | 22 | (Specifically, Quala is based on version 3.7 of LLVM and Clang (the most recent release as of this writing). LLVM is stock and unmodified; Clang is a [patched version][clang-quala] that adds a new type kind called `AnnotatedType`.) 23 | 24 | [Ninja]: http://martine.github.io/ninja/ 25 | [CMake]: http://www.cmake.org/ 26 | [clang-quala]: https://github.com/sampsyo/clang-quala 27 | 28 | 29 | ## Example Type Systems 30 | 31 | There are two example typesystems currently: *tainting* and *nullness* (both inspired by equivalents in the [Checker Framework][]). To build either one, cd to `examples/tainting/` or `examples/nullness/` and type `make`. Or type `make test` to check that it's actually working. 32 | 33 | The type systems come with wrapper scripts that invoke Clang with the right arguments to load the plugin and enable the checker. Use these scripts to compile your own code, sit back, and enjoy the type-checking show. 34 | 35 | ### Tainting 36 | 37 | The first example implements [information flow tracking][ift], which can prevent some kinds of security vulnerabilities. The type system tracks *tainted* values and emits errors when they can influence untainted values. For example, you could use this type system to ensure that no user input flows to SQL statements, thereby preventing SQL injection bugs. 38 | 39 | As with any Quala type system, you want to define a macro that encapsulates the type annotation: 40 | 41 | #define TAINTED __attribute__((type_annotate("tainted"))) 42 | 43 | (This might go in a `tainting.h` header, for example, but it doesn't have to.) Then you can write: 44 | 45 | TAINTED int dangerous; 46 | int safe; 47 | safe = dangerous; // BAD 48 | 49 | and get an error on line 3. 50 | 51 | To suppress errors, define an `ENDORSE` macro: 52 | 53 | #define ENDORSE(e) __builtin_annotation((e), "endorse") 54 | 55 | and then this assignment will not emit an error: 56 | 57 | safe = ENDORSE(dangerous); 58 | 59 | The type system also prevents tainted values from being used in conditions, which conservatively prevents [implicit flows][impflow]. 60 | 61 | [impflow]: http://en.wikipedia.org/wiki/Information_flow_(information_theory)#Explicit_Flows_and_Side_Channels 62 | [ift]: http://en.wikipedia.org/wiki/Information_flow_(information_theory) 63 | 64 | ### Nullness 65 | 66 | The *nullness* type system helps catch potential null-pointer dereferences at compile time. It can be thought of as a sound version of the [Clang analyzer][]'s null-dereference checker (that needs a little help from you). 67 | 68 | By default, all pointers are considered *non-null* and the type system emits a warning whenever you try to nullify one: 69 | 70 | int *p = 0; // WARNING 71 | int *q = nullptr; // EXTRA-SPECIAL C++11 WARNING 72 | 73 | To allow a pointer to be null, you have to mark it as *nullable*: 74 | 75 | #define NULLABLE __attribute__((type_annotate("nullable"))) 76 | int * NULLABLE p = 0; // ok 77 | 78 | Note that declaration is careful to declare a *nullable pointer to an int*, not a pointer to a nullable int—which wouldn't make much sense. One of Quala's strengths (over the Clang analyzer's `nonnull` annotation, for example) is that you can put annotations exactly where you mean them. For example: 79 | 80 | int ** NULLABLE p; // nullable pointer to a non-null pointer 81 | int * NULLABLE *q; // non-null pointer to a nullable pointer 82 | 83 | You can also use annotations anywhere that types go, not just on variable declarations: 84 | 85 | typedef int * NULLABLE nullable_int_ptr; 86 | 87 | [Clang analyzer]: http://clang-analyzer.llvm.org/available_checks.html 88 | 89 | 90 | ## Status 91 | 92 | The [modifications to Clang][clang-quala] are relatively minor; there's one new type kind and one new annotation. There's some nonzero chance that these could land in Clang trunk. (Let me know if you have connections!) 93 | 94 | There is a mostly complete frontend type-checking framework. The [TypeAnnotations.h][] template header contains a "library" for writing type systems. 95 | 96 | Limited code generation is implemented. See the nullness type system for an example that uses IR-level type information to instrument the program with dynamic pointer checks. 97 | 98 | Quala is by [Adrian Sampson][] and made available under the [MIT license][]. 99 | 100 | Thanks to [Frederico Araujo][araujof] for the port to version 3.7 of LLVM/Clang. 101 | 102 | [Adrian Sampson]: http://homes.cs.washington.edu/~asampson/ 103 | [MIT license]: http://opensource.org/licenses/MIT 104 | [TypeAnnotations.h]: https://github.com/sampsyo/quala/blob/master/TypeAnnotations.h 105 | [araujof]: https://github.com/araujof 106 | -------------------------------------------------------------------------------- /TypeAnnotations.h: -------------------------------------------------------------------------------- 1 | #ifndef TYPE_ANNOTATIONS_H 2 | #define TYPE_ANNOTATIONS_H 3 | 4 | #include "clang/AST/RecursiveASTVisitor.h" 5 | #include "llvm/ADT/StringRef.h" 6 | #include "llvm/Support/raw_ostream.h" 7 | #include "clang/AST/AST.h" 8 | #include "clang/AST/ASTConsumer.h" 9 | #include "clang/AST/StmtVisitor.h" 10 | #include "clang/Frontend/CompilerInstance.h" 11 | #include "llvm/Support/Debug.h" 12 | #include "clang/Frontend/MultiplexConsumer.h" 13 | 14 | #include 15 | 16 | #define DEBUG_TYPE "quala" 17 | 18 | namespace clang { 19 | 20 | // DANGEROUS HACK 21 | // This is to gain access to the frontend's consumer order so we can change it 22 | // (below). 23 | class HackyMultiplexConsumer : public SemaConsumer { 24 | public: 25 | std::vector Consumers; 26 | }; 27 | 28 | template 29 | class Annotator : public StmtVisitor { 30 | public: 31 | CompilerInstance &CI; 32 | ImplClass *impl; 33 | FunctionDecl *CurFunc; 34 | bool Instrument; 35 | 36 | Annotator(CompilerInstance &_ci, bool _instrument) : 37 | CI(_ci), 38 | impl(static_cast(this)), 39 | CurFunc(NULL), 40 | Instrument(_instrument) 41 | {}; 42 | 43 | /*** ANNOTATION ASSIGNMENT HELPERS ***/ 44 | 45 | void AddAnnotation(Expr *E, StringRef A) const { 46 | // TODO check whether it already has the annotation & do nothing 47 | if (A.size()) { 48 | E->setType(CI.getASTContext().getAnnotatedType(E->getType(), A)); 49 | } 50 | } 51 | 52 | // Remove an annotated type *at the outermost level of the type tree*. For 53 | // example, this will not remove annotations under typedefs (which seems 54 | // impossible). 55 | void RemoveAnnotation(Expr *E) const { 56 | // TODO remove a specific annotation? or all, if multiple? 57 | // Look for an AnnotatedType in the desugaring chain. 58 | QualType T = E->getType(); 59 | if (auto *AT = dyn_cast(T)) { 60 | E->setType(AT->getBaseType()); 61 | } 62 | } 63 | 64 | /*** ANNOTATION LOOKUP HELPERS ***/ 65 | 66 | // Override this to provide annotations on types regardless of where they 67 | // appear. 68 | llvm::StringRef ImplicitAnnotation(const QualType QT) const { 69 | return StringRef(); 70 | } 71 | 72 | llvm::StringRef AnnotationOf(const Type *T) const { 73 | // TODO multiple annotations? 74 | if (auto *AT = llvm::dyn_cast(T)) { 75 | return AT->getAnnotation(); 76 | } else { 77 | return StringRef(); 78 | } 79 | } 80 | 81 | llvm::StringRef AnnotationOf(QualType QT) const { 82 | // Try implicit annotation. 83 | StringRef ImplicitAnn = impl->ImplicitAnnotation(QT); 84 | if (ImplicitAnn.size()) { 85 | return ImplicitAnn; 86 | } 87 | 88 | // Look up the annotation. 89 | auto &Ctx = CI.getASTContext(); 90 | for (;;) { 91 | const Type *T = QT.getTypePtrOrNull(); 92 | if (!T) { 93 | return StringRef(); 94 | } 95 | StringRef Ann = AnnotationOf(T); 96 | if (Ann.size()) 97 | return Ann; 98 | 99 | // Try stripping away one level of sugar. 100 | QualType DT = QT.getSingleStepDesugaredType(Ctx); 101 | if (DT == QT) { 102 | break; 103 | } else { 104 | QT = DT; 105 | } 106 | } 107 | 108 | // No annotations found in desugaring sequence. 109 | return StringRef(); 110 | } 111 | 112 | llvm::StringRef AnnotationOf(const Expr *E) const { 113 | if (!E) { 114 | return StringRef(); 115 | } else { 116 | return AnnotationOf(E->getType()); 117 | } 118 | } 119 | 120 | llvm::StringRef AnnotationOf(const ValueDecl *D) const { 121 | if (!D) { 122 | return StringRef(); 123 | } else { 124 | return AnnotationOf(D->getType()); 125 | } 126 | } 127 | 128 | bool SamePointerTypeAnnotations(QualType T1, QualType T2, 129 | bool outer=true) const { 130 | // Optionally check the annotation on the types themselves. 131 | if (outer && AnnotationOf(T1) != AnnotationOf(T2)) 132 | return false; 133 | 134 | // Unwrap pointer types to check that they have identical qualifiers in 135 | // their pointed-to types. 136 | ASTContext &Ctx = CI.getASTContext(); 137 | while (Ctx.UnwrapSimilarPointerTypes(T1, T2)) { 138 | if (AnnotationOf(T1) != AnnotationOf(T2)) { 139 | return false; 140 | } 141 | } 142 | return true; // Identical annotations. 143 | } 144 | 145 | // Utility for checking compatibility when pointer types are invariant 146 | // (i.e., if you are sane). Returns true for non-pointer types. For pointer 147 | // (and reference types), ensures that every annotation *below the top 148 | // layer* is identical between the two types. You are supposed to check the 149 | // top layer yourself, since those annotations apply to value types (i.e., 150 | // the pointers themselves). 151 | bool CheckPointerInvariance(QualType LTy, QualType RTy) const { 152 | if (LTy->isReferenceType() && !RTy->isReferenceType()) { 153 | // Reference binding. Strip off the LHS's reference and compare from 154 | // there. 155 | return SamePointerTypeAnnotations(LTy->getPointeeType(), RTy, true); 156 | } else if (LTy->isPointerType() && RTy->isPointerType()) { 157 | // Must have identical annotations (either direction of flow is an 158 | // error). Enforce comparison after the top level. 159 | return SamePointerTypeAnnotations(LTy, RTy, false); 160 | } else { 161 | // Non-pointer type. Above check suffices. 162 | return true; 163 | } 164 | } 165 | 166 | /*** SUBTYPING (COMPATIBILITY) CHECKS ***/ 167 | 168 | // For subclasses to override: determine compatibility of two types. 169 | bool Compatible(QualType LTy, QualType RTy) const { 170 | return true; 171 | } 172 | 173 | // Raise an "incompatible" error if the expressions are not compatible. Don't 174 | // override this. 175 | void AssertCompatible(Stmt *S, QualType LTy, QualType RTy) const { 176 | if (!impl->Compatible(LTy, RTy)) { 177 | impl->EmitIncompatibleError(S, LTy, RTy); 178 | } 179 | } 180 | 181 | DiagnosticsEngine &Diags() const { 182 | return CI.getDiagnostics(); 183 | } 184 | 185 | void EmitIncompatibleError(clang::Stmt* S, QualType LTy, 186 | QualType RTy) { 187 | unsigned did = Diags().getCustomDiagID( 188 | DiagnosticsEngine::Error, 189 | "%0 incompatible with %1" 190 | ); 191 | StringRef LAnn = AnnotationOf(LTy); 192 | StringRef RAnn = AnnotationOf(RTy); 193 | Diags().Report(S->getLocStart(), did) 194 | << (RAnn.size() ? RAnn : "unannotated") 195 | << (LAnn.size() ? LAnn : "unannotated") 196 | << CharSourceRange(S->getSourceRange(), false); 197 | } 198 | 199 | /*** DEFAULT TYPING RULES ***/ 200 | 201 | // Assignment compatibility. 202 | void VisitBinAssign(BinaryOperator *E) { 203 | AssertCompatible(E, E->getLHS()->getType(), E->getRHS()->getType()); 204 | AddAnnotation(E, AnnotationOf(E->getLHS())); 205 | } 206 | 207 | void VisitCompoundAssignOperator(CompoundAssignOperator *E) { 208 | AssertCompatible(E, E->getLHS()->getType(), E->getRHS()->getType()); 209 | AddAnnotation(E, AnnotationOf(E->getLHS())); 210 | } 211 | 212 | // Declaration initializers treated like assignments. 213 | void VisitDeclStmt(DeclStmt *S) { 214 | for (auto i = S->decl_begin(); i != S->decl_end(); ++i) { 215 | auto *VD = dyn_cast(*i); 216 | if (VD) { 217 | Expr *Init = VD->getInit(); 218 | if (Init) { 219 | AssertCompatible(Init, VD->getType(), Init->getType()); 220 | } 221 | } 222 | } 223 | } 224 | 225 | // Propagate types through implicit casts and other "invisible" expressions. 226 | void VisitImplicitCastExpr(ImplicitCastExpr *E) { 227 | AddAnnotation(E, AnnotationOf(E->getSubExpr())); 228 | } 229 | void VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E) { 230 | AddAnnotation(E, AnnotationOf(E->GetTemporaryExpr())); 231 | } 232 | 233 | // Visit all kinds of call expressions the same way. 234 | void visitCall(CallExpr *E) { 235 | // Check parameter types. 236 | FunctionDecl *D = E->getDirectCallee(); 237 | if (D) { 238 | // Check parameter types. 239 | if (D->param_size() == E->getNumArgs()) { 240 | auto pi = D->param_begin(); 241 | auto ai = E->arg_begin(); 242 | for (; pi != D->param_end() && ai != E->arg_end(); ++pi, ++ai) { 243 | AssertCompatible(*ai, (*pi)->getType(), (*ai)->getType()); 244 | } 245 | } else { 246 | // Parameter list length mismatch. Probably a varargs function. FIXME? 247 | DEBUG(llvm::errs() << "UNSOUND: varargs function\n"); 248 | } 249 | 250 | AddAnnotation(E, AnnotationOf(D->getReturnType())); 251 | } else { 252 | // We couldn't determine which function is being called. Unsoundly, we 253 | // check nothing and return the null type. FIXME? 254 | DEBUG(llvm::errs() << "UNSOUND: indirect call\n"); 255 | } 256 | } 257 | void VisitCallExpr(CallExpr *E) { 258 | visitCall(E); 259 | } 260 | void VisitCXXMemberCallExpr(CallExpr *E) { 261 | visitCall(E); 262 | } 263 | void VisitCXXOperatorCallExpr(CallExpr *E) { 264 | visitCall(E); 265 | } 266 | 267 | void VisitReturnStmt(ReturnStmt *S) { 268 | Expr *E = S->getRetValue(); 269 | if (E) { 270 | assert(CurFunc && "return outside of function?"); 271 | AssertCompatible(S, CurFunc->getReturnType(), E->getType()); 272 | } 273 | } 274 | }; 275 | 276 | // Hack to go bottom-up (postorder) on statements. 277 | template 278 | class TAVisitor : public RecursiveASTVisitor< TAVisitor > { 279 | public: 280 | AnnotatorClass *Annotator; 281 | bool SkipHeaders; 282 | 283 | bool TraverseStmt(Stmt *S) { 284 | // Super traversal: visit children. 285 | RecursiveASTVisitor::TraverseStmt(S); 286 | 287 | // Now give type to parent. 288 | if (S) { 289 | Annotator->Visit(S); 290 | } 291 | 292 | return true; 293 | } 294 | 295 | bool TraverseDecl(Decl *D) { 296 | // Skip traversal of declarations in system headers. 297 | if (SkipHeaders) { 298 | auto Loc = D->getLocation(); 299 | if (Loc.isValid()) { 300 | auto &Ctx = Annotator->CI.getASTContext(); 301 | if (Ctx.getSourceManager().isInSystemHeader(Loc)) { 302 | // Do not traverse any children. 303 | return true; 304 | } 305 | } 306 | } 307 | 308 | // Tell the annotator which function it's inside. 309 | auto *Func = dyn_cast(D); 310 | if (Func) 311 | Annotator->CurFunc = Func; 312 | bool r = RecursiveASTVisitor< TAVisitor >::TraverseDecl(D); 313 | if (Func) 314 | Annotator->CurFunc = NULL; 315 | return r; 316 | } 317 | 318 | // Disable "data recursion", which skips calls to Traverse*. 319 | bool shouldUseDataRecursionFor(Stmt *S) const { return false; } 320 | }; 321 | 322 | template 323 | class TAConsumer : public ASTConsumer { 324 | public: 325 | CompilerInstance &CI; 326 | TAVisitor Visitor; 327 | AnnotatorClass Annotator; 328 | bool Instrument; 329 | 330 | TAConsumer(CompilerInstance &_ci, bool _instrument) : 331 | CI(_ci), 332 | Annotator(_ci, _instrument), 333 | Instrument(_instrument) 334 | {} 335 | 336 | virtual void Initialize(ASTContext &Context) { 337 | Visitor.Annotator = &Annotator; 338 | Visitor.SkipHeaders = true; // TODO configurable? 339 | 340 | if (Instrument) { 341 | // DANGEROUS HACK 342 | // Change the order of the frontend's AST consumers. The 343 | // MultiplexConsumer contains references to (a) the "real" Clang 344 | // consumer, which does codegen and such, and (b) all the plugin 345 | // consumers. We need to move this plugin to the front of the list to 346 | // run before codegen. This is *certainly* unsupported and, to make it 347 | // even worse, requires hackery to access the private consumer vector 348 | // in MultiplexConsumer. Too bad. 349 | auto *mxc = cast(&CI.getASTConsumer()); 350 | auto *hmxc = (HackyMultiplexConsumer *)mxc; 351 | 352 | // Find this consumer in the list. 353 | auto it = std::find(hmxc->Consumers.begin(), 354 | hmxc->Consumers.end(), 355 | this); 356 | assert(*it == this && "consumer not found in multiplex list"); 357 | 358 | // Move this from its current position to the first position. 359 | hmxc->Consumers.erase(it); 360 | hmxc->Consumers.insert(hmxc->Consumers.begin(), this); 361 | } 362 | } 363 | 364 | virtual bool HandleTopLevelDecl(DeclGroupRef DG) { 365 | for (auto it : DG) { 366 | Visitor.TraverseDecl(it); 367 | } 368 | return true; 369 | } 370 | }; 371 | 372 | } 373 | 374 | #undef DEBUG_TYPE 375 | 376 | #endif 377 | -------------------------------------------------------------------------------- /bin/c++: -------------------------------------------------------------------------------- 1 | cc -------------------------------------------------------------------------------- /bin/cc: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | here=`dirname $0` 4 | basedir=$here/.. 5 | builtdir=$basedir/build/built 6 | clang=$builtdir/bin/clang 7 | clangargs= 8 | 9 | # Check whether we are invoked as a C++ compiler. 10 | if [ $(basename $0) == "c++" ]; then 11 | clang=$clang++ 12 | cxx=1 13 | fi 14 | 15 | # Patch up include path on OS X. 16 | if xcrun --version >/dev/null 2>&1 ; then 17 | toolchain=$(dirname $(dirname $(dirname $(xcrun -f clang)))) 18 | if [ -n "$cxx" ]; then 19 | clangargs="$clangargs -I$toolchain/usr/include/c++/v1" 20 | # c.f. https://gist.github.com/sampsyo/cd5a24d1158b14a83ef1 21 | clangargs="$clangargs -I$toolchain/usr/lib/c++/v1" 22 | fi 23 | fi 24 | 25 | # Work around Clang include path search bug on Debian/Ubuntu. 26 | if [ -e /usr/include/x86_64-linux-gnu/c++/4.8 ]; then 27 | if [ -n "$cxx" ]; then 28 | clangargs="$clangargs -I/usr/include/x86_64-linux-gnu/c++/4.8" 29 | fi 30 | fi 31 | 32 | exec $clang $clangargs $@ 33 | -------------------------------------------------------------------------------- /cchelper.sh: -------------------------------------------------------------------------------- 1 | # Choose cc or c++ based on the executable name. 2 | compiler=$0 3 | if [ "${compiler#*++}" != "$compiler" ]; then 4 | cc=c++ 5 | else 6 | cc=cc 7 | fi 8 | ccpath=$base/bin/$cc 9 | 10 | # Construct Clang arguments. 11 | if [ `uname` = "Darwin" ]; then 12 | libext=dylib 13 | else 14 | libext=so 15 | fi 16 | -------------------------------------------------------------------------------- /common.mk: -------------------------------------------------------------------------------- 1 | HERE := $(dir $(lastword $(MAKEFILE_LIST))) 2 | BUILD := $(HERE)/build 3 | BUILT := $(BUILD)/built 4 | LLVM_CONFIG := $(BUILT)/bin/llvm-config 5 | 6 | # Get LLVM build parameters from its llvm-config program. 7 | LLVM_CXXFLAGS := $(shell $(LLVM_CONFIG) --cxxflags) -fno-rtti 8 | LLVM_LDFLAGS := $(shell $(LLVM_CONFIG) --ldflags) 9 | LLVM_LIBS := $(shell $(LLVM_CONFIG) --libs --system-libs) 10 | CLANG_LIBS := \ 11 | -lclangAST \ 12 | -lclangAnalysis \ 13 | -lclangBasic \ 14 | -lclangDriver \ 15 | -lclangEdit \ 16 | -lclangFrontend \ 17 | -lclangFrontendTool \ 18 | -lclangLex \ 19 | -lclangParse \ 20 | -lclangSema \ 21 | -lclangEdit \ 22 | -lclangASTMatchers \ 23 | -lclangRewriteCore \ 24 | -lclangRewriteFrontend \ 25 | -lclangStaticAnalyzerFrontend \ 26 | -lclangStaticAnalyzerCheckers \ 27 | -lclangStaticAnalyzerCore \ 28 | -lclangSerialization \ 29 | -lclangTooling 30 | 31 | # On OS X, you need to tell the linker that undefined symbols will be looked 32 | # up at runtime. 33 | PLUGIN_LDFLAGS := -shared 34 | ifeq ($(shell uname -s),Darwin) 35 | PLUGIN_LDFLAGS += -Wl,-undefined,dynamic_lookup 36 | endif 37 | 38 | # Platform-specific library suffix. 39 | ifeq ($(shell uname -s),Darwin) 40 | LIBEXT := dylib 41 | else 42 | LIBEXT := so 43 | endif 44 | -------------------------------------------------------------------------------- /examples/nullness/Makefile: -------------------------------------------------------------------------------- 1 | include ../../common.mk 2 | 3 | CHECKER_SOURCES := Nullness.cpp 4 | PASS_SOURCES := NullChecks.cpp ../../AnnotationInfo.cpp 5 | HEADERS := ../../TypeAnnotations.h 6 | CHECKER_TARGET := Nullness.$(LIBEXT) 7 | PASS_TARGET := NullChecks.$(LIBEXT) 8 | 9 | CHECKER_OBJS := $(CHECKER_SOURCES:%.cpp=%.o) 10 | PASS_OBJS := $(PASS_SOURCES:%.cpp=%.o) 11 | 12 | CXXFLAGS += -I../.. 13 | 14 | .PHONY: all 15 | all: $(CHECKER_TARGET) $(PASS_TARGET) 16 | 17 | # Build the Clang plugin module. 18 | $(CHECKER_TARGET): $(CHECKER_OBJS) 19 | $(CXX) $(PLUGIN_LDFLAGS) $(CXXFLAGS) \ 20 | $(LLVM_CXXFLAGS) $(LLVM_LDFLAGS) \ 21 | -o $@ $^ 22 | 23 | # Build the LLVM pass module. 24 | $(PASS_TARGET): $(PASS_OBJS) 25 | $(CXX) $(PLUGIN_LDFLAGS) $(CXXFLAGS) \ 26 | $(LLVM_CXXFLAGS) $(LLVM_LDFLAGS) \ 27 | -o $@ $^ 28 | 29 | %.o: %.cpp $(HEADERS) 30 | $(CXX) -c $(CXXFLAGS) $(LLVM_CXXFLAGS) \ 31 | -o $@ $< 32 | 33 | .PHONY: clean 34 | clean: 35 | rm -rf $(CHECKER_TARGET) $(CHECKER_OBJS) $(PASS_TARGET) $(PASS_OBJS) 36 | 37 | # Testing stuff. 38 | .PHONY: test smoke 39 | test: $(TARGET) 40 | $(BUILD)/llvm/bin/llvm-lit -v test 41 | smoke: $(TARGET) 42 | ./nullness-c++ -S -emit-llvm -o /dev/null test/simple.cpp -v 43 | -------------------------------------------------------------------------------- /examples/nullness/NullChecks.cpp: -------------------------------------------------------------------------------- 1 | #include "llvm/Pass.h" 2 | #include "llvm/IR/Function.h" 3 | #include "llvm/IR/Module.h" 4 | #include "llvm/IR/Instructions.h" 5 | #include "llvm/IR/Metadata.h" 6 | #include "llvm/IR/IRBuilder.h" 7 | #include "llvm/Support/raw_ostream.h" 8 | #include "llvm/Transforms/IPO/PassManagerBuilder.h" 9 | #include "llvm/IR/LegacyPassManager.h" 10 | 11 | #include "AnnotationInfo.h" 12 | 13 | using namespace llvm; 14 | 15 | namespace { 16 | 17 | struct NullChecks : public FunctionPass { 18 | static char ID; 19 | NullChecks() : FunctionPass(ID) {} 20 | 21 | virtual void getAnalysisUsage(AnalysisUsage &Info) const { 22 | Info.addRequired(); 23 | } 24 | 25 | virtual bool runOnFunction(Function &F) { 26 | AnnotationInfo &AI = getAnalysis(); 27 | bool modified = false; 28 | 29 | for (auto &BB : F) { 30 | for (auto &I : BB) { 31 | // Is this a load or store? Get the address. 32 | Value *Ptr = nullptr; 33 | if (auto *LI = dyn_cast(&I)) { 34 | Ptr = LI->getPointerOperand(); 35 | } else if (auto *SI = dyn_cast(&I)) { 36 | Ptr = SI->getPointerOperand(); 37 | } 38 | 39 | // Dereferencing a pointer (either for a load or a store). Insert a 40 | // check if the pointer is nullable. 41 | if (Ptr) { 42 | if (AI.hasAnnotation(Ptr, "nullable")) { 43 | addCheck(*Ptr, I); 44 | modified = true; 45 | } 46 | } 47 | } 48 | } 49 | 50 | return modified; 51 | } 52 | 53 | Function &getCheckFunc(Module &M) { 54 | LLVMContext &Ctx = M.getContext(); 55 | 56 | // Get or add the declaration. 57 | Constant *C = M.getOrInsertFunction("qualaNullCheck", 58 | Type::getVoidTy(Ctx), Type::getInt1Ty(Ctx), NULL); 59 | auto *F = cast(C); 60 | 61 | // If the function does not have a body yet, write it. 62 | if (F->empty()) { 63 | BasicBlock *Entry = BasicBlock::Create(Ctx, "entry", F); 64 | BasicBlock *Success = BasicBlock::Create(Ctx, "success", F); 65 | BasicBlock *Failure = BasicBlock::Create(Ctx, "failure", F); 66 | 67 | // Check block. 68 | Value *isnull = F->arg_begin(); 69 | IRBuilder<> Bld(Entry); 70 | Bld.CreateCondBr(isnull, Failure, Success); 71 | 72 | // Success (non-null) block. 73 | Bld.SetInsertPoint(Success); 74 | Bld.CreateRetVoid(); 75 | 76 | // Failure (null) block. If there's a funciton in the module to handle 77 | // this, call it. Otherwise, call exit(3). 78 | Bld.SetInsertPoint(Failure); 79 | if (Function *Handler = M.getFunction("qualaHandleNull")) { 80 | // Call the user's handler. 81 | Bld.CreateCall(Handler); 82 | Bld.CreateRetVoid(); 83 | } else { 84 | // Call exit(3). 85 | AttributeSet Attrs; 86 | Attrs.addAttribute(Ctx, AttributeSet::FunctionIndex, 87 | Attribute::NoReturn); 88 | Constant *Exit = M.getOrInsertFunction("exit", Attrs, 89 | Type::getVoidTy(Ctx), Type::getInt32Ty(Ctx), NULL); 90 | Bld.CreateCall(Exit, Bld.getInt32(1)); 91 | Bld.CreateUnreachable(); 92 | } 93 | } 94 | 95 | return *F; 96 | } 97 | 98 | // Insert a null check for the given pointer value just before the 99 | // instruction. 100 | void addCheck(Value &Ptr, Instruction &I) { 101 | IRBuilder<> Bld(&I); 102 | Value *isnull = Bld.CreateIsNull(&Ptr, "isnull"); 103 | Module *M = I.getParent()->getParent()->getParent(); 104 | Bld.CreateCall(&(getCheckFunc(*M)), isnull); 105 | } 106 | }; 107 | 108 | } 109 | 110 | char NullChecks::ID = 0; 111 | 112 | // http://homes.cs.washington.edu/~asampson/blog/clangpass.html 113 | static void registerPass(const PassManagerBuilder &, 114 | legacy::PassManagerBase &PM) { 115 | PM.add(new NullChecks()); 116 | } 117 | static RegisterStandardPasses 118 | RegisterMyPass(PassManagerBuilder::EP_EarlyAsPossible, 119 | registerPass); 120 | -------------------------------------------------------------------------------- /examples/nullness/Nullness.cpp: -------------------------------------------------------------------------------- 1 | #include "TypeAnnotations.h" 2 | 3 | #include "clang/Frontend/FrontendPluginRegistry.h" 4 | #include "clang/Frontend/CompilerInstance.h" 5 | #include "llvm/Support/raw_ostream.h" 6 | using namespace clang; 7 | 8 | namespace { 9 | 10 | #define NULLABLE_ANN "nullable" 11 | 12 | class NullnessAnnotator: public Annotator { 13 | public: 14 | NullnessAnnotator(CompilerInstance &ci, bool instrument) 15 | : Annotator(ci, instrument) {}; 16 | 17 | // Check for NULLABLE annotation. 18 | template 19 | bool nullable(const T V) const { 20 | return AnnotationOf(V).equals(NULLABLE_ANN); 21 | } 22 | 23 | // Check dereferencing and address-of expressions. 24 | void VisitUnaryOperator(UnaryOperator *E) { 25 | switch (E->getOpcode()) { 26 | case UO_Deref: 27 | if (nullable(E->getSubExpr())) { 28 | unsigned did = Diags().getCustomDiagID( 29 | DiagnosticsEngine::Warning, 30 | "dereferencing nullable pointer" 31 | ); 32 | Diags().Report(E->getLocStart(), did) 33 | << CharSourceRange(E->getSourceRange(), false); 34 | } 35 | break; 36 | case UO_AddrOf: 37 | // Address-of always non-null. 38 | break; 39 | default: 40 | // TODO check pointer arithmetic? 41 | break; 42 | } 43 | } 44 | 45 | // Mark null literals as null. 46 | void VisitIntegerLiteral(IntegerLiteral *E) { 47 | if (E->getValue() == 0) { 48 | AddAnnotation(E, NULLABLE_ANN); 49 | } 50 | } 51 | 52 | // The GNU C++ NULL expression. 53 | void VisitGNUNullExpr(GNUNullExpr *E) { 54 | AddAnnotation(E, NULLABLE_ANN); 55 | } 56 | 57 | // C++11 nullptr_t conversions. 58 | void VisitCXXMemberCallExpr(CXXMemberCallExpr *E) { 59 | auto *D = E->getRecordDecl(); 60 | if (D->getName() == "nullptr_t") { 61 | if (auto *Conv = dyn_cast(E->getMethodDecl())) { 62 | if (Conv->getConversionType()->isPointerType()) { 63 | // An `operator T*`. 64 | AddAnnotation(E, NULLABLE_ANN); 65 | } 66 | } 67 | } 68 | Annotator::VisitCXXMemberCallExpr(E); 69 | } 70 | 71 | // Subtyping judgment. 72 | bool Compatible(QualType LTy, QualType RTy) const { 73 | if (LTy->isPointerType()) { 74 | return nullable(LTy) || !nullable(RTy); 75 | } 76 | return CheckPointerInvariance(LTy, RTy); 77 | } 78 | 79 | void EmitIncompatibleError(clang::Stmt* S, QualType LTy, 80 | QualType RTy) { 81 | // TODO would be nice if we could give more context about *which* non-null 82 | // pointer is the problem. 83 | unsigned did = Diags().getCustomDiagID( 84 | DiagnosticsEngine::Warning, 85 | "non-null pointer may become null" 86 | ); 87 | Diags().Report(S->getLocStart(), did) 88 | << CharSourceRange(S->getSourceRange(), false); 89 | } 90 | }; 91 | 92 | class NullnessAction : public PluginASTAction { 93 | protected: 94 | std::unique_ptr CreateASTConsumer(CompilerInstance &CI, 95 | llvm::StringRef) { 96 | // Construct a type checker for our type system. 97 | return llvm::make_unique< TAConsumer >(CI, true); 98 | } 99 | 100 | bool ParseArgs(const CompilerInstance &CI, 101 | const std::vector& args) { 102 | // No arguments. 103 | return true; 104 | } 105 | }; 106 | 107 | } 108 | 109 | static FrontendPluginRegistry::Add 110 | X("nullness", "prevent null pointer dereferences"); 111 | -------------------------------------------------------------------------------- /examples/nullness/nullness-c++: -------------------------------------------------------------------------------- 1 | nullness-cc -------------------------------------------------------------------------------- /examples/nullness/nullness-cc: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | here=`dirname $0` 3 | base=$here/../.. 4 | source $base/cchelper.sh 5 | 6 | exec $ccpath -Xclang -load -Xclang $here/Nullness.$libext \ 7 | -Xclang -add-plugin -Xclang nullness \ 8 | -Xclang -load -Xclang $here/NullChecks.$libext \ 9 | $@ 10 | -------------------------------------------------------------------------------- /examples/nullness/test/codegen.c: -------------------------------------------------------------------------------- 1 | // RUN: clang -Xclang -verify -emit-llvm -S -o - %s | FileCheck %s 2 | 3 | #include 4 | #include 5 | 6 | #define NULLABLE __attribute__((type_annotate("nullable"))) 7 | 8 | int qualaHandleNull() { 9 | fprintf(stderr, "saved from a null dereference!\n"); 10 | exit(1); 11 | } 12 | 13 | int main(int argc, char **argv) { 14 | // CHECK: %foo = alloca i32*, align {{[0-9]+}}, !tyann [[NULLABLE_P:![0-9]+]] 15 | // CHECK: store i32* null, i32** %foo, align {{[0-9]+}}, !tyann [[NULLABLE:![0-9]+]] 16 | int * NULLABLE foo = 0; 17 | // CHECK: call void @qualaNullCheck(i1 %isnull{{[0-9]*}}) 18 | return *foo; // expected-warning {{dereferencing nullable}} 19 | } 20 | 21 | // CHECK-DAG: [[NULLABLE]] = !{!"nullable", i8 0} 22 | // CHECK-DAG: [[NULLABLE_P]] = !{!"nullable", i8 1} 23 | -------------------------------------------------------------------------------- /examples/nullness/test/lit.cfg: -------------------------------------------------------------------------------- 1 | import lit.formats 2 | 3 | config.name = 'tq' 4 | config.test_format = lit.formats.ShTest(execute_external = True) 5 | config.suffixes = ['.c', '.cpp'] 6 | 7 | config.target_triple = 'foo' 8 | 9 | config.substitutions.append( (r' clang ', ' ../nullness-cc ') ) 10 | config.substitutions.append( (r' clang\+\+ ', ' ../nullness-c++ ') ) 11 | config.substitutions.append( (r' FileCheck ', ' ../../../build/llvm/bin/FileCheck ') ) 12 | 13 | # vim: set ft=python : 14 | -------------------------------------------------------------------------------- /examples/nullness/test/simple.c: -------------------------------------------------------------------------------- 1 | // RUN: clang -fsyntax-only -Xclang -verify %s 2 | 3 | #define NULLABLE __attribute__((type_annotate("nullable"))) 4 | 5 | int main() { 6 | int *a; 7 | int * NULLABLE b; 8 | 9 | a = 0; // expected-warning {{may become null}} 10 | b = 0; 11 | a = b; // expected-warning {{may become null}} 12 | b = a; 13 | *a = 1; 14 | *b = 1; // expected-warning {{dereferencing nullable}} 15 | 16 | // Here's something that non-type-based checkers can't do. 17 | int * NULLABLE *c; 18 | int ** NULLABLE d; 19 | c = 0; // expected-warning {{may become null}} 20 | d = 0; 21 | *c = 0; 22 | *d = 0; // expected-warning {{may become null}} \ 23 | expected-warning {{dereferencing nullable}} 24 | 25 | // Or this. 26 | typedef int * NULLABLE nip; 27 | nip e; 28 | e = 0; 29 | b = e; 30 | a = e; // expected-warning {{may become null}} 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /examples/nullness/test/simple.cpp: -------------------------------------------------------------------------------- 1 | // RUN: clang++ -fsyntax-only -Xclang -verify %s 2 | 3 | #include 4 | #include 5 | 6 | #define NULLABLE __attribute__((type_annotate("nullable"))) 7 | 8 | int main() { 9 | int *p; 10 | p = NULL; // expected-warning {{may become null}} 11 | p = nullptr; // expected-warning {{may become null}} 12 | 13 | int * NULLABLE q = 0; 14 | int *&foo = q; // expected-warning {{may become null}} 15 | 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /examples/tainting/Makefile: -------------------------------------------------------------------------------- 1 | include ../../common.mk 2 | 3 | SOURCES := TaintTracking.cpp 4 | HEADERS := ../../TypeAnnotations.h 5 | TARGET := TaintTracking.$(LIBEXT) 6 | 7 | OBJS := $(SOURCES:%.cpp=%.o) 8 | 9 | CXXFLAGS += -I../.. 10 | 11 | $(TARGET): $(OBJS) 12 | $(CXX) $(PLUGIN_LDFLAGS) $(CXXFLAGS) \ 13 | $(LLVM_CXXFLAGS) $(LLVM_LDFLAGS) \ 14 | -o $@ $^ 15 | 16 | %.o: %.cpp $(HEADERS) 17 | $(CXX) -c $(CXXFLAGS) $(LLVM_CXXFLAGS) \ 18 | -o $@ $< 19 | 20 | .PHONY: clean 21 | clean: 22 | rm -rf $(TARGET) $(OBJS) 23 | 24 | # Testing stuff. 25 | .PHONY: test dump smoke 26 | test: $(TARGET) 27 | $(BUILD)/llvm/bin/llvm-lit -v test 28 | dump: $(TARGET) 29 | cd test ; ../ttclang -S -emit-llvm -o - ok.c 30 | smoke: $(TARGET) 31 | cd test ; ../ttclang -S -emit-llvm -o /dev/null endorsement.c 32 | -------------------------------------------------------------------------------- /examples/tainting/TaintTracking.cpp: -------------------------------------------------------------------------------- 1 | #include "TypeAnnotations.h" 2 | 3 | #include "clang/Frontend/FrontendPluginRegistry.h" 4 | #include "clang/Frontend/CompilerInstance.h" 5 | #include "llvm/Support/raw_ostream.h" 6 | #include "clang/Basic/Builtins.h" 7 | using namespace clang; 8 | 9 | namespace { 10 | 11 | #define TAINTED_ANN "tainted" 12 | #define UNTAINTED_ANN "untainted" 13 | 14 | class TaintAnnotator: public Annotator { 15 | public: 16 | TaintAnnotator(CompilerInstance &ci, bool instrument) 17 | : Annotator(ci, instrument) {}; 18 | 19 | // Check whether an expression or type has the "tainted" annotation. 20 | template 21 | bool tainted(const T V) const { 22 | return AnnotationOf(V).equals(TAINTED_ANN); 23 | } 24 | 25 | // Type rule for binary-operator expressions. 26 | void VisitBinaryOperator(BinaryOperator *E) { 27 | // If either subexpression is tainted, so is this expression. 28 | if (tainted(E->getLHS()) || tainted(E->getRHS())) { 29 | AddAnnotation(E, TAINTED_ANN); 30 | } 31 | } 32 | 33 | // And for unary-operator expressions. 34 | void VisitUnaryOperator(UnaryOperator *E) { 35 | // Unary operator just has the type of its operand. 36 | AddAnnotation(E, AnnotationOf(E->getSubExpr())); 37 | } 38 | 39 | // Subtyping judgment. 40 | bool Compatible(QualType LTy, QualType RTy) { 41 | // Top-level annotation: disallow tainted-to-untainted flow. 42 | if (tainted(RTy) && !tainted(LTy)) { 43 | return false; 44 | } 45 | return CheckPointerInvariance(LTy, RTy); 46 | } 47 | 48 | // Endorsements. 49 | void VisitCallExpr(CallExpr *E) { 50 | unsigned biid = E->getBuiltinCallee(); 51 | if (biid == Builtin::BI__builtin_annotation) { 52 | auto *literal = cast(E->getArg(1)); 53 | if (literal->getString() == "endorse") { 54 | // Most of the time, an untainted type just has no annotation 55 | // (i.e., the default). For endorsements, however, we need to mask any 56 | // potential "tainted" annotation anywhere in the hierarchy with 57 | // another annotation so that AnnotationOf returns this instead the 58 | // old type (in the case that the "tainted" is buried somewhere under 59 | // a typedef, for example). 60 | RemoveAnnotation(E); 61 | AddAnnotation(E, "untainted"); 62 | } 63 | } 64 | Annotator::VisitCallExpr(E); 65 | } 66 | 67 | // Conditionals/control flow. Enforce untainted conditions. 68 | void VisitIfStmt(IfStmt *S) { 69 | checkCondition(S->getCond()); 70 | } 71 | void VisitForStmt(ForStmt *S) { 72 | checkCondition(S->getCond()); 73 | } 74 | void VisitWhileStmt(WhileStmt *S) { 75 | checkCondition(S->getCond()); 76 | } 77 | void VisitDoStmt(DoStmt *S) { 78 | checkCondition(S->getCond()); 79 | } 80 | void VisitConditionalOperator(ConditionalOperator *E) { 81 | checkCondition(E->getCond()); 82 | } 83 | void VisitBinaryConditionalOperator(BinaryConditionalOperator *E) { 84 | checkCondition(E->getCond()); 85 | } 86 | void checkCondition(Expr *E) { 87 | if (tainted(E)) { 88 | unsigned did = Diags().getCustomDiagID( 89 | DiagnosticsEngine::Error, 90 | "tainted condition" 91 | ); 92 | Diags().Report(E->getLocStart(), did) 93 | << CharSourceRange(E->getSourceRange(), false); 94 | } 95 | } 96 | }; 97 | 98 | class TaintTrackingAction : public PluginASTAction { 99 | protected: 100 | std::unique_ptr CreateASTConsumer(CompilerInstance &CI, 101 | llvm::StringRef) { 102 | // Construct a type checker for our type system. 103 | return llvm::make_unique< TAConsumer >(CI, true); 104 | } 105 | 106 | bool ParseArgs(const CompilerInstance &CI, 107 | const std::vector& args) { 108 | // No arguments. 109 | return true; 110 | } 111 | }; 112 | 113 | } 114 | 115 | static FrontendPluginRegistry::Add 116 | X("taint-tracking", "information flow type system"); 117 | -------------------------------------------------------------------------------- /examples/tainting/test/codegen_simple.c: -------------------------------------------------------------------------------- 1 | // RUN: clang -Xclang -verify -emit-llvm -S -o - %s | FileCheck %s 2 | // expected-no-diagnostics 3 | 4 | #define TAINTED __attribute__((type_annotate("tainted"))) 5 | 6 | int main() { 7 | // CHECK: %x = alloca i32, align 4, !tyann [[TAINTED_P:![0-9]+]] 8 | // CHECK: %y = alloca i32, align 4 9 | // CHECK: %z = alloca i32, align 4, !tyann [[TAINTED_P]] 10 | 11 | // CHECK: store i32 2, i32* %x, align 4, !tyann [[TAINTED:![0-9]+]] 12 | TAINTED int x = 2; 13 | 14 | // CHECK: store i32 3, i32* %y, align 4 15 | int y = 3; 16 | 17 | // CHECK: %add = add nsw i32 %{{[0-9]+}}, %{{[0-9]+}}, !tyann [[TAINTED]] 18 | TAINTED int z; 19 | z = x + y; 20 | 21 | return 0; 22 | } 23 | 24 | // CHECK-DAG: [[TAINTED]] = !{!"tainted", i8 0} 25 | // CHECK-DAG: [[TAINTED_P]] = !{!"tainted", i8 1} 26 | -------------------------------------------------------------------------------- /examples/tainting/test/cond.c: -------------------------------------------------------------------------------- 1 | // RUN: clang -fsyntax-only -Xclang -verify %s 2 | 3 | #define TAINTED __attribute__((type_annotate("tainted"))) 4 | 5 | int main() { 6 | TAINTED int x = 5; 7 | int y = 6; 8 | 9 | if (x) {} // expected-error {{condition}} 10 | if (y) {} 11 | if (x == 2) {} // expected-error {{condition}} 12 | if (y == 2) {} 13 | for (; x != 10;) {} // expected-error {{condition}} 14 | for (; y != 10;) {} 15 | while (x) {} // expected-error {{condition}} 16 | while (y) {} 17 | do {} while (x); // expected-error {{condition}} 18 | do {} while (y); 19 | y = x ? 1 : 0; // expected-error {{condition}} 20 | y = y ? 1 : 0; 21 | 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /examples/tainting/test/endorsement.c: -------------------------------------------------------------------------------- 1 | // RUN: clang -fsyntax-only -Xclang -verify %s 2 | 3 | #define TAINTED __attribute__((type_annotate("tainted"))) 4 | #define ENDORSE(e) __builtin_annotation((e), "endorse") 5 | 6 | int main() { 7 | // Simplest possible flow violation. 8 | TAINTED int x; 9 | x = 5; 10 | int y; 11 | y = x; // expected-error {{incompatible}} 12 | y = ENDORSE(x); 13 | 14 | if (ENDORSE(x)) {} 15 | if (ENDORSE(x == 2)) {} 16 | if (ENDORSE(x) == 2) {} 17 | 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /examples/tainting/test/funcs.c: -------------------------------------------------------------------------------- 1 | // RUN: clang -fsyntax-only -Xclang -verify %s 2 | 3 | #define TAINTED __attribute__((type_annotate("tainted"))) 4 | 5 | // Parameter is tainted (see below for calls). 6 | void f(TAINTED int p) { 7 | } 8 | 9 | // Return value is tainted (ditto). 10 | TAINTED int g() { 11 | return 2; 12 | } 13 | 14 | // Return value is checked. 15 | int h(TAINTED int p) { 16 | return p; // expected-error {{incompatible}} 17 | } 18 | 19 | // Opposite return value flow is OK. 20 | TAINTED int i(int p) { 21 | return p; 22 | } 23 | 24 | int main() { 25 | TAINTED int x = 5; 26 | int y = 10; 27 | 28 | // Flow into parameter. 29 | f(x); 30 | f(y); 31 | i(x); // expected-error {{incompatible}} 32 | i(y); 33 | 34 | // Flow out of return value. 35 | y = g(); // expected-error {{incompatible}} 36 | x = g(); 37 | y = h(1); 38 | x = h(1); 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /examples/tainting/test/lit.cfg: -------------------------------------------------------------------------------- 1 | import lit.formats 2 | 3 | config.name = 'tq' 4 | config.test_format = lit.formats.ShTest(execute_external = True) 5 | config.suffixes = ['.c', '.cpp'] 6 | 7 | config.target_triple = 'foo' 8 | 9 | config.substitutions.append( (r' clang ', ' ../ttclang ') ) 10 | config.substitutions.append( (r' clang\+\+ ', ' ../ttclang++ ') ) 11 | config.substitutions.append( (r' FileCheck ', ' ../../../build/llvm/bin/FileCheck ') ) 12 | 13 | # vim: set ft=python : 14 | -------------------------------------------------------------------------------- /examples/tainting/test/ok.c: -------------------------------------------------------------------------------- 1 | // RUN: clang -fsyntax-only -Xclang -verify %s 2 | // expected-no-diagnostics 3 | 4 | #define TAINTED __attribute__((type_annotate("tainted"))) 5 | 6 | int main() { 7 | TAINTED int x = 2; 8 | TAINTED float y = (float)x; 9 | TAINTED int z; 10 | z = x + 5; 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /examples/tainting/test/simple.c: -------------------------------------------------------------------------------- 1 | // RUN: clang -fsyntax-only -Xclang -verify %s 2 | 3 | #define TAINTED __attribute__((type_annotate("tainted"))) 4 | 5 | int main() { 6 | // Simplest possible flow violation. 7 | TAINTED int x; 8 | x = 5; 9 | int y; 10 | y = x; // expected-error {{incompatible}} 11 | 12 | // Opposite direction of flow is allowed. 13 | x = y; 14 | 15 | // Binary operator. 16 | y = x + 3; // expected-error {{incompatible}} 17 | 18 | // Unary operator. 19 | y = -x; // expected-error {{incompatible}} 20 | 21 | // Implicit casts (coercions). 22 | float z; 23 | z = x; // expected-error {{incompatible}} 24 | 25 | // Initializers are treated like assignments. 26 | int a = x * 9; // expected-error {{incompatible}} 27 | 28 | // Both directions of flow should be invalid under pointers (because 29 | // mutation). 30 | TAINTED int *b; 31 | int *c; 32 | b = c; // expected-error {{incompatible}} 33 | c = b; // expected-error {{incompatible}} 34 | b = b; 35 | c = c; 36 | b = 0; 37 | c = 0; 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /examples/tainting/test/simple.cpp: -------------------------------------------------------------------------------- 1 | // RUN: clang++ -fsyntax-only -Xclang -verify %s 2 | 3 | #define TAINTED __attribute__((type_annotate("tainted"))) 4 | 5 | int main() { 6 | TAINTED int x; 7 | int y; 8 | 9 | x = y; 10 | TAINTED int &r = y; // expected-error {{incompatible}} 11 | 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /examples/tainting/ttclang: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | here=`dirname $0` 3 | base=$here/../.. 4 | source $base/cchelper.sh 5 | 6 | exec $ccpath -Xclang -load -Xclang $here/TaintTracking.$libext \ 7 | -Xclang -add-plugin -Xclang taint-tracking $@ 8 | -------------------------------------------------------------------------------- /examples/tainting/ttclang++: -------------------------------------------------------------------------------- 1 | ttclang -------------------------------------------------------------------------------- /qτ.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sampsyo/quala/bbb14da98e50d7a3347a0cd9c3686a19e67c5981/qτ.jpeg --------------------------------------------------------------------------------