Skip to content

Commit 6552a02

Browse files
committed
[ownership] Fix a corner case in the linear lifetime checker.
Specifically, if we had a value that was consumed in the same block that it was produced in, we ignored use-after-frees in subsequent blocks. We did check for use-after-frees in the same block though. The general case was handled correctly, this was just an incorrect early exit. On master, this only found one problem (namely the one fixed in: 14d39c0). I do not expect this corner case to have more impact after cherry-picking to 5.1. rdar://49794321
1 parent d88419d commit 6552a02

File tree

2 files changed

+58
-0
lines changed

2 files changed

+58
-0
lines changed

lib/SIL/LinearLifetimeChecker.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,25 @@ LinearLifetimeError swift::valueHasLinearLifetime(
486486
// have been detected by initializing our consuming uses. So we are done.
487487
if (consumingUses.size() == 1 &&
488488
consumingUses[0].getParent() == value->getParentBlock()) {
489+
// Check if any of our non consuming uses are not in the parent block. We
490+
// flag those as additional use after frees. Any in the same block, we would
491+
// have flagged.
492+
if (llvm::any_of(nonConsumingUses, [&](BranchPropagatedUser user) {
493+
return user.getParent() != value->getParentBlock();
494+
})) {
495+
state.error.handleUseAfterFree([&] {
496+
llvm::errs() << "Function: '" << value->getFunction()->getName()
497+
<< "'\n"
498+
<< "Found use after free due to unvisited non lifetime "
499+
"ending uses?!\n"
500+
<< "Value: " << *value << " Remaining Users:\n";
501+
for (const auto &user : nonConsumingUses) {
502+
llvm::errs() << "User: " << *user.getInst();
503+
}
504+
llvm::errs() << "\n";
505+
});
506+
}
507+
489508
return state.error;
490509
}
491510

test/SIL/ownership-verifier/over_consume.sil

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ case some(T)
2222
case none
2323
}
2424

25+
protocol Error {}
26+
27+
struct NativeObjectPair {
28+
var obj1 : Builtin.NativeObject
29+
var obj2 : Builtin.NativeObject
30+
}
31+
2532
class SuperKlass {
2633
func doSomething()
2734
}
@@ -485,3 +492,35 @@ bb0(%0 : @guaranteed $ClassProtConformingRef, %1 : @owned $ClassProtConformingRe
485492
%5 = tuple(%3 : $ClassProt, %4 : $ClassProt)
486493
return %5 : $(ClassProt, ClassProt)
487494
}
495+
496+
sil [ossa] @eliminate_copy_try_apple_callee : $@convention(thin) (@owned Builtin.NativeObject) -> @error Error {
497+
entry(%0 : @owned $Builtin.NativeObject):
498+
%9999 = tuple()
499+
return %9999 : $()
500+
}
501+
502+
503+
// CHECK-LABEL: Function: 'use_after_free_consume_in_same_block'
504+
// CHECK: Found use after free due to unvisited non lifetime ending uses?!
505+
// CHECK: Value: %3 = copy_value %2 : $Builtin.NativeObject
506+
// CHECK: Remaining Users:
507+
// CHECK: User: %10 = apply %7(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
508+
sil [ossa] @use_after_free_consume_in_same_block : $@convention(thin) (@owned NativeObjectPair) -> @error Error {
509+
bb0(%0 : @owned $NativeObjectPair):
510+
%1 = begin_borrow %0 : $NativeObjectPair
511+
%2 = struct_extract %1 : $NativeObjectPair, #NativeObjectPair.obj1
512+
%3 = copy_value %2 : $Builtin.NativeObject
513+
end_borrow %1 : $NativeObjectPair
514+
destroy_value %0 : $NativeObjectPair
515+
%4 = function_ref @eliminate_copy_try_apple_callee : $@convention(thin) (@owned Builtin.NativeObject) -> @error Error
516+
%5 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
517+
try_apply %4(%3) : $@convention(thin) (@owned Builtin.NativeObject) -> @error Error, normal bb1, error bb2
518+
519+
bb1(%errorEmptyTup: $()):
520+
apply %5(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
521+
%9999 = tuple()
522+
return %9999 : $()
523+
524+
bb2(%error : @owned $Error):
525+
throw %error : $Error
526+
}

0 commit comments

Comments
 (0)