Skip to content

Commit 40b8079

Browse files
committed
[Runtime] Fix missing memcpy in handleSingleRefCountInitWithCopy
rdar://117755666 Missing memcpy in handleSingleRefCountInitWithCopy caused wrong enum cases, crashes etc.
1 parent e9b297a commit 40b8079

File tree

3 files changed

+59
-6
lines changed

3 files changed

+59
-6
lines changed

stdlib/public/runtime/BytecodeLayouts.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ static void singlePayloadEnumGeneric(const Metadata *metadata,
281281
if (SWIFT_LIKELY(xiType)) {
282282
auto tag = xiType->vw_getEnumTagSinglePayload(
283283
(const OpaqueValue *)(addr + addrOffset + xiTagBytesOffset),
284-
numEmptyCases);
284+
xiType->vw_getNumExtraInhabitants());
285285
if (SWIFT_LIKELY(tag == 0)) {
286286
return;
287287
}
@@ -501,7 +501,7 @@ static void singlePayloadEnumGeneric(const Metadata *metadata,
501501
if (SWIFT_LIKELY(xiType)) {
502502
auto tag = xiType->vw_getEnumTagSinglePayload(
503503
(const OpaqueValue *)(src + addrOffset + xiTagBytesOffset),
504-
numEmptyCases);
504+
xiType->vw_getNumExtraInhabitants());
505505
if (SWIFT_LIKELY(tag == 0)) {
506506
return;
507507
}
@@ -1317,7 +1317,12 @@ static void handleSingleRefCountInitWithCopy(const Metadata *metadata,
13171317
uint8_t *dest,
13181318
uint8_t *src) {
13191319
auto tag = reader.readBytes<uint64_t>();
1320-
addrOffset += (tag & ~(0xFFULL << 56));
1320+
auto _addrOffset = addrOffset;
1321+
auto offset = (tag & ~(0xFFULL << 56));
1322+
if (SWIFT_UNLIKELY(offset)) {
1323+
memcpy(dest + _addrOffset, src + _addrOffset, offset);
1324+
}
1325+
addrOffset = _addrOffset + offset;
13211326
tag >>= 56;
13221327
if (SWIFT_UNLIKELY(tag == 0)) {
13231328
return;
@@ -1490,13 +1495,13 @@ static void singlePayloadEnumGenericAssignWithCopy(const Metadata *metadata,
14901495
if (!srcTag) {
14911496
srcTag = xiType->vw_getEnumTagSinglePayload(
14921497
(const OpaqueValue *)(src + addrOffset + xiTagBytesOffset),
1493-
numEmptyCases);
1498+
xiType->vw_getNumExtraInhabitants());
14941499
}
14951500

14961501
if (!destTag) {
14971502
destTag = xiType->vw_getEnumTagSinglePayload(
14981503
(const OpaqueValue *)(dest + addrOffset + xiTagBytesOffset),
1499-
numEmptyCases);
1504+
xiType->vw_getNumExtraInhabitants());
15001505
}
15011506
}
15021507

@@ -2059,7 +2064,8 @@ swift_singlePayloadEnumGeneric_getEnumTag(swift::OpaqueValue *address,
20592064
uint8_t numExtraTagBytes) -> unsigned {
20602065
if (xiType) {
20612066
return xiType->vw_getEnumTagSinglePayload(
2062-
(const OpaqueValue *)(addr + xiTagBytesOffset), numEmptyCases);
2067+
(const OpaqueValue *)(addr + xiTagBytesOffset),
2068+
xiType->vw_getNumExtraInhabitants());
20632069
}
20642070

20652071
return 0;

test/Interpreter/Inputs/layout_string_witnesses_types.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,12 @@ public enum PrespecializedMultiPayloadEnum<T> {
543543
case nonEmpty1(T, Int)
544544
}
545545

546+
public enum SinglePayloadEnumExistential {
547+
case a(SomeProtocol, AnyObject)
548+
case b
549+
case c
550+
}
551+
546552
@inline(never)
547553
public func consume<T>(_ x: T.Type) {
548554
withExtendedLifetime(x) {}
@@ -575,6 +581,10 @@ public func testAssign<T>(_ ptr: UnsafeMutablePointer<T>, from x: UnsafeMutableP
575581
ptr.assign(from: x, count: 1)
576582
}
577583

584+
public func testAssignCopy<T>(_ ptr: UnsafeMutablePointer<T>, from x: inout T) {
585+
ptr.update(from: &x, count: 1)
586+
}
587+
578588
@inline(never)
579589
public func testInit<T>(_ ptr: UnsafeMutablePointer<T>, to x: T) {
580590
ptr.initialize(to: x)

test/Interpreter/layout_string_witnesses_static.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,43 @@ func testSinglePayloadEnumManyXIEmpty() {
770770

771771
testSinglePayloadEnumManyXIEmpty()
772772

773+
func testEnumWithExistential() {
774+
// Regression test for rdar://117755666
775+
// A missing call to memcpy in `handleSingleRefCountInitWithCopy`
776+
// was causing unexpected behavior like wrong enum cases, crashes etc.
777+
// Without the fix, this test would not release the references.
778+
struct SomeProtocolImpl: SomeProtocol {
779+
let x: AnyObject
780+
}
781+
782+
let ptr = UnsafeMutablePointer<SinglePayloadEnumExistential>.allocate(capacity: 1)
783+
784+
do {
785+
let x = SinglePayloadEnumExistential.b
786+
testInit(ptr, to: x)
787+
}
788+
789+
do {
790+
var z = SinglePayloadEnumExistential.a(SomeProtocolImpl(x: SimpleClass(x: 23)), SimpleClass(x: 43))
791+
792+
// CHECK-NEXT: Before deinit
793+
print("Before deinit")
794+
795+
testAssignCopy(ptr, from: &z)
796+
}
797+
798+
// CHECK-NEXT: Before deinit
799+
print("Before deinit")
800+
801+
// CHECK-NEXT: SimpleClass deinitialized!
802+
// CHECK-NEXT: SimpleClass deinitialized!
803+
testDestroy(ptr)
804+
805+
ptr.deallocate()
806+
}
807+
808+
testEnumWithExistential()
809+
773810
#if os(macOS)
774811
func testObjc() {
775812
let ptr = UnsafeMutablePointer<ObjcWrapper>.allocate(capacity: 1)

0 commit comments

Comments
 (0)