Skip to content

Commit 3e330ab

Browse files
committed
Fix use-after-free in ownership rauw
Instead of saving BorrowingOperand on the context save the SILBasicBlock and index of the terminator operand. This avoids the use-after-free in eliminateReborrowsOfRecursiveBorrows. Previously, eliminateReborrowsOfRecursiveBorrows called helper insertOwnedBaseValueAlongBranchEdge, which can delete a branch instruction (reborrow) that could have been cached in recursiveReborrows.
1 parent 3efa18f commit 3e330ab

File tree

4 files changed

+59
-16
lines changed

4 files changed

+59
-16
lines changed

include/swift/SIL/OwnershipUtils.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -472,13 +472,14 @@ struct BorrowedValue {
472472
/// reborrows, place them in BorrowingOperand form into \p
473473
/// foundReborrows. Returns true if we appended any such reborrows to
474474
/// foundReborrows... false otherwise.
475-
bool
476-
gatherReborrows(SmallVectorImpl<BorrowingOperand> &foundReborrows) const {
475+
bool gatherReborrows(SmallVectorImpl<std::pair<SILBasicBlock *, unsigned>>
476+
&foundReborrows) const {
477477
bool foundAnyReborrows = false;
478478
for (auto *op : value->getUses()) {
479479
if (auto borrowingOperand = BorrowingOperand::get(op)) {
480480
if (borrowingOperand.isReborrow()) {
481-
foundReborrows.push_back(*borrowingOperand);
481+
foundReborrows.push_back(
482+
{value->getParentBlock(), op->getOperandNumber()});
482483
foundAnyReborrows = true;
483484
}
484485
}

include/swift/SILOptimizer/Utils/OwnershipOptUtils.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ struct OwnershipFixupContext {
3939
JointPostDominanceSetComputer &jointPostDomSetComputer;
4040

4141
SmallVector<Operand *, 8> transitiveBorrowedUses;
42-
SmallVector<BorrowingOperand, 8> recursiveReborrows;
42+
SmallVector<std::pair<SILBasicBlock *, unsigned>, 8> recursiveReborrows;
4343

4444
/// Extra state initialized by OwnershipRAUWFixupHelper::get() that we use
4545
/// when RAUWing addresses. This ensures we do not need to recompute this

lib/SILOptimizer/Utils/OwnershipOptUtils.cpp

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ insertOwnedBaseValueAlongBranchEdge(BranchInst *bi, SILValue innerCopy,
100100
}
101101

102102
static bool findTransitiveBorrowedUses(
103-
SILValue value, SmallVectorImpl<Operand *> &usePoints,
104-
SmallVectorImpl<BorrowingOperand> &reborrowPoints) {
103+
SILValue value, SmallVectorImpl<Operand *> &usePoints,
104+
SmallVectorImpl<std::pair<SILBasicBlock *, unsigned>> &reborrowPoints) {
105105
assert(value.getOwnershipKind() == OwnershipKind::Guaranteed);
106106

107107
unsigned firstOffset = usePoints.size();
@@ -165,7 +165,9 @@ static bool findTransitiveBorrowedUses(
165165
[&](Operand *scopeEndingUse) {
166166
if (auto scopeEndingBorrowingOp = BorrowingOperand(scopeEndingUse)) {
167167
if (scopeEndingBorrowingOp.isReborrow()) {
168-
reborrowPoints.push_back(scopeEndingUse);
168+
auto *branch = scopeEndingUse->getUser();
169+
reborrowPoints.push_back(
170+
{branch->getParent(), scopeEndingUse->getOperandNumber()});
169171
return true;
170172
}
171173
}
@@ -430,18 +432,20 @@ OwnershipLifetimeExtender::createPlusZeroBorrow(SILValue newValue,
430432
//===----------------------------------------------------------------------===//
431433

432434
static void eliminateReborrowsOfRecursiveBorrows(
433-
ArrayRef<BorrowingOperand> transitiveReborrows,
435+
ArrayRef<std::pair<SILBasicBlock *, unsigned>> transitiveReborrows,
434436
SmallVectorImpl<Operand *> &usePoints, InstModCallbacks &callbacks) {
435437
SmallVector<std::pair<SILPhiArgument *, SILPhiArgument *>, 8>
436438
baseBorrowedValuePair;
437439
// Ok, we have transitive reborrows.
438-
for (auto borrowingOperand : transitiveReborrows) {
440+
for (auto it : transitiveReborrows) {
439441
// We eliminate the reborrow by creating a new copy+borrow at the reborrow
440442
// edge from the base value and using that for the reborrow instead of the
441443
// actual value. We of course insert an end_borrow for our original incoming
442444
// value.
445+
auto *bi = cast<BranchInst>(it.first->getTerminator());
446+
auto &op = bi->getOperandRef(it.second);
447+
BorrowingOperand borrowingOperand(&op);
443448
SILValue value = borrowingOperand->get();
444-
auto *bi = cast<BranchInst>(borrowingOperand->getUser());
445449
SILBuilderWithScope reborrowBuilder(bi);
446450
// Use an auto-generated location here, because the branch may have an
447451
// incompatible LocationKind
@@ -501,15 +505,19 @@ static void eliminateReborrowsOfRecursiveBorrows(
501505
}
502506
}
503507

504-
static void rewriteReborrows(SILValue newBorrowedValue,
505-
ArrayRef<BorrowingOperand> foundReborrows,
506-
InstModCallbacks &callbacks) {
508+
static void
509+
rewriteReborrows(SILValue newBorrowedValue,
510+
ArrayRef<std::pair<SILBasicBlock *, unsigned>> foundReborrows,
511+
InstModCallbacks &callbacks) {
507512
// Each initial reborrow that we have is a use of oldValue, so we know
508513
// that copy should be valid at the reborrow.
509514
SmallVector<std::pair<SILPhiArgument *, SILPhiArgument *>, 8>
510515
baseBorrowedValuePair;
511-
for (auto reborrow : foundReborrows) {
512-
auto *bi = cast<BranchInst>(reborrow.op->getUser());
516+
for (auto it : foundReborrows) {
517+
auto *bi = cast<BranchInst>(it.first->getTerminator());
518+
auto &op = bi->getOperandRef(it.second);
519+
BorrowingOperand reborrow(&op);
520+
513521
SILBuilderWithScope reborrowBuilder(bi);
514522
// Use an auto-generated location here, because the branch may have an
515523
// incompatible LocationKind
@@ -713,7 +721,7 @@ SILBasicBlock::iterator OwnershipRAUWUtility::handleGuaranteed() {
713721
// non-dominating copy value, allowing us to force our borrowing value to
714722
// need a base phi argument (the one of our choosing).
715723
if (auto oldValueBorrowedVal = BorrowedValue::get(oldValue)) {
716-
SmallVector<BorrowingOperand, 8> foundReborrows;
724+
SmallVector<std::pair<SILBasicBlock *, unsigned>, 8> foundReborrows;
717725
if (oldValueBorrowedVal.gatherReborrows(foundReborrows)) {
718726
rewriteReborrows(newBorrowedValue, foundReborrows, ctx.callbacks);
719727
}

test/SILOptimizer/cse_ossa_nontrivial.sil

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,3 +596,37 @@ bb8:
596596
return %res : $()
597597
}
598598

599+
struct RecurStruct {
600+
var val:NonTrivialStruct
601+
}
602+
603+
// Test to make sure we do not crash on a use after free when the branch instructions in bb1 and bb2 are deleted
604+
// CHECK-LABEL: sil [ossa] @test_useafterfreeinrauw :
605+
// CHECK: bb0
606+
// CHECK: struct_extract
607+
// CHECK-NOT: struct_extract
608+
// CHECK: cond_br
609+
// CHECK-LABEL: } // end sil function 'test_useafterfreeinrauw'
610+
sil [ossa] @test_useafterfreeinrauw : $@convention(thin) (@guaranteed RecurStruct) -> @owned NonTrivialStruct {
611+
bb0(%0 : @guaranteed $RecurStruct):
612+
%1 = struct_extract %0 : $RecurStruct, #RecurStruct.val
613+
debug_value %1 : $NonTrivialStruct
614+
%2 = struct_extract %0 : $RecurStruct, #RecurStruct.val
615+
cond_br undef, bb1, bb2
616+
617+
bb1:
618+
%3 = struct_extract %2 : $NonTrivialStruct, #NonTrivialStruct.val
619+
%borrow3 = begin_borrow %3 : $Klass
620+
br bb3(%borrow3 : $Klass)
621+
622+
bb2:
623+
%4 = struct_extract %2 : $NonTrivialStruct, #NonTrivialStruct.val
624+
%borrow4 = begin_borrow %4 : $Klass
625+
br bb3(%borrow4 : $Klass)
626+
627+
bb3(%borrow : @guaranteed $Klass):
628+
end_borrow %borrow : $Klass
629+
%copy = copy_value %1 : $NonTrivialStruct
630+
return %copy : $NonTrivialStruct
631+
}
632+

0 commit comments

Comments
 (0)