From 7bb9579434d26f6793792e1a4ec22976b7eb835e Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Wed, 5 Nov 2025 17:17:58 -0500 Subject: [PATCH] [Runtime] Fix copying in extended existential value witnesses. Instead of copying the data and the type and witnesses separately, use the size in the value witness table and copy everything at once. copyTypeInto assumed the type was an ordinary existential. When it was actually an extended existential, it would do an incorrect cast and read part of a pointer as the number of witness tables to copy. This would typically result in a large buffer overflow and crash. At this point we already know the type's size, so we can use that info directly rather than essentially recomputing it. rdar://163980446 --- .../public/runtime/ExistentialMetadataImpl.h | 99 +++++++++---------- .../extended_existential_copying.swift | 88 +++++++++++++++++ 2 files changed, 133 insertions(+), 54 deletions(-) create mode 100644 test/Interpreter/extended_existential_copying.swift diff --git a/stdlib/public/runtime/ExistentialMetadataImpl.h b/stdlib/public/runtime/ExistentialMetadataImpl.h index da19576593888..1715c13e3a938 100644 --- a/stdlib/public/runtime/ExistentialMetadataImpl.h +++ b/stdlib/public/runtime/ExistentialMetadataImpl.h @@ -25,7 +25,20 @@ namespace swift { namespace metadataimpl { /// A common base class for opaque-existential and class-existential boxes. -template struct SWIFT_LIBRARY_VISIBILITY ExistentialBoxBase {}; +template +struct SWIFT_LIBRARY_VISIBILITY ExistentialBoxBase { + template + static void copyBytes(Container *dest, Container *src, A... args) { + memcpy(dest, src, src->getContainerStride(args...)); + } + + template + static void copyTypeBytes(Container *dest, Container *src, A...args) { + auto valueBytes = src->getValueSize(); + auto toCopy = src->getContainerStride(args...) - valueBytes; + memcpy((char *)dest + valueBytes, (char *)src + valueBytes, toCopy); + } +}; /// A common base class for fixed and non-fixed opaque-existential box /// implementations. @@ -83,7 +96,7 @@ struct SWIFT_LIBRARY_VISIBILITY OpaqueExistentialBoxBase template static Container *initializeWithCopy(Container *dest, Container *src, A... args) { - src->copyTypeInto(dest, args...); + copyTypeBytes(dest, src, args...); auto *type = src->getType(); auto *vwt = type->getValueWitnesses(); @@ -104,10 +117,7 @@ struct SWIFT_LIBRARY_VISIBILITY OpaqueExistentialBoxBase template static Container *initializeWithTake(Container *dest, Container *src, A... args) { - src->copyTypeInto(dest, args...); - auto from = src->getBuffer(args...); - auto to = dest->getBuffer(args...); - memcpy(to, from, sizeof(ValueBuffer)); + copyBytes(dest, src, args...); return dest; } @@ -151,7 +161,7 @@ struct SWIFT_LIBRARY_VISIBILITY OpaqueExistentialBoxBase // Move dest value aside so we can destroy it later. destType->vw_initializeWithTake(opaqueTmpBuffer, destValue); - src->copyTypeInto(dest, args...); + copyTypeBytes(dest, src, args...); if (srcVwt->isValueInline()) { // Inline src value. @@ -172,7 +182,7 @@ struct SWIFT_LIBRARY_VISIBILITY OpaqueExistentialBoxBase auto *destRef = *reinterpret_cast(dest->getBuffer(args...)); - src->copyTypeInto(dest, args...); + copyTypeBytes(dest, src, args...); if (srcVwt->isValueInline()) { // initWithCopy. @@ -239,7 +249,7 @@ struct SWIFT_LIBRARY_VISIBILITY OpaqueExistentialBoxBase // Move dest value aside. destType->vw_initializeWithTake(opaqueTmpBuffer, destValue); - src->copyTypeInto(dest, args...); + copyTypeBytes(dest, src, args...); if (srcVwt->isValueInline()) { // Inline src value. @@ -260,7 +270,7 @@ struct SWIFT_LIBRARY_VISIBILITY OpaqueExistentialBoxBase auto *destRef = *reinterpret_cast(dest->getBuffer(args...)); - src->copyTypeInto(dest, args...); + copyTypeBytes(dest, src, args...); if (srcVwt->isValueInline()) { // initWithCopy. @@ -291,12 +301,20 @@ template struct SWIFT_LIBRARY_VISIBILITY FixedOpaqueExistentialContainer { OpaqueExistentialContainer Header; const void *WitnessTables[NumWitnessTables]; + + static size_t getValueSize() { + return sizeof(Header.Buffer); + } }; // We need to be able to instantiate for NumWitnessTables==0, which // requires an explicit specialization. template <> struct FixedOpaqueExistentialContainer<0> { OpaqueExistentialContainer Header; + + static size_t getValueSize() { + return sizeof(Header.Buffer); + } }; /// A box implementation class for an opaque existential type with @@ -311,10 +329,7 @@ struct SWIFT_LIBRARY_VISIBILITY OpaqueExistentialBox ValueBuffer *getBuffer() { return &this->Header.Buffer; } - void copyTypeInto(Container *dest) const { - this->Header.copyTypeInto(&dest->Header, NumWitnessTables); - } - + static size_t getContainerStride() { return sizeof(Container); } @@ -355,9 +370,6 @@ struct SWIFT_LIBRARY_VISIBILITY NonFixedOpaqueExistentialBox ValueBuffer *getBuffer(const Metadata *self) { return &Header.Buffer; } - void copyTypeInto(Container *dest, const Metadata *self) { - Header.copyTypeInto(&dest->Header, getNumWitnessTables(self)); - } static unsigned getNumWitnessTables(const Metadata *self) { auto castSelf = static_cast(self); @@ -377,7 +389,11 @@ struct SWIFT_LIBRARY_VISIBILITY NonFixedOpaqueExistentialBox } static size_t getContainerStride(const Metadata *self) { - return getStride(getNumWitnessTables(self)); + return self->vw_stride(); + } + + static size_t getValueSize() { + return sizeof(Header.Buffer); } }; @@ -411,9 +427,8 @@ struct SWIFT_LIBRARY_VISIBILITY ClassExistentialBoxBase template static Container *initializeWithCopy(Container *dest, Container *src, A... args) { - src->copyTypeInto(dest, args...); auto newValue = *src->getValueSlot(); - *dest->getValueSlot() = newValue; + copyBytes(dest, src, args...); swift_unknownObjectRetain(newValue); return dest; } @@ -421,18 +436,16 @@ struct SWIFT_LIBRARY_VISIBILITY ClassExistentialBoxBase template static Container *initializeWithTake(Container *dest, Container *src, A... args) { - src->copyTypeInto(dest, args...); - *dest->getValueSlot() = *src->getValueSlot(); + copyBytes(dest, src, args...); return dest; } template static Container *assignWithCopy(Container *dest, Container *src, A... args) { - src->copyTypeInto(dest, args...); auto newValue = *src->getValueSlot(); auto oldValue = *dest->getValueSlot(); - *dest->getValueSlot() = newValue; + copyBytes(dest, src, args...); swift_unknownObjectRetain(newValue); swift_unknownObjectRelease(oldValue); return dest; @@ -441,10 +454,8 @@ struct SWIFT_LIBRARY_VISIBILITY ClassExistentialBoxBase template static Container *assignWithTake(Container *dest, Container *src, A... args) { - src->copyTypeInto(dest, args...); - auto newValue = *src->getValueSlot(); auto oldValue = *dest->getValueSlot(); - *dest->getValueSlot() = newValue; + copyBytes(dest, src, args...); swift_unknownObjectRelease(oldValue); return dest; } @@ -470,10 +481,6 @@ struct SWIFT_LIBRARY_VISIBILITY ClassExistentialBox : ClassExistentialBoxBase { ClassExistentialContainer Header; const void *TypeInfo[NumWitnessTables]; - void copyTypeInto(Container *dest) const { - for (unsigned i = 0; i != NumWitnessTables; ++i) - dest->TypeInfo[i] = TypeInfo[i]; - } void **getValueSlot() { return &Header.Value; } void * const *getValueSlot() const { return &Header.Value; } @@ -501,10 +508,6 @@ struct SWIFT_LIBRARY_VISIBILITY NonFixedClassExistentialBox return castSelf->Flags.getNumWitnessTables(); } - void copyTypeInto(Container *dest, const Metadata *self) { - Header.copyTypeInto(&dest->Header, getNumWitnessTables(self)); - } - void **getValueSlot() { return &Header.Value; } void * const *getValueSlot() const { return &Header.Value; } @@ -520,7 +523,7 @@ struct SWIFT_LIBRARY_VISIBILITY NonFixedClassExistentialBox return getSize(numWitnessTables); } static size_t getContainerStride(const Metadata *self) { - return getStride(getNumWitnessTables(self)); + return self->vw_stride(); } }; using type = Container; @@ -540,32 +543,28 @@ struct SWIFT_LIBRARY_VISIBILITY ExistentialMetatypeBoxBase template static Container *initializeWithCopy(Container *dest, Container *src, A... args) { - src->copyTypeInto(dest, args...); - *dest->getValueSlot() = *src->getValueSlot(); - return dest; + copyBytes(dest, src, args...); + return dest; } template static Container *initializeWithTake(Container *dest, Container *src, A... args) { - src->copyTypeInto(dest, args...); - *dest->getValueSlot() = *src->getValueSlot(); + copyBytes(dest, src, args...); return dest; } template static Container *assignWithCopy(Container *dest, Container *src, A... args) { - src->copyTypeInto(dest, args...); - *dest->getValueSlot() = *src->getValueSlot(); + copyBytes(dest, src, args...); return dest; } template static Container *assignWithTake(Container *dest, Container *src, A... args) { - src->copyTypeInto(dest, args...); - *dest->getValueSlot() = *src->getValueSlot(); + copyBytes(dest, src, args...); return dest; } @@ -593,10 +592,6 @@ struct SWIFT_LIBRARY_VISIBILITY ExistentialMetatypeBox ExistentialMetatypeContainer Header; const void *TypeInfo[NumWitnessTables]; - void copyTypeInto(Container *dest) const { - for (unsigned i = 0; i != NumWitnessTables; ++i) - dest->TypeInfo[i] = TypeInfo[i]; - } const Metadata **getValueSlot() { return &Header.Value; } const Metadata * const *getValueSlot() const { return &Header.Value; } @@ -624,10 +619,6 @@ struct SWIFT_LIBRARY_VISIBILITY NonFixedExistentialMetatypeBox return castSelf->Flags.getNumWitnessTables(); } - void copyTypeInto(Container *dest, const Metadata *self) { - Header.copyTypeInto(&dest->Header, getNumWitnessTables(self)); - } - const Metadata **getValueSlot() { return &Header.Value; } const Metadata * const *getValueSlot() const { return &Header.Value; } @@ -643,7 +634,7 @@ struct SWIFT_LIBRARY_VISIBILITY NonFixedExistentialMetatypeBox return getSize(numWitnessTables); } static size_t getContainerStride(const Metadata *self) { - return getStride(getNumWitnessTables(self)); + return self->vw_stride(); } }; using type = Container; diff --git a/test/Interpreter/extended_existential_copying.swift b/test/Interpreter/extended_existential_copying.swift new file mode 100644 index 0000000000000..20f8cc1521cf2 --- /dev/null +++ b/test/Interpreter/extended_existential_copying.swift @@ -0,0 +1,88 @@ +// RUN: %target-run-simple-swift | %FileCheck %s + +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime +// REQUIRES: OS=macosx +// REQUIRES: executable_test + +protocol P1 { + associatedtype P1T + + func printValue() +} + +protocol P2 { + associatedtype P2T +} + +protocol P3 { + associatedtype P3T +} + +struct Small: P1, P2, P3 { + typealias P1T = Int + typealias P2T = Int + typealias P3T = Int + + var str = "I am Small" + + func printValue() { print(str) } +} + +struct Big: P1, P2, P3 { + typealias P1T = Int + typealias P2T = Int + typealias P3T = Int + + var str = "I am Big" + var str2 = "" + var str3 = "" + + func printValue() { print(str) } +} + +class Class: P1, P2, P3 { + typealias P1T = Int + typealias P2T = Int + typealias P3T = Int + + var str = "I am Class" + + func printValue() { print(str) } +} + +func test(_ value: T) { + var array: [T] = [] + array.append(value) + for v in array { + (v as? P1)?.printValue() + } +} + +// CHECK: I am Small +test(Small() as any P1) +// CHECK: I am Small +test(Small() as any P1 & P2) +// CHECK: I am Small +test(Small() as any P1 & P2 & P3) + +// CHECK: I am Big +test(Big() as any P1) +// CHECK: I am Big +test(Big() as any P1 & P2) +// CHECK: I am Big +test(Big() as any P1 & P2 & P3) + +// CHECK: I am Class +test(Class() as any P1) +// CHECK: I am Class +test(Class() as any P1 & P2) +// CHECK: I am Class +test(Class() as any P1 & P2 & P3) + +// CHECK: I am Class +test(Class() as any AnyObject & P1) +// CHECK: I am Class +test(Class() as any AnyObject & P1 & P2) +// CHECK: I am Class +test(Class() as any AnyObject & P1 & P2 & P3)