Skip to content

Commit 53c1579

Browse files
authored
[StackProtector] Fix phi handling in HasAddressTaken() (#129248)
Despite the name, the HasAddressTaken() heuristic identifies not only allocas that have their address taken, but also those that have accesses that cannot be proven to be in-bounds. However, the current handling for phi nodes is incorrect. Phi nodes are only visited once, and will perform the analysis using whichever (remaining) allocation size is passed the first time the phi node is visited. If it is later visited with a smaller remaining size, which may lead to out of bounds accesses, it will not be detected. Fix this by keeping track of the smallest seen remaining allocation size and redo the analysis if it is decreased. To avoid degenerate cases (including via loops), limit the number of allowed decreases to a small number.
1 parent ba1da5c commit 53c1579

File tree

2 files changed

+52
-8
lines changed

2 files changed

+52
-8
lines changed

llvm/lib/CodeGen/StackProtector.cpp

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -251,10 +251,21 @@ static bool ContainsProtectableArray(Type *Ty, Module *M, unsigned SSPBufferSize
251251
return NeedsProtector;
252252
}
253253

254+
/// Maximum remaining allocation size observed for a phi node, and how often
255+
/// the allocation size has already been decreased. We only allow a limited
256+
/// number of decreases.
257+
struct PhiInfo {
258+
TypeSize AllocSize;
259+
unsigned NumDecreased = 0;
260+
static constexpr unsigned MaxNumDecreased = 3;
261+
PhiInfo(TypeSize AllocSize) : AllocSize(AllocSize) {}
262+
};
263+
using PhiMap = SmallDenseMap<const PHINode *, PhiInfo, 16>;
264+
254265
/// Check whether a stack allocation has its address taken.
255266
static bool HasAddressTaken(const Instruction *AI, TypeSize AllocSize,
256267
Module *M,
257-
SmallPtrSet<const PHINode *, 16> &VisitedPHIs) {
268+
PhiMap &VisitedPHIs) {
258269
const DataLayout &DL = M->getDataLayout();
259270
for (const User *U : AI->users()) {
260271
const auto *I = cast<Instruction>(U);
@@ -325,9 +336,20 @@ static bool HasAddressTaken(const Instruction *AI, TypeSize AllocSize,
325336
// Keep track of what PHI nodes we have already visited to ensure
326337
// they are only visited once.
327338
const auto *PN = cast<PHINode>(I);
328-
if (VisitedPHIs.insert(PN).second)
329-
if (HasAddressTaken(PN, AllocSize, M, VisitedPHIs))
339+
auto [It, Inserted] = VisitedPHIs.try_emplace(PN, AllocSize);
340+
if (!Inserted) {
341+
if (TypeSize::isKnownGE(AllocSize, It->second.AllocSize))
342+
break;
343+
344+
// Check again with smaller size.
345+
if (It->second.NumDecreased == PhiInfo::MaxNumDecreased)
330346
return true;
347+
348+
It->second.AllocSize = AllocSize;
349+
++It->second.NumDecreased;
350+
}
351+
if (HasAddressTaken(PN, AllocSize, M, VisitedPHIs))
352+
return true;
331353
break;
332354
}
333355
case Instruction::Load:
@@ -377,7 +399,7 @@ bool SSPLayoutAnalysis::requiresStackProtector(Function *F,
377399
// The set of PHI nodes visited when determining if a variable's reference has
378400
// been taken. This set is maintained to ensure we don't visit the same PHI
379401
// node multiple times.
380-
SmallPtrSet<const PHINode *, 16> VisitedPHIs;
402+
PhiMap VisitedPHIs;
381403

382404
unsigned SSPBufferSize = F->getFnAttributeAsParsedInteger(
383405
"stack-protector-buffer-size", SSPLayoutInfo::DefaultSSPBufferSize);

llvm/test/CodeGen/X86/stack-protector-phi.ll

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,29 @@
44
define void @test_phi_diff_size(i1 %c) sspstrong {
55
; CHECK-LABEL: test_phi_diff_size:
66
; CHECK: # %bb.0: # %entry
7+
; CHECK-NEXT: subq $24, %rsp
8+
; CHECK-NEXT: .cfi_def_cfa_offset 32
9+
; CHECK-NEXT: movq %fs:40, %rax
10+
; CHECK-NEXT: movq %rax, {{[0-9]+}}(%rsp)
711
; CHECK-NEXT: testb $1, %dil
812
; CHECK-NEXT: je .LBB0_1
913
; CHECK-NEXT: # %bb.2: # %if
10-
; CHECK-NEXT: leaq -{{[0-9]+}}(%rsp), %rax
11-
; CHECK-NEXT: movq $0, (%rax)
12-
; CHECK-NEXT: retq
14+
; CHECK-NEXT: leaq {{[0-9]+}}(%rsp), %rax
15+
; CHECK-NEXT: jmp .LBB0_3
1316
; CHECK-NEXT: .LBB0_1:
14-
; CHECK-NEXT: leaq -{{[0-9]+}}(%rsp), %rax
17+
; CHECK-NEXT: leaq {{[0-9]+}}(%rsp), %rax
18+
; CHECK-NEXT: .LBB0_3: # %join
1519
; CHECK-NEXT: movq $0, (%rax)
20+
; CHECK-NEXT: movq %fs:40, %rax
21+
; CHECK-NEXT: cmpq {{[0-9]+}}(%rsp), %rax
22+
; CHECK-NEXT: jne .LBB0_5
23+
; CHECK-NEXT: # %bb.4: # %SP_return
24+
; CHECK-NEXT: addq $24, %rsp
25+
; CHECK-NEXT: .cfi_def_cfa_offset 8
1626
; CHECK-NEXT: retq
27+
; CHECK-NEXT: .LBB0_5: # %CallStackCheckFailBlk
28+
; CHECK-NEXT: .cfi_def_cfa_offset 32
29+
; CHECK-NEXT: callq __stack_chk_fail@PLT
1730
entry:
1831
%a = alloca i64
1932
br i1 %c, label %if, label %join
@@ -38,6 +51,8 @@ define void @test_phi_loop(i1 %c) sspstrong {
3851
; CHECK-NEXT: .cfi_def_cfa_register %rbp
3952
; CHECK-NEXT: andq $-131072, %rsp # imm = 0xFFFE0000
4053
; CHECK-NEXT: subq $262144, %rsp # imm = 0x40000
54+
; CHECK-NEXT: movq %fs:40, %rax
55+
; CHECK-NEXT: movq %rax, {{[0-9]+}}(%rsp)
4156
; CHECK-NEXT: movq %rsp, %rax
4257
; CHECK-NEXT: .p2align 4
4358
; CHECK-NEXT: .LBB1_1: # %loop
@@ -47,10 +62,17 @@ define void @test_phi_loop(i1 %c) sspstrong {
4762
; CHECK-NEXT: testb $1, %dil
4863
; CHECK-NEXT: jne .LBB1_1
4964
; CHECK-NEXT: # %bb.2: # %exit
65+
; CHECK-NEXT: movq %fs:40, %rax
66+
; CHECK-NEXT: cmpq {{[0-9]+}}(%rsp), %rax
67+
; CHECK-NEXT: jne .LBB1_4
68+
; CHECK-NEXT: # %bb.3: # %SP_return
5069
; CHECK-NEXT: movq %rbp, %rsp
5170
; CHECK-NEXT: popq %rbp
5271
; CHECK-NEXT: .cfi_def_cfa %rsp, 8
5372
; CHECK-NEXT: retq
73+
; CHECK-NEXT: .LBB1_4: # %CallStackCheckFailBlk
74+
; CHECK-NEXT: .cfi_def_cfa %rbp, 16
75+
; CHECK-NEXT: callq __stack_chk_fail@PLT
5476
entry:
5577
%a = alloca <10000 x i64>
5678
br label %loop

0 commit comments

Comments
 (0)