Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(512)

Unified Diff: lib/Analysis/NaCl/PNaClABIVerifyModule.cpp

Issue 939073008: Rebased PNaCl localmods in LLVM to 223109 (Closed)
Patch Set: undo localmod Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/Analysis/NaCl/PNaClABIVerifyFunctions.cpp ('k') | lib/Analysis/NaCl/PNaClAllowedIntrinsics.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
+}
« no previous file with comments | « lib/Analysis/NaCl/PNaClABIVerifyFunctions.cpp ('k') | lib/Analysis/NaCl/PNaClAllowedIntrinsics.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698