Index: lib/Analysis/NaCl/PNaClABIVerifyModule.cpp |
diff --git a/lib/Analysis/NaCl/PNaClABIVerifyModule.cpp b/lib/Analysis/NaCl/PNaClABIVerifyModule.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d4bb151367c9607d00490b9592625ae24b98d734 |
--- /dev/null |
+++ b/lib/Analysis/NaCl/PNaClABIVerifyModule.cpp |
@@ -0,0 +1,327 @@ |
+//===- PNaClABIVerifyModule.cpp - Verify PNaCl ABI rules ------------------===// |
+// |
+// The LLVM Compiler Infrastructure |
+// |
+// This file is distributed under the University of Illinois Open Source |
+// License. See LICENSE.TXT for details. |
+// |
+//===----------------------------------------------------------------------===// |
+// |
+// Verify module-level PNaCl ABI requirements (specifically those that do not |
+// require looking at the function bodies) |
+// |
+// |
+//===----------------------------------------------------------------------===// |
+ |
+#include "llvm/Analysis/NaCl/PNaClABIVerifyModule.h" |
+#include "llvm/ADT/Twine.h" |
+#include "llvm/Analysis/NaCl/PNaClABIProps.h" |
+#include "llvm/Analysis/NaCl/PNaClABITypeChecker.h" |
+#include "llvm/Analysis/NaCl/PNaClAllowedIntrinsics.h" |
+#include "llvm/IR/Constants.h" |
+#include "llvm/IR/DerivedTypes.h" |
+#include "llvm/IR/Instructions.h" |
+#include "llvm/Support/Debug.h" |
+#include "llvm/Support/raw_ostream.h" |
+ |
+using namespace llvm; |
+ |
+namespace llvm { |
+cl::opt<bool> |
+PNaClABIAllowDebugMetadata("pnaclabi-allow-debug-metadata", |
+ cl::desc("Allow debug metadata during PNaCl ABI verification."), |
+ cl::init(false)); |
+ |
+cl::opt<bool> |
+PNaClABIAllowMinsfiSyscalls("pnaclabi-allow-minsfi-syscalls", |
+ cl::desc("Allow undefined references to MinSFI syscall functions."), |
+ cl::init(false)); |
+ |
+} |
+ |
+PNaClABIVerifyModule::~PNaClABIVerifyModule() { |
+ if (ReporterIsOwned) |
+ delete Reporter; |
+} |
+ |
+// MinSFI syscalls are functions with a given prefix which are left undefined |
+// and later linked against their implementation inside the trusted runtime. |
+// If the corresponding flag is set, do allow these external symbols in the |
+// module. |
+// |
+// We also require the syscall declarations to have an i32 return type. This |
+// is meant to prevent abusing syscalls to obtain an undefined value, e.g. by |
+// invoking a syscall whose trusted implementation returns void as a function |
+// which returns an integer, leaking the value of a register (see comments in |
+// the SubstituteUndefs pass for more information on undef values). |
+static bool isAllowedMinsfiSyscall(const Function *Func) { |
+ return PNaClABIAllowMinsfiSyscalls && |
+ Func->getName().startswith("__minsfi_syscall_") && |
+ Func->getReturnType()->isIntegerTy(32); |
+} |
+ |
+// Check linkage type and section attributes, which are the same for |
+// GlobalVariables and Functions. |
+void PNaClABIVerifyModule::checkGlobalValue(const GlobalValue *GV) { |
+ assert(!isa<GlobalAlias>(GV)); |
+ const char *GVTypeName = PNaClABIProps::GVTypeName(isa<Function>(GV)); |
+ GlobalValue::LinkageTypes Linkage = GV->getLinkage(); |
+ if (!PNaClABIProps::isValidGlobalLinkage(Linkage)) { |
+ Reporter->addError() << GVTypeName << " " << GV->getName() |
+ << " has disallowed linkage type: " |
+ << PNaClABIProps::LinkageName(Linkage) << "\n"; |
+ } |
+ if (Linkage == GlobalValue::ExternalLinkage) checkExternalSymbol(GV); |
+ if (GV->getVisibility() != GlobalValue::DefaultVisibility) { |
+ std::string Text = "unknown"; |
+ if (GV->getVisibility() == GlobalValue::HiddenVisibility) { |
+ Text = "hidden"; |
+ } else if (GV->getVisibility() == GlobalValue::ProtectedVisibility) { |
+ Text = "protected"; |
+ } |
+ Reporter->addError() << GVTypeName << " " << GV->getName() |
+ << " has disallowed visibility: " << Text << "\n"; |
+ } |
+ if (GV->hasSection()) { |
+ Reporter->addError() << GVTypeName << " " << GV->getName() << |
+ " has disallowed \"section\" attribute\n"; |
+ } |
+ if (GV->getType()->getAddressSpace() != 0) { |
+ Reporter->addError() << GVTypeName << " " << GV->getName() |
+ << " has addrspace attribute (disallowed)\n"; |
+ } |
+ // The "unnamed_addr" attribute can be used to merge duplicate |
+ // definitions, but that should be done by user-toolchain |
+ // optimization passes, not by the PNaCl translator. |
+ if (GV->hasUnnamedAddr()) { |
+ Reporter->addError() << GVTypeName << " " << GV->getName() |
+ << " has disallowed \"unnamed_addr\" attribute\n"; |
+ } |
+} |
+ |
+void PNaClABIVerifyModule::checkExternalSymbol(const GlobalValue *GV) { |
+ if (const Function *Func = dyn_cast<const Function>(GV)) { |
+ if (Func->isIntrinsic() || isAllowedMinsfiSyscall(Func)) |
+ return; |
+ } |
+ |
+ // We only allow __pnacl_pso_root to be a variable, not a function, to |
+ // reduce the number of cases that the translator needs to handle. |
+ bool ValidEntry = |
+ (isa<Function>(GV) && GV->getName().equals("_start")) || |
+ (isa<GlobalVariable>(GV) && GV->getName().equals("__pnacl_pso_root")); |
+ if (!ValidEntry) { |
+ Reporter->addError() |
+ << GV->getName() |
+ << " is not a valid external symbol (disallowed)\n"; |
+ } else { |
+ if (SeenEntryPoint) { |
+ Reporter->addError() << |
+ "Module has multiple entry points (disallowed)\n"; |
+ } |
+ SeenEntryPoint = true; |
+ } |
+} |
+ |
+static bool isPtrToIntOfGlobal(const Constant *C) { |
+ if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(C)) { |
+ return CE->getOpcode() == Instruction::PtrToInt && |
+ isa<GlobalValue>(CE->getOperand(0)); |
+ } |
+ return false; |
+} |
+ |
+// This checks for part of the normal form produced by FlattenGlobals. |
+static bool isSimpleElement(const Constant *C) { |
+ // A SimpleElement is one of the following: |
+ // 1) An i8 array literal or zeroinitializer: |
+ // [SIZE x i8] c"DATA" |
+ // [SIZE x i8] zeroinitializer |
+ if (ArrayType *Ty = dyn_cast<ArrayType>(C->getType())) { |
+ return Ty->getElementType()->isIntegerTy(8) && |
+ (isa<ConstantAggregateZero>(C) || |
+ isa<ConstantDataArray>(C)); |
+ } |
+ // 2) A reference to a GlobalValue (a function or global variable) |
+ // with an optional byte offset added to it (the addend). |
+ if (C->getType()->isIntegerTy(32)) { |
+ const ConstantExpr *CE = dyn_cast<ConstantExpr>(C); |
+ if (!CE) |
+ return false; |
+ // Without addend: ptrtoint (TYPE* @GLOBAL to i32) |
+ if (isPtrToIntOfGlobal(CE)) |
+ return true; |
+ // With addend: add (i32 ptrtoint (TYPE* @GLOBAL to i32), i32 ADDEND) |
+ if (CE->getOpcode() == Instruction::Add && |
+ isPtrToIntOfGlobal(CE->getOperand(0)) && |
+ isa<ConstantInt>(CE->getOperand(1))) |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+// This checks for part of the normal form produced by FlattenGlobals. |
+static bool isCompoundElement(const Constant *C) { |
+ const ConstantStruct *CS = dyn_cast<ConstantStruct>(C); |
+ if (!CS || !CS->getType()->isPacked() || CS->getType()->hasName() || |
+ CS->getNumOperands() <= 1) |
+ return false; |
+ for (unsigned I = 0; I < CS->getNumOperands(); ++I) { |
+ if (!isSimpleElement(CS->getOperand(I))) |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+static std::string getAttributesAsString(AttributeSet Attrs) { |
+ std::string AttrsAsString; |
+ for (unsigned Slot = 0; Slot < Attrs.getNumSlots(); ++Slot) { |
+ for (AttributeSet::iterator Attr = Attrs.begin(Slot), |
+ E = Attrs.end(Slot); Attr != E; ++Attr) { |
+ AttrsAsString += " "; |
+ AttrsAsString += Attr->getAsString(); |
+ } |
+ } |
+ return AttrsAsString; |
+} |
+ |
+// This checks that the GlobalVariable has the normal form produced by |
+// the FlattenGlobals pass. |
+void PNaClABIVerifyModule::checkGlobalIsFlattened(const GlobalVariable *GV) { |
+ if (!GV->hasInitializer()) { |
+ Reporter->addError() << "Global variable " << GV->getName() |
+ << " has no initializer (disallowed)\n"; |
+ return; |
+ } |
+ const Constant *InitVal = GV->getInitializer(); |
+ if (isSimpleElement(InitVal) || isCompoundElement(InitVal)) |
+ return; |
+ Reporter->addError() << "Global variable " << GV->getName() |
+ << " has non-flattened initializer (disallowed): " |
+ << *InitVal << "\n"; |
+} |
+ |
+void PNaClABIVerifyModule::checkFunction(const Function *F, |
+ const StringRef &Name, |
+ PNaClAllowedIntrinsics &Intrinsics) { |
+ if (F->isIntrinsic()) { |
+ // Check intrinsics. |
+ if (!Intrinsics.isAllowed(F)) { |
+ Reporter->addError() << "Function " << F->getName() |
+ << " is a disallowed LLVM intrinsic\n"; |
+ } |
+ } else { |
+ // Check types of functions and their arguments. Not necessary |
+ // for intrinsics, whose types are fixed anyway, and which have |
+ // argument types that we disallow such as i8. |
+ if (!PNaClABITypeChecker::isValidFunctionType(F->getFunctionType())) { |
+ Reporter->addError() |
+ << "Function " << Name << " has disallowed type: " |
+ << PNaClABITypeChecker::getTypeName(F->getFunctionType()) |
+ << "\n"; |
+ } |
+ // This check is disabled in streaming mode because it would |
+ // reject a function that is defined but not read in yet. |
+ // Unfortunately this means we simply don't check this property |
+ // when translating a pexe in the browser. |
+ // TODO(mseaborn): Enforce this property in the bitcode reader. |
+ if (!StreamingMode && F->isDeclaration() && !isAllowedMinsfiSyscall(F)) { |
+ Reporter->addError() << "Function " << Name |
+ << " is declared but not defined (disallowed)\n"; |
+ } |
+ if (!F->getAttributes().isEmpty()) { |
+ Reporter->addError() |
+ << "Function " << Name << " has disallowed attributes:" |
+ << getAttributesAsString(F->getAttributes()) << "\n"; |
+ } |
+ if (!PNaClABIProps::isValidCallingConv(F->getCallingConv())) { |
+ Reporter->addError() |
+ << "Function " << Name << " has disallowed calling convention: " |
+ << PNaClABIProps::CallingConvName(F->getCallingConv()) << " (" |
+ << F->getCallingConv() << ")\n"; |
+ } |
+ } |
+ |
+ checkGlobalValue(F); |
+ |
+ if (F->hasGC()) { |
+ Reporter->addError() << "Function " << Name << |
+ " has disallowed \"gc\" attribute\n"; |
+ } |
+ // Knowledge of what function alignments are useful is |
+ // architecture-specific and sandbox-specific, so PNaCl pexes |
+ // should not be able to specify function alignment. |
+ if (F->getAlignment() != 0) { |
+ Reporter->addError() << "Function " << Name << |
+ " has disallowed \"align\" attribute\n"; |
+ } |
+} |
+ |
+bool PNaClABIVerifyModule::runOnModule(Module &M) { |
+ SeenEntryPoint = false; |
+ PNaClAllowedIntrinsics Intrinsics(&M.getContext()); |
+ |
+ if (!M.getModuleInlineAsm().empty()) { |
+ Reporter->addError() << |
+ "Module contains disallowed top-level inline assembly\n"; |
+ } |
+ |
+ for (Module::const_global_iterator MI = M.global_begin(), ME = M.global_end(); |
+ MI != ME; ++MI) { |
+ checkGlobalIsFlattened(MI); |
+ checkGlobalVariable(MI); |
+ |
+ if (MI->isThreadLocal()) { |
+ Reporter->addError() << "Variable " << MI->getName() << |
+ " has disallowed \"thread_local\" attribute\n"; |
+ } |
+ if (MI->isExternallyInitialized()) { |
+ Reporter->addError() << "Variable " << MI->getName() << |
+ " has disallowed \"externally_initialized\" attribute\n"; |
+ } |
+ } |
+ |
+ // No aliases allowed for now. |
+ for (Module::alias_iterator MI = M.alias_begin(), |
+ E = M.alias_end(); MI != E; ++MI) { |
+ Reporter->addError() << "Variable " << MI->getName() << |
+ " is an alias (disallowed)\n"; |
+ } |
+ |
+ for (Module::const_iterator MI = M.begin(), ME = M.end(); MI != ME; ++MI) { |
+ checkFunction(MI, MI->getName(), Intrinsics); |
+ } |
+ |
+ // Check named metadata nodes |
+ for (Module::const_named_metadata_iterator I = M.named_metadata_begin(), |
+ E = M.named_metadata_end(); I != E; ++I) { |
+ if (!PNaClABIProps::isWhitelistedMetadata(I)) { |
+ Reporter->addError() << "Named metadata node " << I->getName() |
+ << " is disallowed\n"; |
+ } |
+ } |
+ |
+ if (!SeenEntryPoint) { |
+ Reporter->addError() << "Module has no entry point (disallowed)\n"; |
+ } |
+ Reporter->checkForFatalErrors(); |
+ return false; |
+} |
+ |
+// This method exists so that the passes can easily be run with opt -analyze. |
+// In this case the default constructor is used and we want to reset the error |
+// messages after each print (this is more of an issue for the FunctionPass |
+// than the ModulePass) |
+void PNaClABIVerifyModule::print(llvm::raw_ostream &O, const Module *M) const { |
+ Reporter->printErrors(O); |
+ Reporter->reset(); |
+} |
+ |
+char PNaClABIVerifyModule::ID = 0; |
+INITIALIZE_PASS(PNaClABIVerifyModule, "verify-pnaclabi-module", |
+ "Verify module for PNaCl", false, true) |
+ |
+ModulePass *llvm::createPNaClABIVerifyModulePass( |
+ PNaClABIErrorReporter *Reporter, bool StreamingMode) { |
+ return new PNaClABIVerifyModule(Reporter, StreamingMode); |
+} |