[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
This commit is contained in:
Mike Ash
2025-11-05 17:17:58 -05:00
parent 6dbe0cb0da
commit 7bb9579434
2 changed files with 133 additions and 54 deletions

View File

@@ -25,7 +25,20 @@ namespace swift {
namespace metadataimpl {
/// A common base class for opaque-existential and class-existential boxes.
template <typename Impl> struct SWIFT_LIBRARY_VISIBILITY ExistentialBoxBase {};
template <typename Impl>
struct SWIFT_LIBRARY_VISIBILITY ExistentialBoxBase {
template <class Container, class... A>
static void copyBytes(Container *dest, Container *src, A... args) {
memcpy(dest, src, src->getContainerStride(args...));
}
template <class Container, class... A>
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 <class Container, class... A>
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 <class Container, class... A>
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<HeapObject **>(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<HeapObject **>(dest->getBuffer(args...));
src->copyTypeInto(dest, args...);
copyTypeBytes(dest, src, args...);
if (srcVwt->isValueInline()) {
// initWithCopy.
@@ -291,12 +301,20 @@ template <unsigned NumWitnessTables>
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<const ExistentialTypeMetadata*>(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 <class Container, class... A>
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 <class Container, class... A>
static Container *initializeWithTake(Container *dest, Container *src,
A... args) {
src->copyTypeInto(dest, args...);
*dest->getValueSlot() = *src->getValueSlot();
copyBytes(dest, src, args...);
return dest;
}
template <class Container, class... A>
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 <class Container, class... A>
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 <class Container, class... A>
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 <class Container, class... A>
static Container *initializeWithTake(Container *dest, Container *src,
A... args) {
src->copyTypeInto(dest, args...);
*dest->getValueSlot() = *src->getValueSlot();
copyBytes(dest, src, args...);
return dest;
}
template <class Container, class... A>
static Container *assignWithCopy(Container *dest, Container *src,
A... args) {
src->copyTypeInto(dest, args...);
*dest->getValueSlot() = *src->getValueSlot();
copyBytes(dest, src, args...);
return dest;
}
template <class Container, class... A>
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;

View File

@@ -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<P1T> {
associatedtype P1T
func printValue()
}
protocol P2<P2T> {
associatedtype P2T
}
protocol P3<P3T> {
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<T>(_ 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<Int>)
// CHECK: I am Small
test(Small() as any P1<Int> & P2<Int>)
// CHECK: I am Small
test(Small() as any P1<Int> & P2<Int> & P3<Int>)
// CHECK: I am Big
test(Big() as any P1<Int>)
// CHECK: I am Big
test(Big() as any P1<Int> & P2<Int>)
// CHECK: I am Big
test(Big() as any P1<Int> & P2<Int> & P3<Int>)
// CHECK: I am Class
test(Class() as any P1<Int>)
// CHECK: I am Class
test(Class() as any P1<Int> & P2<Int>)
// CHECK: I am Class
test(Class() as any P1<Int> & P2<Int> & P3<Int>)
// CHECK: I am Class
test(Class() as any AnyObject & P1<Int>)
// CHECK: I am Class
test(Class() as any AnyObject & P1<Int> & P2<Int>)
// CHECK: I am Class
test(Class() as any AnyObject & P1<Int> & P2<Int> & P3<Int>)