Index: llvm/lib/CodeGen/StackProtector.cpp =================================================================== --- llvm/lib/CodeGen/StackProtector.cpp +++ llvm/lib/CodeGen/StackProtector.cpp @@ -189,8 +189,22 @@ } case Instruction::Invoke: return true; + case Instruction::GetElementPtr: { + // If the GEP offset is out-of-bounds, or is non-constant and so has to be + // assumed to be potentially out-of-bounds, then any memory access that + // would use it could also be out-of-bounds meaning stack protection is + // required. + const GetElementPtrInst *GEP = cast(I); + const DataLayout &DL = M->getDataLayout(); + unsigned TypeSize = DL.getIndexTypeSizeInBits(I->getType()); + APInt Offset(TypeSize, 0); + APInt MaxOffset(TypeSize, DL.getTypeAllocSize( + AI->getType()->getPointerElementType())); + if (!GEP->accumulateConstantOffset(DL, Offset) || Offset.uge(MaxOffset)) + return true; + } + LLVM_FALLTHROUGH; case Instruction::BitCast: - case Instruction::GetElementPtr: case Instruction::Select: case Instruction::AddrSpaceCast: if (HasAddressTaken(I)) Index: llvm/test/CodeGen/AArch64/stack-guard-oob.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/AArch64/stack-guard-oob.ll @@ -0,0 +1 @@ +; RUN: llc -mtriple=aarch64 -O0 < %S/../Inputs/stack-guard-oob.ll | FileCheck %S/../Inputs/stack-guard-oob.ll Index: llvm/test/CodeGen/ARM/stack-guard-oob.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/ARM/stack-guard-oob.ll @@ -0,0 +1,2 @@ +; RUN: llc -mtriple=armv7a -O0 < %S/../Inputs/stack-guard-oob.ll | FileCheck %S/../Inputs/stack-guard-oob.ll +; RUN: llc -mtriple=thumbv7m -O0 < %S/../Inputs/stack-guard-oob.ll | FileCheck %S/../Inputs/stack-guard-oob.ll Index: llvm/test/CodeGen/Inputs/stack-guard-oob.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/Inputs/stack-guard-oob.ll @@ -0,0 +1,272 @@ +; CHECK-LABEL: in_bounds: +; CHECK-NOT: __stack_chk_guard +define i32 @in_bounds() #0 { + %var = alloca i32, align 4 + store i32 0, i32* %var, align 4 + %gep = getelementptr inbounds i32, i32* %var, i32 0 + %ret = load i32, i32* %gep, align 4 + ret i32 %ret +} + +; CHECK-LABEL: constant_out_of_bounds: +; CHECK: __stack_chk_guard +define i32 @constant_out_of_bounds() #0 { + %var = alloca i32, align 4 + store i32 0, i32* %var, align 4 + %gep = getelementptr inbounds i32, i32* %var, i32 1 + %ret = load i32, i32* %gep, align 4 + ret i32 %ret +} + +; CHECK-LABEL: nonconstant_out_of_bounds: +; CHECK: __stack_chk_guard +define i32 @nonconstant_out_of_bounds(i32 %n) #0 { + %var = alloca i32, align 4 + store i32 0, i32* %var, align 4 + %gep = getelementptr inbounds i32, i32* %var, i32 %n + %ret = load i32, i32* %gep, align 4 + ret i32 %ret +} + +; CHECK-LABEL: phi_before_gep_in_bounds: +; CHECK-NOT: __stack_chk_guard +define i32 @phi_before_gep_in_bounds(i32 %k) #0 { +entry: + %var1 = alloca i32, align 4 + %var2 = alloca i32, align 4 + store i32 0, i32* %var1, align 4 + store i32 0, i32* %var2, align 4 + %cmp = icmp ne i32 %k, 0 + br i1 %cmp, label %if, label %then + +if: + br label %then + +then: + %ptr = phi i32* [ %var1, %entry ], [ %var2, %if ] + %gep = getelementptr inbounds i32, i32* %ptr, i32 0 + %ret = load i32, i32* %gep, align 4 + ret i32 %ret +} + +; CHECK-LABEL: phi_before_gep_constant_out_of_bounds: +; CHECK: __stack_chk_guard +define i32 @phi_before_gep_constant_out_of_bounds(i32 %k) #0 { +entry: + %var1 = alloca i32, align 4 + %var2 = alloca i32, align 4 + store i32 0, i32* %var1, align 4 + store i32 0, i32* %var2, align 4 + %cmp = icmp ne i32 %k, 0 + br i1 %cmp, label %if, label %then + +if: + br label %then + +then: + %ptr = phi i32* [ %var1, %entry ], [ %var2, %if ] + %gep = getelementptr inbounds i32, i32* %ptr, i32 1 + %ret = load i32, i32* %gep, align 4 + ret i32 %ret +} + +; CHECK-LABEL: phi_before_gep_nonconstant_out_of_bounds: +; CHECK: __stack_chk_guard +define i32 @phi_before_gep_nonconstant_out_of_bounds(i32 %k, i32 %n) #0 { +entry: + %var1 = alloca i32, align 4 + %var2 = alloca i32, align 4 + store i32 0, i32* %var1, align 4 + store i32 0, i32* %var2, align 4 + %cmp = icmp ne i32 %k, 0 + br i1 %cmp, label %if, label %then + +if: + br label %then + +then: + %ptr = phi i32* [ %var1, %entry ], [ %var2, %if ] + %gep = getelementptr inbounds i32, i32* %ptr, i32 %n + %ret = load i32, i32* %gep, align 4 + ret i32 %ret +} + +; CHECK-LABEL: phi_after_gep_in_bounds: +; CHECK-NOT: __stack_chk_guard +define i32 @phi_after_gep_in_bounds(i32 %k) #0 { +entry: + %var1 = alloca i32, align 4 + %var2 = alloca i32, align 4 + store i32 0, i32* %var1, align 4 + store i32 0, i32* %var2, align 4 + %cmp = icmp ne i32 %k, 0 + br i1 %cmp, label %if, label %else + +if: + %gep1 = getelementptr inbounds i32, i32* %var1, i32 0 + br label %then + +else: + %gep2 = getelementptr inbounds i32, i32* %var2, i32 0 + br label %then + +then: + %ptr = phi i32* [ %gep1, %if ], [ %gep2, %else ] + %ret = load i32, i32* %ptr, align 4 + ret i32 %ret +} + +; CHECK-LABEL: phi_after_gep_constant_out_of_bounds_a: +; CHECK: __stack_chk_guard +define i32 @phi_after_gep_constant_out_of_bounds_a(i32 %k) #0 { +entry: + %var1 = alloca i32, align 4 + %var2 = alloca i32, align 4 + store i32 0, i32* %var1, align 4 + store i32 0, i32* %var2, align 4 + %cmp = icmp ne i32 %k, 0 + br i1 %cmp, label %if, label %else + +if: + %gep1 = getelementptr inbounds i32, i32* %var1, i32 0 + br label %then + +else: + %gep2 = getelementptr inbounds i32, i32* %var2, i32 1 + br label %then + +then: + %ptr = phi i32* [ %gep1, %if ], [ %gep2, %else ] + %ret = load i32, i32* %ptr, align 4 + ret i32 %ret +} + +; CHECK-LABEL: phi_after_gep_constant_out_of_bounds_b: +; CHECK: __stack_chk_guard +define i32 @phi_after_gep_constant_out_of_bounds_b(i32 %k) #0 { +entry: + %var1 = alloca i32, align 4 + %var2 = alloca i32, align 4 + store i32 0, i32* %var1, align 4 + store i32 0, i32* %var2, align 4 + %cmp = icmp ne i32 %k, 0 + br i1 %cmp, label %if, label %else + +if: + %gep1 = getelementptr inbounds i32, i32* %var1, i32 1 + br label %then + +else: + %gep2 = getelementptr inbounds i32, i32* %var2, i32 0 + br label %then + +then: + %ptr = phi i32* [ %gep1, %if ], [ %gep2, %else ] + %ret = load i32, i32* %ptr, align 4 + ret i32 %ret +} + +; CHECK-LABEL: phi_after_gep_nonconstant_out_of_bounds_a: +; CHECK: __stack_chk_guard +define i32 @phi_after_gep_nonconstant_out_of_bounds_a(i32 %k, i32 %n) #0 { +entry: + %var1 = alloca i32, align 4 + %var2 = alloca i32, align 4 + store i32 0, i32* %var1, align 4 + store i32 0, i32* %var2, align 4 + %cmp = icmp ne i32 %k, 0 + br i1 %cmp, label %if, label %else + +if: + %gep1 = getelementptr inbounds i32, i32* %var1, i32 0 + br label %then + +else: + %gep2 = getelementptr inbounds i32, i32* %var2, i32 %n + br label %then + +then: + %ptr = phi i32* [ %gep1, %if ], [ %gep2, %else ] + %ret = load i32, i32* %ptr, align 4 + ret i32 %ret +} + +; CHECK-LABEL: phi_after_gep_nonconstant_out_of_bounds_b: +; CHECK: __stack_chk_guard +define i32 @phi_after_gep_nonconstant_out_of_bounds_b(i32 %k, i32 %n) #0 { +entry: + %var1 = alloca i32, align 4 + %var2 = alloca i32, align 4 + store i32 0, i32* %var1, align 4 + store i32 0, i32* %var2, align 4 + %cmp = icmp ne i32 %k, 0 + br i1 %cmp, label %if, label %else + +if: + %gep1 = getelementptr inbounds i32, i32* %var1, i32 %n + br label %then + +else: + %gep2 = getelementptr inbounds i32, i32* %var2, i32 0 + br label %then + +then: + %ptr = phi i32* [ %gep1, %if ], [ %gep2, %else ] + %ret = load i32, i32* %ptr, align 4 + ret i32 %ret +} + +%struct.outer = type { %struct.inner, %struct.inner } +%struct.inner = type { i32, i32 } + +; CHECK-LABEL: struct_in_bounds: +; CHECK-NOT: __stack_chk_guard +define void @struct_in_bounds() #0 { + %var = alloca %struct.outer, align 4 + %outergep = getelementptr inbounds %struct.outer, %struct.outer* %var, i32 0, i32 1 + %innergep = getelementptr inbounds %struct.inner, %struct.inner* %outergep, i32 0, i32 1 + store i32 0, i32* %innergep, align 4 + ret void +} + +; CHECK-LABEL: struct_constant_out_of_bounds_a: +; CHECK: __stack_chk_guard +define void @struct_constant_out_of_bounds_a() #0 { + %var = alloca %struct.outer, align 4 + %outergep = getelementptr inbounds %struct.outer, %struct.outer* %var, i32 1, i32 0 + %innergep = getelementptr inbounds %struct.inner, %struct.inner* %outergep, i32 0, i32 0 + store i32 0, i32* %innergep, align 4 + ret void +} + +; CHECK-LABEL: struct_constant_out_of_bounds_b: +; CHECK: __stack_chk_guard +define void @struct_constant_out_of_bounds_b() #0 { + %var = alloca %struct.outer, align 4 + %outergep = getelementptr inbounds %struct.outer, %struct.outer* %var, i32 0, i32 0 + %innergep = getelementptr inbounds %struct.inner, %struct.inner* %outergep, i32 1, i32 0 + store i32 0, i32* %innergep, align 4 + ret void +} + +; CHECK-LABEL: struct_nonconstant_out_of_bounds_a: +; CHECK: __stack_chk_guard +define void @struct_nonconstant_out_of_bounds_a(i32 %n) #0 { + %var = alloca %struct.outer, align 4 + %outergep = getelementptr inbounds %struct.outer, %struct.outer* %var, i32 %n, i32 0 + %innergep = getelementptr inbounds %struct.inner, %struct.inner* %outergep, i32 0, i32 0 + store i32 0, i32* %innergep, align 4 + ret void +} + +; CHECK-LABEL: struct_nonconstant_out_of_bounds_b: +; CHECK: __stack_chk_guard +define void @struct_nonconstant_out_of_bounds_b(i32 %n) #0 { + %var = alloca %struct.outer, align 4 + %outergep = getelementptr inbounds %struct.outer, %struct.outer* %var, i32 0, i32 0 + %innergep = getelementptr inbounds %struct.inner, %struct.inner* %outergep, i32 %n, i32 0 + store i32 0, i32* %innergep, align 4 + ret void +} + +attributes #0 = { sspstrong } Index: llvm/test/CodeGen/X86/stack-guard-oob.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/X86/stack-guard-oob.ll @@ -0,0 +1,2 @@ +; RUN: llc -mtriple=i686 -O0 < %S/../Inputs/stack-guard-oob.ll | FileCheck %S/../Inputs/stack-guard-oob.ll +; RUN: llc -mtriple=x86_64 -O0 < %S/../Inputs/stack-guard-oob.ll | FileCheck %S/../Inputs/stack-guard-oob.ll