Skip to content

Commit ebd9c21

Browse files
committed
[Reflection] Sanity-check metadata sizes in MetadataReader before reading.
MetadataReader can be given corrupt or garbage data and we need to be able to handle it gracefully. When reading metadata, the full size to read is calculated from partial data. When we're given bad data, these calculated sizes can be enormous, up to 4GB. Trying to read that much data can cause address space exhaustion which leads to unpleasantness. To avoid this, fix a limit of 1MB on metadata sizes, and fail early if the size is larger. No real-world metadata should ever be that large. We also switch these potentially large calls to use the readBytes variant that returns a unique_ptr, rather than allocating a buffer and reading into it. Our clients typically implement that as the primitive, so this avoids an unnecessary extra data copy and extra address space usage for them. Clients that implement reading into a provided buffer as the primitive should see the same performance as before. rdar://78621784
1 parent 5a6ee21 commit ebd9c21

File tree

2 files changed

+42
-34
lines changed

2 files changed

+42
-34
lines changed

include/swift/Remote/MetadataReader.h

Lines changed: 36 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -170,21 +170,24 @@ class MetadataReader {
170170
using StoredSize = typename Runtime::StoredSize;
171171

172172
private:
173+
/// The maximum number of bytes to read when reading metadata. Anything larger
174+
/// will automatically return failure. This prevents us from reading absurd
175+
/// amounts of data when we encounter corrupt values for sizes/counts.
176+
static const uint64_t MaxMetadataSize = 1048576; // 1MB
177+
173178
/// A cache of built types, keyed by the address of the type.
174179
std::unordered_map<StoredPointer, BuiltType> TypeCache;
175180

176-
using MetadataRef = RemoteRef<TargetMetadata<Runtime>>;
177-
using OwnedMetadataRef =
178-
std::unique_ptr<const TargetMetadata<Runtime>, delete_with_free>;
181+
using MetadataRef = RemoteRef<const TargetMetadata<Runtime>>;
182+
using OwnedMetadataRef = MemoryReader::ReadBytesResult;
179183

180184
/// A cache of read type metadata, keyed by the address of the metadata.
181185
std::unordered_map<StoredPointer, OwnedMetadataRef>
182186
MetadataCache;
183187

184-
using ContextDescriptorRef = RemoteRef<TargetContextDescriptor<Runtime>>;
185-
using OwnedContextDescriptorRef =
186-
std::unique_ptr<const TargetContextDescriptor<Runtime>,
187-
delete_with_free>;
188+
using ContextDescriptorRef =
189+
RemoteRef<const TargetContextDescriptor<Runtime>>;
190+
using OwnedContextDescriptorRef = MemoryReader::ReadBytesResult;
188191

189192
/// A reference to a context descriptor that may be in an unloaded image.
190193
class ParentContextDescriptorRef {
@@ -984,7 +987,9 @@ class MetadataReader {
984987

985988
auto cached = ContextDescriptorCache.find(address);
986989
if (cached != ContextDescriptorCache.end())
987-
return ContextDescriptorRef(address, cached->second.get());
990+
return ContextDescriptorRef(
991+
address, reinterpret_cast<const TargetContextDescriptor<Runtime> *>(
992+
cached->second.get()));
988993

989994
// Read the flags to figure out how much space we should read.
990995
ContextDescriptorFlags flags;
@@ -993,9 +998,9 @@ class MetadataReader {
993998
return nullptr;
994999

9951000
TypeContextDescriptorFlags typeFlags(flags.getKindSpecificFlags());
996-
unsigned baseSize = 0;
997-
unsigned genericHeaderSize = sizeof(GenericContextDescriptorHeader);
998-
unsigned metadataInitSize = 0;
1001+
uint64_t baseSize = 0;
1002+
uint64_t genericHeaderSize = sizeof(GenericContextDescriptorHeader);
1003+
uint64_t metadataInitSize = 0;
9991004
bool hasVTable = false;
10001005

10011006
auto readMetadataInitSize = [&]() -> unsigned {
@@ -1059,7 +1064,7 @@ class MetadataReader {
10591064
// Determine the full size of the descriptor. This is reimplementing a fair
10601065
// bit of TrailingObjects but for out-of-process; maybe there's a way to
10611066
// factor the layout stuff out...
1062-
unsigned genericsSize = 0;
1067+
uint64_t genericsSize = 0;
10631068
if (flags.isGeneric()) {
10641069
GenericContextDescriptorHeader header;
10651070
auto headerAddr = address
@@ -1077,7 +1082,7 @@ class MetadataReader {
10771082
* sizeof(TargetGenericRequirementDescriptor<Runtime>);
10781083
}
10791084

1080-
unsigned vtableSize = 0;
1085+
uint64_t vtableSize = 0;
10811086
if (hasVTable) {
10821087
TargetVTableDescriptorHeader<Runtime> header;
10831088
auto headerAddr = address
@@ -1092,22 +1097,20 @@ class MetadataReader {
10921097
vtableSize = sizeof(header)
10931098
+ header.VTableSize * sizeof(TargetMethodDescriptor<Runtime>);
10941099
}
1095-
1096-
unsigned size = baseSize + genericsSize + metadataInitSize + vtableSize;
1097-
auto buffer = (uint8_t *)malloc(size);
1098-
if (buffer == nullptr) {
1100+
1101+
uint64_t size = baseSize + genericsSize + metadataInitSize + vtableSize;
1102+
if (size > MaxMetadataSize)
10991103
return nullptr;
1100-
}
1101-
if (!Reader->readBytes(RemoteAddress(address), buffer, size)) {
1102-
free(buffer);
1104+
auto readResult = Reader->readBytes(RemoteAddress(address), size);
1105+
if (!readResult)
11031106
return nullptr;
1104-
}
11051107

1106-
auto descriptor
1107-
= reinterpret_cast<TargetContextDescriptor<Runtime> *>(buffer);
1108+
auto descriptor =
1109+
reinterpret_cast<const TargetContextDescriptor<Runtime> *>(
1110+
readResult.get());
11081111

11091112
ContextDescriptorCache.insert(
1110-
std::make_pair(address, OwnedContextDescriptorRef(descriptor)));
1113+
std::make_pair(address, std::move(readResult)));
11111114
return ContextDescriptorRef(address, descriptor);
11121115
}
11131116

@@ -1633,7 +1636,9 @@ class MetadataReader {
16331636
MetadataRef readMetadata(StoredPointer address) {
16341637
auto cached = MetadataCache.find(address);
16351638
if (cached != MetadataCache.end())
1636-
return MetadataRef(address, cached->second.get());
1639+
return MetadataRef(address,
1640+
reinterpret_cast<const TargetMetadata<Runtime> *>(
1641+
cached->second.get()));
16371642

16381643
StoredPointer KindValue = 0;
16391644
if (!Reader->readInteger(RemoteAddress(address), &KindValue))
@@ -1807,18 +1812,15 @@ class MetadataReader {
18071812
}
18081813

18091814
MetadataRef _readMetadata(StoredPointer address, size_t sizeAfter) {
1810-
auto size = sizeAfter;
1811-
uint8_t *buffer = (uint8_t *) malloc(size);
1812-
if (!buffer)
1815+
if (sizeAfter > MaxMetadataSize)
18131816
return nullptr;
1814-
1815-
if (!Reader->readBytes(RemoteAddress(address), buffer, size)) {
1816-
free(buffer);
1817+
auto readResult = Reader->readBytes(RemoteAddress(address), sizeAfter);
1818+
if (!readResult)
18171819
return nullptr;
1818-
}
18191820

1820-
auto metadata = reinterpret_cast<TargetMetadata<Runtime>*>(buffer);
1821-
MetadataCache.insert(std::make_pair(address, OwnedMetadataRef(metadata)));
1821+
auto metadata =
1822+
reinterpret_cast<const TargetMetadata<Runtime> *>(readResult.get());
1823+
MetadataCache.insert(std::make_pair(address, std::move(readResult)));
18221824
return MetadataRef(address, metadata);
18231825
}
18241826

test/stdlib/symbol-visibility-linux.test-sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
// RUN: -e _ZNSt6vectorIjSaIjEE13_M_insert_auxIJjEEEvN9__gnu_cxx17__normal_iteratorIPjS1_EEDpOT_ \
2020
// RUN: -e _ZNSt6vectorISt10unique_ptrIKvSt8functionIFvPS1_EEESaIS6_EE19_M_emplace_back_auxIJS6_EEEvDpOT_ \
2121
// RUN: -e _ZNSt6vectorISt10unique_ptrIKvSt8functionIFvPS1_EEESaIS6_EE17_M_realloc_insertIJS6_EEEvN9__gnu_cxx17__normal_iteratorIPS6_S8_EEDpOT_ \
22+
// RUN: -e _ZNSt10_HashtableImSt4pairIKmSt10unique_ptrIKvSt8functionIFvPS3_EEEESaIS9_ENSt8__detail10_Select1stESt8equal_toImESt4hashImENSB_18_Mod_range_hashingENSB_20_Default_ranged_hashENSB_20_Prime_rehash_policyENSB_17_Hashtable_traitsILb0ELb0ELb1EEEE10_M_emplaceIJS0_ImS8_EEEES0_INSB_14_Node_iteratorIS9_Lb0ELb0EEEbESt17integral_constantIbLb1EEDpOT_ \
23+
// RUN: -e _ZNSt10_HashtableImSt4pairIKmSt10unique_ptrIKvSt8functionIFvPS3_EEEESaIS9_ENSt8__detail10_Select1stESt8equal_toImESt4hashImENSB_18_Mod_range_hashingENSB_20_Default_ranged_hashENSB_20_Prime_rehash_policyENSB_17_Hashtable_traitsILb0ELb0ELb1EEEE13_M_rehash_auxEmSt17integral_constantIbLb1EE \
24+
// RUN: -e _ZNSt10_HashtableImSt4pairIKmSt10unique_ptrIKvSt8functionIFvPS3_EEEESaIS9_ENSt8__detail10_Select1stESt8equal_toImESt4hashImENSB_18_Mod_range_hashingENSB_20_Default_ranged_hashENSB_20_Prime_rehash_policyENSB_17_Hashtable_traitsILb0ELb0ELb1EEEED2Ev \
2225
// RUN: -e _ZNSt3_V28__rotateIPcEET_S2_S2_S2_St26random_access_iterator_tag \
2326
// RUN: -e _ZN9__gnu_cxx12__to_xstringINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEcEET_PFiPT0_mPKS8_P13__va_list_tagEmSB_z \
2427
// RUN: -e _ZN9__gnu_cxx12__to_xstringINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEcEET_PFiPT0_mPKS8_St9__va_listEmSB_z \
@@ -38,6 +41,9 @@
3841
// RUN: -e _ZNSt6vectorIjSaIjEE13_M_insert_auxIJjEEEvN9__gnu_cxx17__normal_iteratorIPjS1_EEDpOT_ \
3942
// RUN: -e _ZNSt6vectorISt10unique_ptrIKvSt8functionIFvPS1_EEESaIS6_EE19_M_emplace_back_auxIJS6_EEEvDpOT_ \
4043
// RUN: -e _ZNSt6vectorISt10unique_ptrIKvSt8functionIFvPS1_EEESaIS6_EE17_M_realloc_insertIJS6_EEEvN9__gnu_cxx17__normal_iteratorIPS6_S8_EEDpOT_ \
44+
// RUN: -e _ZNSt10_HashtableImSt4pairIKmSt10unique_ptrIKvSt8functionIFvPS3_EEEESaIS9_ENSt8__detail10_Select1stESt8equal_toImESt4hashImENSB_18_Mod_range_hashingENSB_20_Default_ranged_hashENSB_20_Prime_rehash_policyENSB_17_Hashtable_traitsILb0ELb0ELb1EEEE10_M_emplaceIJS0_ImS8_EEEES0_INSB_14_Node_iteratorIS9_Lb0ELb0EEEbESt17integral_constantIbLb1EEDpOT_ \
45+
// RUN: -e _ZNSt10_HashtableImSt4pairIKmSt10unique_ptrIKvSt8functionIFvPS3_EEEESaIS9_ENSt8__detail10_Select1stESt8equal_toImESt4hashImENSB_18_Mod_range_hashingENSB_20_Default_ranged_hashENSB_20_Prime_rehash_policyENSB_17_Hashtable_traitsILb0ELb0ELb1EEEE13_M_rehash_auxEmSt17integral_constantIbLb1EE \
46+
// RUN: -e _ZNSt10_HashtableImSt4pairIKmSt10unique_ptrIKvSt8functionIFvPS3_EEEESaIS9_ENSt8__detail10_Select1stESt8equal_toImESt4hashImENSB_18_Mod_range_hashingENSB_20_Default_ranged_hashENSB_20_Prime_rehash_policyENSB_17_Hashtable_traitsILb0ELb0ELb1EEEED2Ev \
4147
// RUN: -e _ZNSt3_V28__rotateIPcEET_S2_S2_S2_St26random_access_iterator_tag \
4248
// RUN: -e _ZN9__gnu_cxx12__to_xstringINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEcEET_PFiPT0_mPKS8_P13__va_list_tagEmSB_z \
4349
// RUN: -e _ZN9__gnu_cxx12__to_xstringINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEcEET_PFiPT0_mPKS8_St9__va_listEmSB_z \

0 commit comments

Comments
 (0)