| 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);
|
| +}
|
|
|