Skip to content

Commit 8c197eb

Browse files
author
Gabor Horvath
committed
[cxx-interop] Delay lowering unowned convention until ownership elimination
Unowned result conventions do not work well with OSSA. Retain the result right after the call when we come out of OSSA so we can treat the returned value as if it was owned when we do optimizations. This fix a miscompilation due to the DestroyAddrHoisting pass hoisting destroys above copies with unowned sources. When the destroyed object was the last reference to the pointed memory the copy is happening too late resulting in a use after free. rdar://160462854
1 parent bc88160 commit 8c197eb

File tree

5 files changed

+70
-5
lines changed

5 files changed

+70
-5
lines changed

lib/SIL/IR/SILType.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,11 @@ SILResultInfo::getOwnershipKind(SILFunction &F,
655655
case ResultConvention::Owned:
656656
return OwnershipKind::Owned;
657657
case ResultConvention::Unowned:
658+
// We insert a retain right after the call returning an unowned value in
659+
// OwnershipModelEliminator. So treat the result as owned.
660+
if (IsTrivial)
661+
return OwnershipKind::None;
662+
return OwnershipKind::Owned;
658663
case ResultConvention::UnownedInnerPointer:
659664
if (IsTrivial)
660665
return OwnershipKind::None;

lib/SILGen/SILGenApply.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6188,13 +6188,12 @@ RValue SILGenFunction::emitApply(
61886188
B.getDefaultAtomicity());
61896189
hasAlreadyLifetimeExtendedSelf = true;
61906190
}
6191-
LLVM_FALLTHROUGH;
6192-
6193-
case ResultConvention::Unowned:
6194-
// Unretained. Retain the value.
61956191
result = resultTL.emitCopyValue(B, loc, result);
61966192
break;
61976193

6194+
case ResultConvention::Unowned:
6195+
// Handled in OwnershipModelEliminator.
6196+
break;
61986197
case ResultConvention::GuaranteedAddress:
61996198
case ResultConvention::Guaranteed:
62006199
llvm_unreachable("borrow accessor is not yet implemented");

lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
///
2323
//===----------------------------------------------------------------------===//
2424

25+
#include "swift/AST/Types.h"
26+
#include "swift/SIL/SILValue.h"
2527
#define DEBUG_TYPE "sil-ownership-model-eliminator"
2628

2729
#include "swift/Basic/Assertions.h"
@@ -404,6 +406,16 @@ bool OwnershipModelEliminatorVisitor::visitApplyInst(ApplyInst *ai) {
404406
changed = true;
405407
}
406408

409+
// Insert a retain for unowned results.
410+
SILBuilderWithScope builder(ai->getNextInstruction(), builderCtx);
411+
builder.emitDestructureValueOperation(
412+
ai->getLoc(), ai, [&](unsigned idx, SILValue v) {
413+
auto resultsIt = fnConv.getDirectSILResults().begin();
414+
if (resultsIt->getConvention() == ResultConvention::Unowned)
415+
builder.emitCopyValueOperation(ai->getLoc(), v);
416+
++resultsIt;
417+
});
418+
407419
return changed;
408420
}
409421

test/Interop/Cxx/foreign-reference/Inputs/logging-frts.h

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ class SharedFRT {
77
public:
88
SharedFRT() : _refCount(1) { logMsg("Ctor"); }
99

10+
protected:
11+
virtual ~SharedFRT() { logMsg("Dtor"); }
12+
1013
private:
1114
void logMsg(const char *s) const {
1215
printf("RefCount: %d, message: %s\n", _refCount, s);
1316
}
1417

15-
~SharedFRT() { logMsg("Dtor"); }
1618
SharedFRT(const SharedFRT &) = delete;
1719
SharedFRT &operator=(const SharedFRT &) = delete;
1820
SharedFRT(SharedFRT &&) = delete;
@@ -62,3 +64,24 @@ inline LargeStructWithRefCountedField getStruct() {
6264
inline LargeStructWithRefCountedFieldNested getNestedStruct() {
6365
return {0, {0, 0, 0, 0, new SharedFRT()}};
6466
}
67+
68+
template <class T>
69+
struct Ref {
70+
T *_Nonnull ptr() const { return ref; }
71+
T *ref;
72+
};
73+
74+
class Payload final : public SharedFRT {
75+
public:
76+
static Ref<Payload> create(int value) {
77+
Ref<Payload> ref;
78+
ref.ref = new Payload(value);
79+
return ref;
80+
}
81+
82+
int value() const { return m_value; }
83+
84+
private:
85+
explicit Payload(int value) : m_value(value) {}
86+
int m_value;
87+
};
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %target-run-simple-swift(-I %swift_src_root/lib/ClangImporter/SwiftBridging -I %S/Inputs -cxx-interoperability-mode=default -Xfrontend -disable-availability-checking -O) | %FileCheck %s
2+
3+
// REQUIRES: executable_test
4+
5+
import LoggingFrts
6+
7+
@inline(never)
8+
func use(_ x: CInt) {
9+
print("Value is \(x).")
10+
}
11+
12+
func testRefIssues() {
13+
var a2 = Optional.some(Payload.create(0));
14+
let b2 = a2!.ptr();
15+
a2 = Optional.none;
16+
let v2 = b2.value();
17+
use(v2)
18+
}
19+
testRefIssues()
20+
21+
// CHECK: RefCount: 1, message: Ctor
22+
// CHECK-NEXT: RefCount: 2, message: retain
23+
// CHECK-NEXT: RefCount: 1, message: release
24+
// CHECK-NEXT: Value is 0.
25+
// CHECK-NEXT: RefCount: 0, message: release
26+
// CHECK-NEXT: RefCount: 0, message: Dtor

0 commit comments

Comments
 (0)