Skip to content

Commit 776c8ed

Browse files
authored
Merge pull request #37127 from eeckstein/fix-deadobject-elim
DeadObjectAllocation: fix a use-after free crash
2 parents bf6c673 + 5a76154 commit 776c8ed

File tree

2 files changed

+73
-23
lines changed

2 files changed

+73
-23
lines changed

lib/SILOptimizer/Transforms/DeadObjectElimination.cpp

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -784,20 +784,35 @@ static bool isAllocatingApply(SILInstruction *Inst) {
784784
namespace {
785785
class DeadObjectElimination : public SILFunctionTransform {
786786
llvm::DenseMap<SILType, bool> DestructorAnalysisCache;
787-
llvm::SmallVector<SILInstruction*, 16> Allocations;
788787

789-
void collectAllocations(SILFunction &Fn) {
790-
for (auto &BB : Fn) {
791-
for (auto &II : BB) {
792-
if (isa<AllocationInst>(&II) ||
793-
isAllocatingApply(&II) ||
794-
isa<KeyPathInst>(&II)) {
795-
Allocations.push_back(&II);
796-
}
797-
}
788+
// This is not a SILBasicBlock::iterator because we need a null value and we
789+
// don't have a specific block whose end() we could use.
790+
SILInstruction *nextInstToProcess = nullptr;
791+
792+
/// Advance nextInstToProcess to the next instruction in its block.
793+
/// If nextInstToProcess is the last instruction in its block, it is set to
794+
/// null.
795+
void advance() {
796+
if (!nextInstToProcess)
797+
return;
798+
SILBasicBlock *block = nextInstToProcess->getParent();
799+
auto nextIter = std::next(nextInstToProcess->getIterator());
800+
if (nextIter == block->end()) {
801+
nextInstToProcess = nullptr;
802+
} else {
803+
nextInstToProcess = &*nextIter;
804+
}
805+
}
806+
807+
void handleDeleteNotification(SILNode *node) override {
808+
if (auto *inst = dyn_cast<SILInstruction>(node)) {
809+
if (inst == nextInstToProcess)
810+
advance();
798811
}
799812
}
800813

814+
bool needsNotifications() override { return true; }
815+
801816
bool processAllocRef(AllocRefInst *ARI);
802817
bool processAllocStack(AllocStackInst *ASI);
803818
bool processKeyPath(KeyPathInst *KPI);
@@ -806,21 +821,30 @@ class DeadObjectElimination : public SILFunctionTransform {
806821

807822
bool processFunction(SILFunction &Fn) {
808823
DeadEndBlocks DEBlocks(&Fn);
809-
Allocations.clear();
810824
DestructorAnalysisCache.clear();
825+
811826
bool Changed = false;
812-
collectAllocations(Fn);
813-
for (auto *II : Allocations) {
814-
if (auto *A = dyn_cast<AllocRefInst>(II))
815-
Changed |= processAllocRef(A);
816-
else if (auto *A = dyn_cast<AllocStackInst>(II))
817-
Changed |= processAllocStack(A);
818-
else if (auto *KPI = dyn_cast<KeyPathInst>(II))
819-
Changed |= processKeyPath(KPI);
820-
else if (auto *A = dyn_cast<AllocBoxInst>(II))
821-
Changed |= processAllocBox(A);
822-
else if (auto *A = dyn_cast<ApplyInst>(II))
823-
Changed |= processAllocApply(A, DEBlocks);
827+
828+
for (auto &BB : Fn) {
829+
nextInstToProcess = &BB.front();
830+
831+
// We cannot just iterate over the instructions, because the processing
832+
// functions might deleted instruction before or after the current
833+
// instruction - and also inst itself.
834+
while (SILInstruction *inst = nextInstToProcess) {
835+
advance();
836+
837+
if (auto *A = dyn_cast<AllocRefInst>(inst))
838+
Changed |= processAllocRef(A);
839+
else if (auto *A = dyn_cast<AllocStackInst>(inst))
840+
Changed |= processAllocStack(A);
841+
else if (auto *KPI = dyn_cast<KeyPathInst>(inst))
842+
Changed |= processKeyPath(KPI);
843+
else if (auto *A = dyn_cast<AllocBoxInst>(inst))
844+
Changed |= processAllocBox(A);
845+
else if (auto *A = dyn_cast<ApplyInst>(inst))
846+
Changed |= processAllocApply(A, DEBlocks);
847+
}
824848
}
825849
return Changed;
826850
}

test/SILOptimizer/dead_alloc_elim.sil

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,3 +644,29 @@ bb3:
644644
return %10 : $()
645645
}
646646

647+
struct II { }
648+
649+
struct I {
650+
@_hasStorage public let x: II { get }
651+
}
652+
653+
struct S2 { }
654+
655+
sil @getter : $@convention(method) (@guaranteed KeyPath<I, II>, S2) -> @out II
656+
sil @kpgetter : $@convention(thin) (@in_guaranteed S2, UnsafeRawPointer) -> @out II
657+
sil @equ : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool
658+
sil @hsh : $@convention(thin) (UnsafeRawPointer) -> Int
659+
660+
661+
// CHECK-LABEL: sil @remove_dead_keypath
662+
// CHECK-NOT: keypath
663+
// CHECK: } // end sil function 'remove_dead_keypath'
664+
sil @remove_dead_keypath: $@convention(thin) () -> () {
665+
bb0:
666+
%0 = keypath $KeyPath<I, II>, (root $I; stored_property #I.x : $II)
667+
%1 = keypath $KeyPath<S2, II>, (root $S2; gettable_property $II, id @getter : $@convention(method) (@guaranteed KeyPath<I, II>, S2) -> @out II, getter @kpgetter: $@convention(thin) (@in_guaranteed S2, UnsafeRawPointer) -> @out II, indices [%$0 : $KeyPath<I, II> : $KeyPath<I, II>], indices_equals @equ : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @hsh : $@convention(thin) (UnsafeRawPointer) -> Int) (%0)
668+
669+
%r = tuple ()
670+
return %r : $()
671+
}
672+

0 commit comments

Comments
 (0)