Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stack clash - mir attempt #42

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions clang/docs/ClangCommandLineReference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1900,6 +1900,10 @@ Use a strong heuristic to apply stack protectors to functions

Emit section containing metadata on function stack sizes

.. option:: -fstack-clash-protection

Instrument stack allocation to prevent stack clash attacks (x86, non-Windows only).

.. option:: -fstandalone-debug, -fno-limit-debug-info, -fno-standalone-debug

Emit full debug info for all types used by the program
Expand Down
4 changes: 4 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ New Compiler Flags
You can also force vzeroupper insertion to be used on CPUs that normally
wouldn't with -mvzeroupper.

- -fstack-clash-protection will provide a protection against the stack clash
attack for x86 architecture through automatic probing of each page of
allocated stack.

Deprecated Compiler Flags
-------------------------

Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/CodeGenOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ CODEGENOPT(NoWarn , 1, 0) ///< Set when -Wa,--no-warn is enabled.
CODEGENOPT(EnableSegmentedStacks , 1, 0) ///< Set when -fsplit-stack is enabled.
CODEGENOPT(NoInlineLineTables, 1, 0) ///< Whether debug info should contain
///< inline line tables.
CODEGENOPT(StackClashProtector, 1, 0) ///< Set when -fstack-clash-protection is enabled.
CODEGENOPT(NoImplicitFloat , 1, 0) ///< Set when -mno-implicit-float is enabled.
CODEGENOPT(NoInfsFPMath , 1, 0) ///< Assume FP arguments, results not +-Inf.
CODEGENOPT(NoSignedZeros , 1, 0) ///< Allow ignoring the signedness of FP zero
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticFrontendKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ def err_fe_error_backend : Error<"error in backend: %0">, DefaultFatal;

def err_fe_inline_asm : Error<"%0">, CatInlineAsm;
def warn_fe_inline_asm : Warning<"%0">, CatInlineAsm, InGroup<BackendInlineAsm>;
def warn_fe_stack_clash_protection_inline_asm : Warning<
"Unable to protect inline asm that clobbers stack pointer against stack clash">,
CatInlineAsm, InGroup<BackendInlineAsm>;
def note_fe_inline_asm : Note<"%0">, CatInlineAsm;
def note_fe_inline_asm_here : Note<"instantiated into assembly here">;
def err_fe_cannot_link_module : Error<"cannot link module '%0': %1">,
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/TargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,8 @@ class TargetInfo : public virtual TransferrableTargetInfo,
StringRef getNormalizedGCCRegisterName(StringRef Name,
bool ReturnCanonical = false) const;

virtual const char *getSPRegName() const { return nullptr; }

/// Extracts a register from the passed constraint (if it is a
/// single-register constraint) and the asm label expression related to a
/// variable in the input or output list of an inline asm statement.
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Driver/CC1Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,8 @@ def stack_protector : Separate<["-"], "stack-protector">,
HelpText<"Enable stack protectors">;
def stack_protector_buffer_size : Separate<["-"], "stack-protector-buffer-size">,
HelpText<"Lower bound for a buffer to be considered for stack protection">;
def stack_clash_protection : Separate<["-"], "stack-clash-protection">,
HelpText<"Enable stack clash protection">;
def fvisibility : Separate<["-"], "fvisibility">,
HelpText<"Default type and symbol visibility">;
def ftype_visibility : Separate<["-"], "ftype-visibility">,
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -1746,6 +1746,8 @@ def fno_signed_char : Flag<["-"], "fno-signed-char">, Group<f_Group>,
def fsplit_stack : Flag<["-"], "fsplit-stack">, Group<f_Group>;
def fstack_protector_all : Flag<["-"], "fstack-protector-all">, Group<f_Group>,
HelpText<"Enable stack protectors for all functions">;
def fstack_clash_protection : Flag<["-"], "fstack-clash-protection">, Group<f_Group>,
HelpText<"Enable stack clash protection">;
def fstack_protector_strong : Flag<["-"], "fstack-protector-strong">, Group<f_Group>,
HelpText<"Enable stack protectors for some functions vulnerable to stack smashing. "
"Compared to -fstack-protector, this uses a stronger heuristic "
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Basic/Targets/X86.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ class LLVM_LIBRARY_VISIBILITY X86TargetInfo : public TargetInfo {

ArrayRef<TargetInfo::AddlRegName> getGCCAddlRegNames() const override;

const char *getSPRegName() const override { return "rsp"; }

bool validateCpuSupports(StringRef Name) const override;

bool validateCpuIs(StringRef Name) const override;
Expand Down
12 changes: 10 additions & 2 deletions clang/lib/CodeGen/CGStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@
//
//===----------------------------------------------------------------------===//

#include "CodeGenFunction.h"
#include "CGDebugInfo.h"
#include "CodeGenFunction.h"
#include "CodeGenModule.h"
#include "TargetInfo.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/PrettyStackTrace.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Driver/DriverDiagnostic.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/InlineAsm.h"
Expand Down Expand Up @@ -2244,8 +2246,14 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {

if (Clobber == "memory")
ReadOnly = ReadNone = false;
else if (Clobber != "cc")
else if (Clobber != "cc") {
Clobber = getTarget().getNormalizedGCCRegisterName(Clobber);
if (CGM.getCodeGenOpts().StackClashProtector &&
Clobber == getTarget().getSPRegName()) {
CGM.getDiags().Report(S.getAsmLoc(),
diag::warn_fe_stack_clash_protection_inline_asm);
}
}

if (!Constraints.empty())
Constraints += ',';
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1485,6 +1485,9 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
if (CodeGenOpts.UnwindTables)
B.addAttribute(llvm::Attribute::UWTable);

if (CodeGenOpts.StackClashProtector)
B.addAttribute("probe-stack", "inline-asm");

if (!hasUnwindExceptions(LangOpts))
B.addAttribute(llvm::Attribute::NoUnwind);

Expand Down
28 changes: 28 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2563,6 +2563,33 @@ static void RenderSSPOptions(const ToolChain &TC, const ArgList &Args,
}
}

static void RenderSCPOptions(const ToolChain &TC, const ArgList &Args,
ArgStringList &CmdArgs) {
const llvm::Triple &EffectiveTriple = TC.getEffectiveTriple();

if (!EffectiveTriple.isOSLinux())
return;

switch (EffectiveTriple.getArch()) {
default:
return;
case llvm::Triple::ArchType::x86:
case llvm::Triple::ArchType::x86_64:
break;
}

for (const Arg *A : Args) {
switch (A->getOption().getID()) {
default:
continue;
case options::OPT_fstack_clash_protection: {
A->claim();
CmdArgs.push_back("-stack-clash-protection");
}
}
}
}

static void RenderTrivialAutoVarInitOptions(const Driver &D,
const ToolChain &TC,
const ArgList &Args,
Expand Down Expand Up @@ -4723,6 +4750,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back(Args.MakeArgString("-mspeculative-load-hardening"));

RenderSSPOptions(TC, Args, CmdArgs, KernelOrKext);
RenderSCPOptions(TC, Args, CmdArgs);
RenderTrivialAutoVarInitOptions(D, TC, Args, CmdArgs);

// Translate -mstackrealign
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1221,6 +1221,8 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,

Opts.NoStackArgProbe = Args.hasArg(OPT_mno_stack_arg_probe);

Opts.StackClashProtector = Args.hasArg(OPT_stack_clash_protection);

if (Arg *A = Args.getLastArg(OPT_fobjc_dispatch_method_EQ)) {
StringRef Name = A->getValue();
unsigned Method = llvm::StringSwitch<unsigned>(Name)
Expand Down
34 changes: 34 additions & 0 deletions clang/test/CodeGen/stack-clash-protection.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// check interaction between -fstack-clash-protection and dynamic allocation schemes
// RUN: %clang -target x86_64 -O0 -o %t.out %s -fstack-clash-protection && %t.out

#include <alloca.h>
#include <string.h>

int large_stack() __attribute__((noinline));

int large_stack() {
int stack[20000], i;
for (i = 0; i < sizeof(stack) / sizeof(int); ++i)
stack[i] = i;
return stack[1];
}

int main(int argc, char **argv) {
int volatile static_mem[8000];
for (size_t i = 0; i < argc * sizeof(static_mem) / sizeof(static_mem[0]); ++i)
static_mem[i] = argc * i;

int vla[argc];
memset(&vla[0], 0, argc);

int index = large_stack();

// also check allocation of 0 size
volatile void *mem = __builtin_alloca(argc - 1);

int volatile *dyn_mem = alloca(sizeof(static_mem) * argc);
for (size_t i = 0; i < argc * sizeof(static_mem) / sizeof(static_mem[0]); ++i)
dyn_mem[i] = argc * i;

return static_mem[(7999 * argc) / 2] - dyn_mem[(7999 * argc) / 2] + vla[argc - index];
}
30 changes: 30 additions & 0 deletions clang/test/Driver/stack-clash-protection.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// RUN: %clang -target i386-unknown-linux -fstack-clash-protection -### %s 2>&1 | FileCheck %s -check-prefix=SCP-i386
// SCP-i386: "-stack-clash-protection"

// RUN: %clang -target x86_64-scei-linux -fstack-clash-protection -### %s 2>&1 | FileCheck %s -check-prefix=SCP-x86
// SCP-x86: "-stack-clash-protection"

// RUN: %clang -target armv7k-apple-linux -fstack-clash-protection -### %s 2>&1 | FileCheck %s -check-prefix=SCP-armv7
// SCP-armv7-NOT: "-stack-clash-protection"
// SCP-armv7: argument unused during compilation: '-fstack-clash-protection'

// RUN: %clang -target x86_64-unknown-linux -fstack-clash-protection -c %s 2>&1 | FileCheck %s -check-prefix=SCP-warn
// SCP-warn: warning: Unable to protect inline asm that clobbers stack pointer against stack clash

// RUN: %clang -target x86_64-pc-unknown-linux -fstack-clash-protection -S -emit-llvm -o- %s | FileCheck %s -check-prefix=SCP-ll-linux64
// SCP-ll-linux64: attributes {{.*}} "probe-stack"="inline-asm"

// RUN: %clang -target x86_64-pc-windows-msvc -fstack-clash-protection -S -emit-llvm -o- %s 2>&1 | FileCheck %s -check-prefix=SCP-ll-win64
// SCP-ll-win64-NOT: attributes {{.*}} "probe-stack"="inline-asm"
// SCP-ll-win64: argument unused during compilation: '-fstack-clash-protection'

int foo(int c) {
int r;
__asm__("sub %0, %%rsp"
:
: "rm"(c)
: "rsp");
__asm__("mov %%rsp, %0"
: "=rm"(r)::);
return r;
}
4 changes: 4 additions & 0 deletions llvm/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ Changes to the X86 Target
feature flag which has the opposite polarity. So -vzeroupper has the same
effect as +fast-partial-ymm-or-zmm-write.

* Functions with the probe-stack attribute set to "inline-asm" are now protected
against stack clash without the need of a third-party probing function and
with limited impact on performance.

Changes to the AMDGPU Target
-----------------------------

Expand Down
4 changes: 4 additions & 0 deletions llvm/include/llvm/CodeGen/TargetLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -1621,6 +1621,10 @@ class TargetLoweringBase {

/// Returns the name of the symbol used to emit stack probes or the empty
/// string if not applicable.
virtual bool hasStackProbeSymbol(MachineFunction &MF) const { return false; }

virtual bool hasInlineStackProbe(MachineFunction &MF) const { return false; }

virtual StringRef getStackProbeSymbolName(MachineFunction &MF) const {
return "";
}
Expand Down
5 changes: 2 additions & 3 deletions llvm/lib/Target/X86/X86CallFrameOptimization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,14 +162,13 @@ bool X86CallFrameOptimization::isLegal(MachineFunction &MF) {
// memory for arguments.
unsigned FrameSetupOpcode = TII->getCallFrameSetupOpcode();
unsigned FrameDestroyOpcode = TII->getCallFrameDestroyOpcode();
bool UseStackProbe =
!STI->getTargetLowering()->getStackProbeSymbolName(MF).empty();
bool EmitStackProbeCall = STI->getTargetLowering()->hasStackProbeSymbol(MF);
unsigned StackProbeSize = STI->getTargetLowering()->getStackProbeSize(MF);
for (MachineBasicBlock &BB : MF) {
bool InsideFrameSequence = false;
for (MachineInstr &MI : BB) {
if (MI.getOpcode() == FrameSetupOpcode) {
if (TII->getFrameSize(MI) >= StackProbeSize && UseStackProbe)
if (TII->getFrameSize(MI) >= StackProbeSize && EmitStackProbeCall)
return false;
if (InsideFrameSequence)
return false;
Expand Down
Loading