Skip to content

[Runtime] Fix UB in layout string runtime functions #66112

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions stdlib/public/runtime/BytecodeLayouts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,18 @@ static const size_t layoutStringHeaderSize = sizeof(uint64_t) + sizeof(size_t);
/// offset
template <typename T>
T readBytes(const uint8_t *typeLayout, size_t &i) {
T returnVal = *(const T *)(typeLayout + i);
T returnVal;
memcpy(&returnVal, typeLayout + i, sizeof(T));
i += sizeof(T);
return returnVal;
}

/// Given a pointer, a value, and an offset, write the value at the given
/// offset in big-endian order
/// offset and increment offset by the size of T
template <typename T>
void writeBytes(uint8_t *typeLayout, size_t i, T value) {
*((T*)(typeLayout + i)) = value;
void writeBytes(uint8_t *typeLayout, size_t &i, T value) {
memcpy(typeLayout + i, &value, sizeof(T));
i += sizeof(T);
}

Metadata *getExistentialTypeMetadata(OpaqueValue *object) {
Expand All @@ -64,7 +66,8 @@ const Metadata *getResilientTypeMetadata(const Metadata* metadata,
const uint8_t *layoutStr,
size_t &offset) {
auto absolute = layoutStr + offset;
auto relativeOffset = (uintptr_t)(intptr_t)(int32_t)readBytes<intptr_t>(layoutStr, offset);
auto relativeOffset =
(uintptr_t)(intptr_t)(int32_t)readBytes<intptr_t>(layoutStr, offset);
MetadataAccessor fn;

#if SWIFT_PTRAUTH
Expand Down Expand Up @@ -404,12 +407,13 @@ void swift::swift_resolve_resilientAccessors(

switch (tag) {
case RefCountingKind::Resilient: {
auto *type = getResilientTypeMetadata(fieldType, fieldLayoutStr,
i);
uint8_t *curPos = (layoutStr + layoutStrOffset + currentOffset - layoutStringHeaderSize);
*((uint64_t*)curPos) =
auto *type = getResilientTypeMetadata(fieldType, fieldLayoutStr, i);
size_t writeOffset = layoutStrOffset + currentOffset -
layoutStringHeaderSize;
uint64_t tagAndOffset =
(((uint64_t)RefCountingKind::Metatype) << 56) | size;
*((Metadata const* *)(curPos + sizeof(uint64_t))) = type;
writeBytes(layoutStr, writeOffset, tagAndOffset);
writeBytes(layoutStr, writeOffset, type);
break;
}
case RefCountingKind::Metatype:
Expand Down
95 changes: 57 additions & 38 deletions stdlib/public/runtime/Metadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2641,24 +2641,40 @@ void swift::swift_initStructMetadata(StructMetadata *structType,
vwtable->publishLayout(layout);
}

enum LayoutStringFlags : uint64_t {
Empty = 0,
// TODO: Track other useful information tha can be used to optimize layout
// strings, like different reference kinds contained in the string
// number of ref counting operations (maybe up to 4), so we can
// use witness functions optimized for these cases.
HasRelativePointers = (1ULL << 63),
};
namespace {
enum LayoutStringFlags : uint64_t {
Empty = 0,
// TODO: Track other useful information tha can be used to optimize layout
// strings, like different reference kinds contained in the string
// number of ref counting operations (maybe up to 4), so we can
// use witness functions optimized for these cases.
HasRelativePointers = (1ULL << 63),
};

inline bool operator&(LayoutStringFlags a, LayoutStringFlags b) {
return (uint64_t(a) & uint64_t(b)) != 0;
}
inline LayoutStringFlags operator|(LayoutStringFlags a, LayoutStringFlags b) {
return LayoutStringFlags(uint64_t(a) | uint64_t(b));
}
inline LayoutStringFlags &operator|=(LayoutStringFlags &a, LayoutStringFlags b) {
return a = (a | b);
}
inline bool operator&(LayoutStringFlags a, LayoutStringFlags b) {
return (uint64_t(a) & uint64_t(b)) != 0;
}
inline LayoutStringFlags operator|(LayoutStringFlags a, LayoutStringFlags b) {
return LayoutStringFlags(uint64_t(a) | uint64_t(b));
}
inline LayoutStringFlags &operator|=(LayoutStringFlags &a, LayoutStringFlags b) {
return a = (a | b);
}

template <typename T>
inline T readBytes(const uint8_t *layoutStr, size_t &i) {
T returnVal;
memcpy(&returnVal, layoutStr + i, sizeof(T));
i += sizeof(T);
return returnVal;
}

template <typename T>
inline void writeBytes(uint8_t *layoutStr, size_t &i, T value) {
memcpy(layoutStr + i, &value, sizeof(T));
i += sizeof(T);
}
} // end anonymous namespace

void swift::swift_initStructMetadataWithLayoutString(
StructMetadata *structType, StructLayoutFlags layoutFlags, size_t numFields,
Expand Down Expand Up @@ -2732,9 +2748,9 @@ void swift::swift_initStructMetadataWithLayoutString(
uint8_t *layoutStr = (uint8_t *)MetadataAllocator(LayoutStringTag)
.Allocate(fixedLayoutStringSize + refCountBytes, alignof(uint8_t));

*((size_t*)(layoutStr + sizeof(uint64_t))) = refCountBytes;
size_t layoutStrOffset = sizeof(uint64_t);

size_t layoutStrOffset = layoutStringHeaderSize;
writeBytes(layoutStr, layoutStrOffset, refCountBytes);
size_t fullOffset = 0;
size_t previousFieldOffset = 0;
LayoutStringFlags flags = LayoutStringFlags::Empty;
Expand All @@ -2753,9 +2769,8 @@ void swift::swift_initStructMetadataWithLayoutString(
auto tag = fieldTag <= 0x2 ? RefCountingKind::UnknownUnowned :
RefCountingKind::UnknownWeak;

*(uint64_t *)(layoutStr + layoutStrOffset) =
((uint64_t)tag << 56) | offset;
layoutStrOffset += sizeof(uint64_t);
auto tagAndOffset = ((uint64_t)tag << 56) | offset;
writeBytes(layoutStr, layoutStrOffset, tagAndOffset);
}

fullOffset += fieldType->size;
Expand Down Expand Up @@ -2793,10 +2808,18 @@ void swift::swift_initStructMetadataWithLayoutString(
}

if (offset) {
*(uint64_t *)(layoutStr + layoutStrOffset) += offset;
auto layoutStrOffsetCopy = layoutStrOffset;
auto firstTagAndOffset =
readBytes<uint64_t>(layoutStr, layoutStrOffsetCopy);
layoutStrOffsetCopy = layoutStrOffset;
firstTagAndOffset += offset;
writeBytes(layoutStr, layoutStrOffsetCopy, firstTagAndOffset);
}

previousFieldOffset = *(const uint64_t*)(fieldLayoutStr + layoutStringHeaderSize + fieldRefCountBytes);
auto previousFieldOffsetOffset =
layoutStringHeaderSize + fieldRefCountBytes;
previousFieldOffset = readBytes<uint64_t>(fieldLayoutStr,
previousFieldOffsetOffset);
layoutStrOffset += fieldRefCountBytes;
} else {
previousFieldOffset += fieldType->vw_size();
Expand Down Expand Up @@ -2827,39 +2850,35 @@ void swift::swift_initStructMetadataWithLayoutString(
};
}

*(uint64_t*)(layoutStr + layoutStrOffset) =
((uint64_t)tag << 56) | offset;
layoutStrOffset += sizeof(uint64_t);
writeBytes(layoutStr, layoutStrOffset, ((uint64_t)tag << 56) | offset);
previousFieldOffset = fieldType->vw_size();
fullOffset += previousFieldOffset;
} else if (fieldType->isAnyExistentialType()) {
auto *existential = dyn_cast<ExistentialTypeMetadata>(fieldType);
assert(existential);
auto tag = existential->isClassBounded() ? RefCountingKind::Unknown
: RefCountingKind::Existential;
*(uint64_t*)(layoutStr + layoutStrOffset) =
((uint64_t)tag << 56) | offset;
layoutStrOffset += sizeof(uint64_t);
writeBytes(layoutStr, layoutStrOffset, ((uint64_t)tag << 56) | offset);
previousFieldOffset = fieldType->vw_size();
fullOffset += previousFieldOffset;
} else {
metadata:
*(uint64_t*)(layoutStr + layoutStrOffset) =
((uint64_t)RefCountingKind::Metatype << 56) | offset;
*(uintptr_t*)(layoutStr + layoutStrOffset + sizeof(uint64_t)) =
(uintptr_t)fieldType;
layoutStrOffset += sizeof(uint64_t) + sizeof(uintptr_t);
writeBytes(layoutStr, layoutStrOffset,
((uint64_t)RefCountingKind::Metatype << 56) | offset);
writeBytes(layoutStr, layoutStrOffset, fieldType);
previousFieldOffset = fieldType->vw_size();
fullOffset += previousFieldOffset;
}
}

*(uint64_t *)(layoutStr + layoutStrOffset) = previousFieldOffset;
*(uint64_t *)(layoutStr + layoutStrOffset + sizeof(uint64_t)) = 0;
writeBytes(layoutStr, layoutStrOffset, previousFieldOffset);
writeBytes(layoutStr, layoutStrOffset, 0);

// we mask out HasRelativePointers, because at this point they have all been
// resolved to metadata pointers
*(uint64_t *)(layoutStr) = ((uint64_t)flags) & ~((uint64_t)LayoutStringFlags::HasRelativePointers);
layoutStrOffset = 0;
writeBytes(layoutStr, layoutStrOffset,
((uint64_t)flags) & ~((uint64_t)LayoutStringFlags::HasRelativePointers));

structType->setLayoutString(layoutStr);

Expand Down