]> granicus.if.org Git - llvm/commitdiff
[Stack Protection] Add diagnostic information for why stack protection was applied...
authorDavid Bozier <seifsta@gmail.com>
Thu, 9 Feb 2017 15:08:40 +0000 (15:08 +0000)
committerDavid Bozier <seifsta@gmail.com>
Thu, 9 Feb 2017 15:08:40 +0000 (15:08 +0000)
Stack Smash Protection is not completely free, so in hot code, the overhead it causes can cause performance issues. By adding diagnostic information for which function have SSP and why, a user can quickly determine what they can do to stop SSP being applied to a specific hot function.

This change adds an SSP-specific DiagnosticInfo class and uses of it to the Stack Protection code. A subsequent change to clang will cause the remarks to be emitted when enabled.

Patch by: James Henderson

Differential Revision: https://reviews.llvm.org/D29023

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@294590 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/CodeGen/StackProtector.h
include/llvm/IR/DiagnosticInfo.h
lib/CodeGen/StackProtector.cpp
test/CodeGen/X86/stack-protector-remarks.ll [new file with mode: 0644]

index 1b3c0eb4a4d0ac2815086dd77d21e377eefd6dca..44044e6cf16e43daed58611d09a0691535ad8dab 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/Triple.h"
+#include "llvm/IR/DiagnosticInfo.h"
 #include "llvm/IR/Dominators.h"
 #include "llvm/IR/ValueMap.h"
 #include "llvm/Pass.h"
@@ -134,6 +135,36 @@ public:
 
   bool runOnFunction(Function &Fn) override;
 };
+
+/// Diagnostic information for why SSP was applied.
+class DiagnosticInfoSSP : public DiagnosticInfoWithDebugLocBase {
+public:
+  enum SSPReason {
+    Alloca = 0,
+    BufferOrStruct = 1,
+    AddressTaken = 2,
+    Attribute = 3,
+    LastUsedValue = 3
+  };
+
+  /// \p Fn is the function where the diagnostic is being emitted. \p Reason is
+  /// an enum value representing why the function has stack protection.
+  DiagnosticInfoSSP(const Function &Fn, SSPReason Reason)
+      : DiagnosticInfoWithDebugLocBase(DK_SSPReason, DS_Remark, Fn, DebugLoc()),
+        Func(Fn), Why(Reason) {}
+
+  static bool classof(const DiagnosticInfo *DI) {
+    return DI->getKind() == DK_SSPReason;
+  }
+
+  void print(DiagnosticPrinter &DP) const override;
+
+  SSPReason Reason() const { return Why; }
+
+private:
+  const Function &Func;
+  const SSPReason Why;
+};
 } // end namespace llvm
 
 #endif // LLVM_CODEGEN_STACKPROTECTOR_H
index 1a5770c86323270d0a97baae99bd8eb7dc2fbb43..9fc1175ea1de4aa25698295c198b7c783555ad57 100644 (file)
@@ -77,6 +77,7 @@ enum DiagnosticKind {
   DK_MIRParser,
   DK_PGOProfile,
   DK_Unsupported,
+  DK_SSPReason,
   DK_FirstPluginKind
 };
 
index c2c010a29d44b40f88957ddfa5cc57903999792d..ac747a44567603e6c8a73f56ce7971dcab5e87fd 100644 (file)
@@ -26,6 +26,7 @@
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/DebugInfo.h"
 #include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/DiagnosticPrinter.h"
 #include "llvm/IR/Function.h"
 #include "llvm/IR/GlobalValue.h"
 #include "llvm/IR/GlobalVariable.h"
@@ -51,7 +52,7 @@ static cl::opt<bool> EnableSelectionDAGSP("enable-selectiondag-sp",
 
 char StackProtector::ID = 0;
 INITIALIZE_TM_PASS(StackProtector, "stack-protector", "Insert stack protectors",
-                false, true)
+                   false, true)
 
 FunctionPass *llvm::createStackProtectorPass(const TargetMachine *TM) {
   return new StackProtector(TM);
@@ -223,6 +224,8 @@ bool StackProtector::RequiresStackProtector() {
     return false;
 
   if (F->hasFnAttribute(Attribute::StackProtectReq)) {
+    F->getContext().diagnose(
+        DiagnosticInfoSSP(*F, DiagnosticInfoSSP::SSPReason::Attribute));
     NeedsProtector = true;
     Strong = true; // Use the same heuristic as strong to determine SSPLayout
   } else if (F->hasFnAttribute(Attribute::StackProtectStrong))
@@ -241,15 +244,21 @@ bool StackProtector::RequiresStackProtector() {
               // A call to alloca with size >= SSPBufferSize requires
               // stack protectors.
               Layout.insert(std::make_pair(AI, SSPLK_LargeArray));
+              F->getContext().diagnose(
+                  DiagnosticInfoSSP(*F, DiagnosticInfoSSP::SSPReason::Alloca));
               NeedsProtector = true;
             } else if (Strong) {
               // Require protectors for all alloca calls in strong mode.
               Layout.insert(std::make_pair(AI, SSPLK_SmallArray));
+              F->getContext().diagnose(
+                  DiagnosticInfoSSP(*F, DiagnosticInfoSSP::SSPReason::Alloca));
               NeedsProtector = true;
             }
           } else {
             // A call to alloca with a variable size requires protectors.
             Layout.insert(std::make_pair(AI, SSPLK_LargeArray));
+            F->getContext().diagnose(
+                DiagnosticInfoSSP(*F, DiagnosticInfoSSP::SSPReason::Alloca));
             NeedsProtector = true;
           }
           continue;
@@ -259,6 +268,8 @@ bool StackProtector::RequiresStackProtector() {
         if (ContainsProtectableArray(AI->getAllocatedType(), IsLarge, Strong)) {
           Layout.insert(std::make_pair(AI, IsLarge ? SSPLK_LargeArray
                                                    : SSPLK_SmallArray));
+          F->getContext().diagnose(DiagnosticInfoSSP(
+              *F, DiagnosticInfoSSP::SSPReason::BufferOrStruct));
           NeedsProtector = true;
           continue;
         }
@@ -266,6 +277,8 @@ bool StackProtector::RequiresStackProtector() {
         if (Strong && HasAddressTaken(AI)) {
           ++NumAddrTaken;
           Layout.insert(std::make_pair(AI, SSPLK_AddrOf));
+          F->getContext().diagnose(DiagnosticInfoSSP(
+              *F, DiagnosticInfoSSP::SSPReason::AddressTaken));
           NeedsProtector = true;
         }
       }
@@ -464,3 +477,30 @@ BasicBlock *StackProtector::CreateFailBB() {
 bool StackProtector::shouldEmitSDCheck(const BasicBlock &BB) const {
   return HasPrologue && !HasIRCheck && dyn_cast<ReturnInst>(BB.getTerminator());
 }
+
+void DiagnosticInfoSSP::print(DiagnosticPrinter &DP) const {
+  std::string Str;
+  raw_string_ostream OS(Str);
+
+  StringRef ReasonStr;
+  switch (Reason())
+  {
+  case Alloca:
+      ReasonStr = "a call to alloca or use of a variable length array";
+      break;
+  case BufferOrStruct:
+      ReasonStr = "a stack allocated buffer or struct containing a buffer";
+      break;
+  case AddressTaken:
+      ReasonStr = "the address of a local variable being taken";
+      break;
+  case Attribute:
+      ReasonStr = "a function attribute or command-line switch";
+      break;
+  }
+
+  OS << getLocationStr() << ": SSP applied to function " << Func.getName()
+     << " due to " << ReasonStr << '\n';
+  OS.flush();
+  DP << Str;
+}
diff --git a/test/CodeGen/X86/stack-protector-remarks.ll b/test/CodeGen/X86/stack-protector-remarks.ll
new file mode 100644 (file)
index 0000000..f02384a
--- /dev/null
@@ -0,0 +1,87 @@
+; RUN: llc %s -o /dev/null 2>&1 | FileCheck %s
+; CHECK-NOT: nossp
+; CHECK-NOT: alloca_fixed_small_nossp
+; CHECK: function attribute_ssp
+; CHECK-SAME: a function attribute or command-line switch
+; CHECK: function alloca_fixed_small_ssp
+; CHECK-SAME: a call to alloca or use of a variable length array
+; CHECK: function alloca_fixed_large_ssp
+; CHECK-SAME: a call to alloca or use of a variable length array
+; CHECK: function alloca_variable_ssp
+; CHECK-SAME: a call to alloca or use of a variable length array
+; CHECK: function buffer_ssp
+; CHECK-SAME: a stack allocated buffer or struct containing a buffer
+; CHECK: function struct_ssp
+; CHECK-SAME: a stack allocated buffer or struct containing a buffer
+; CHECK: function address_ssp
+; CHECK-SAME: the address of a local variable being taken
+; CHECK: function multiple_ssp
+; CHECK-SAME: a function attribute or command-line switch
+; CHECK: function multiple_ssp
+; CHECK-SAME: a stack allocated buffer or struct containing a buffer
+; CHECK: function multiple_ssp
+; CHECK-SAME: a stack allocated buffer or struct containing a buffer
+; CHECK: function multiple_ssp
+; CHECK-SAME: the address of a local variable being taken
+; CHECK: function multiple_ssp
+; CHECK-SAME: a call to alloca or use of a variable length array
+
+define void @nossp() sspstrong {
+    ret void
+}
+
+define void @attribute_ssp() sspreq {
+  ret void
+}
+
+define void @alloca_fixed_small_nossp() ssp {
+  %1 = alloca i8, i64 2, align 16
+  ret void
+}
+
+define void @alloca_fixed_small_ssp() sspstrong {
+  %1 = alloca i8, i64 2, align 16
+  ret void
+}
+
+define void @alloca_fixed_large_ssp() ssp {
+  %1 = alloca i8, i64 64, align 16
+  ret void
+}
+
+define void @alloca_variable_ssp(i64 %x) ssp {
+  %1 = alloca i8, i64 %x, align 16
+  ret void
+}
+
+define void @buffer_ssp() sspstrong {
+  %x = alloca [64 x i32], align 16
+  ret void
+}
+
+%struct.X = type { [64 x i32] }
+define void @struct_ssp() sspstrong {
+  %x = alloca %struct.X, align 4
+  ret void
+}
+
+define void @address_ssp() sspstrong {
+entry:
+  %x = alloca i32, align 4
+  %y = alloca i32*, align 8
+  store i32 32, i32* %x, align 4
+  store i32* %x, i32** %y, align 8
+  ret void
+}
+
+define void @multiple_ssp() sspreq {
+entry:
+  %x = alloca %struct.X, align 4
+  %y = alloca [64 x i32], align 16
+  %a = alloca i32, align 4
+  %b = alloca i32*, align 8
+  %0 = alloca i8, i64 2, align 16
+  store i32 32, i32* %a, align 4
+  store i32* %a, i32** %b, align 8
+  ret void
+}