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 out-of-bounds, then any memory access that would use it + // would also have to 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.getTypeStoreSize( + 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/Generic/stack-guard-oob.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/Generic/stack-guard-oob.ll @@ -0,0 +1,338 @@ +; RUN: llc -mtriple=aarch64 -O0 < %s | FileCheck %s +; RUN: llc -mtriple=armv7a -O0 < %s | FileCheck %s +; RUN: llc -mtriple=thumbv7m -O0 < %s | FileCheck %s +; RUN: llc -mtriple=i686 -O0 < %s | FileCheck %s +; RUN: llc -mtriple=x86_64 -O0 < %s | FileCheck %s + +; 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 +} + +; CHECK-LABEL: loop_inbounds +; CHECK-NOT: __stack_chk_guard +define void @loop_inbounds(i32 %k) #0 { +entry: + %var = alloca i32, align 4 + br label %loop + +loop: + %idx = phi i32 [ %k, %entry ], [ %idx.next, %loop ] + %ptr = phi i32* [ %var, %entry ], [ %ptr.next, %loop ] + store i32 %idx, i32* %ptr, align 4 + %ptr.next = getelementptr inbounds i32, i32* %ptr, i64 0 + %idx.next = add nsw i32 %idx, -1 + %cond = icmp eq i32 %idx.next, 0 + br i1 %cond, label %exit, label %loop + +exit: + ret void +} + +; CHECK-LABEL: loop_constant_out_of_bounds +; CHECK: __stack_chk_guard +define void @loop_constant_out_of_bounds(i32 %k) #0 { +entry: + %var = alloca i32, align 4 + br label %loop + +loop: + %idx = phi i32 [ %k, %entry ], [ %idx.next, %loop ] + %ptr = phi i32* [ %var, %entry ], [ %ptr.next, %loop ] + store i32 %idx, i32* %ptr, align 4 + %ptr.next = getelementptr inbounds i32, i32* %ptr, i32 1 + %idx.next = add nsw i32 %idx, -1 + %cond = icmp eq i32 %idx.next, 0 + br i1 %cond, label %exit, label %loop + +exit: + ret void +} + +; CHECK-LABEL: loop_nonconstant_out_of_bounds +; CHECK: __stack_chk_guard +define void @loop_nonconstant_out_of_bounds(i32 %k, i32 %n) #0 { +entry: + %var = alloca i32, align 4 + br label %loop + +loop: + %idx = phi i32 [ %k, %entry ], [ %idx.next, %loop ] + %ptr = phi i32* [ %var, %entry ], [ %ptr.next, %loop ] + store i32 %idx, i32* %ptr, align 4 + %ptr.next = getelementptr inbounds i32, i32* %ptr, i32 %n + %idx.next = add nsw i32 %idx, -1 + %cond = icmp eq i32 %idx.next, 0 + br i1 %cond, label %exit, label %loop + +exit: + ret void +} + +attributes #0 = { sspstrong }