From ebd9c215ecaa01d0c15313dff5d75296c05f48b4 Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Thu, 10 Jun 2021 13:51:15 -0400 Subject: [PATCH] [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 --- include/swift/Remote/MetadataReader.h | 70 +++++++++++---------- test/stdlib/symbol-visibility-linux.test-sh | 6 ++ 2 files changed, 42 insertions(+), 34 deletions(-) diff --git a/include/swift/Remote/MetadataReader.h b/include/swift/Remote/MetadataReader.h index b6651d24959ce..091098611f518 100644 --- a/include/swift/Remote/MetadataReader.h +++ b/include/swift/Remote/MetadataReader.h @@ -170,21 +170,24 @@ class MetadataReader { using StoredSize = typename Runtime::StoredSize; private: + /// The maximum number of bytes to read when reading metadata. Anything larger + /// will automatically return failure. This prevents us from reading absurd + /// amounts of data when we encounter corrupt values for sizes/counts. + static const uint64_t MaxMetadataSize = 1048576; // 1MB + /// A cache of built types, keyed by the address of the type. std::unordered_map TypeCache; - using MetadataRef = RemoteRef>; - using OwnedMetadataRef = - std::unique_ptr, delete_with_free>; + using MetadataRef = RemoteRef>; + using OwnedMetadataRef = MemoryReader::ReadBytesResult; /// A cache of read type metadata, keyed by the address of the metadata. std::unordered_map MetadataCache; - using ContextDescriptorRef = RemoteRef>; - using OwnedContextDescriptorRef = - std::unique_ptr, - delete_with_free>; + using ContextDescriptorRef = + RemoteRef>; + using OwnedContextDescriptorRef = MemoryReader::ReadBytesResult; /// A reference to a context descriptor that may be in an unloaded image. class ParentContextDescriptorRef { @@ -984,7 +987,9 @@ class MetadataReader { auto cached = ContextDescriptorCache.find(address); if (cached != ContextDescriptorCache.end()) - return ContextDescriptorRef(address, cached->second.get()); + return ContextDescriptorRef( + address, reinterpret_cast *>( + cached->second.get())); // Read the flags to figure out how much space we should read. ContextDescriptorFlags flags; @@ -993,9 +998,9 @@ class MetadataReader { return nullptr; TypeContextDescriptorFlags typeFlags(flags.getKindSpecificFlags()); - unsigned baseSize = 0; - unsigned genericHeaderSize = sizeof(GenericContextDescriptorHeader); - unsigned metadataInitSize = 0; + uint64_t baseSize = 0; + uint64_t genericHeaderSize = sizeof(GenericContextDescriptorHeader); + uint64_t metadataInitSize = 0; bool hasVTable = false; auto readMetadataInitSize = [&]() -> unsigned { @@ -1059,7 +1064,7 @@ class MetadataReader { // Determine the full size of the descriptor. This is reimplementing a fair // bit of TrailingObjects but for out-of-process; maybe there's a way to // factor the layout stuff out... - unsigned genericsSize = 0; + uint64_t genericsSize = 0; if (flags.isGeneric()) { GenericContextDescriptorHeader header; auto headerAddr = address @@ -1077,7 +1082,7 @@ class MetadataReader { * sizeof(TargetGenericRequirementDescriptor); } - unsigned vtableSize = 0; + uint64_t vtableSize = 0; if (hasVTable) { TargetVTableDescriptorHeader header; auto headerAddr = address @@ -1092,22 +1097,20 @@ class MetadataReader { vtableSize = sizeof(header) + header.VTableSize * sizeof(TargetMethodDescriptor); } - - unsigned size = baseSize + genericsSize + metadataInitSize + vtableSize; - auto buffer = (uint8_t *)malloc(size); - if (buffer == nullptr) { + + uint64_t size = baseSize + genericsSize + metadataInitSize + vtableSize; + if (size > MaxMetadataSize) return nullptr; - } - if (!Reader->readBytes(RemoteAddress(address), buffer, size)) { - free(buffer); + auto readResult = Reader->readBytes(RemoteAddress(address), size); + if (!readResult) return nullptr; - } - auto descriptor - = reinterpret_cast *>(buffer); + auto descriptor = + reinterpret_cast *>( + readResult.get()); ContextDescriptorCache.insert( - std::make_pair(address, OwnedContextDescriptorRef(descriptor))); + std::make_pair(address, std::move(readResult))); return ContextDescriptorRef(address, descriptor); } @@ -1633,7 +1636,9 @@ class MetadataReader { MetadataRef readMetadata(StoredPointer address) { auto cached = MetadataCache.find(address); if (cached != MetadataCache.end()) - return MetadataRef(address, cached->second.get()); + return MetadataRef(address, + reinterpret_cast *>( + cached->second.get())); StoredPointer KindValue = 0; if (!Reader->readInteger(RemoteAddress(address), &KindValue)) @@ -1807,18 +1812,15 @@ class MetadataReader { } MetadataRef _readMetadata(StoredPointer address, size_t sizeAfter) { - auto size = sizeAfter; - uint8_t *buffer = (uint8_t *) malloc(size); - if (!buffer) + if (sizeAfter > MaxMetadataSize) return nullptr; - - if (!Reader->readBytes(RemoteAddress(address), buffer, size)) { - free(buffer); + auto readResult = Reader->readBytes(RemoteAddress(address), sizeAfter); + if (!readResult) return nullptr; - } - auto metadata = reinterpret_cast*>(buffer); - MetadataCache.insert(std::make_pair(address, OwnedMetadataRef(metadata))); + auto metadata = + reinterpret_cast *>(readResult.get()); + MetadataCache.insert(std::make_pair(address, std::move(readResult))); return MetadataRef(address, metadata); } diff --git a/test/stdlib/symbol-visibility-linux.test-sh b/test/stdlib/symbol-visibility-linux.test-sh index 22c9f03c494cb..ae544917060d0 100644 --- a/test/stdlib/symbol-visibility-linux.test-sh +++ b/test/stdlib/symbol-visibility-linux.test-sh @@ -19,6 +19,9 @@ // RUN: -e _ZNSt6vectorIjSaIjEE13_M_insert_auxIJjEEEvN9__gnu_cxx17__normal_iteratorIPjS1_EEDpOT_ \ // RUN: -e _ZNSt6vectorISt10unique_ptrIKvSt8functionIFvPS1_EEESaIS6_EE19_M_emplace_back_auxIJS6_EEEvDpOT_ \ // RUN: -e _ZNSt6vectorISt10unique_ptrIKvSt8functionIFvPS1_EEESaIS6_EE17_M_realloc_insertIJS6_EEEvN9__gnu_cxx17__normal_iteratorIPS6_S8_EEDpOT_ \ +// 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_ \ +// 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 \ +// 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 \ // RUN: -e _ZNSt3_V28__rotateIPcEET_S2_S2_S2_St26random_access_iterator_tag \ // RUN: -e _ZN9__gnu_cxx12__to_xstringINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEcEET_PFiPT0_mPKS8_P13__va_list_tagEmSB_z \ // RUN: -e _ZN9__gnu_cxx12__to_xstringINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEcEET_PFiPT0_mPKS8_St9__va_listEmSB_z \ @@ -38,6 +41,9 @@ // RUN: -e _ZNSt6vectorIjSaIjEE13_M_insert_auxIJjEEEvN9__gnu_cxx17__normal_iteratorIPjS1_EEDpOT_ \ // RUN: -e _ZNSt6vectorISt10unique_ptrIKvSt8functionIFvPS1_EEESaIS6_EE19_M_emplace_back_auxIJS6_EEEvDpOT_ \ // RUN: -e _ZNSt6vectorISt10unique_ptrIKvSt8functionIFvPS1_EEESaIS6_EE17_M_realloc_insertIJS6_EEEvN9__gnu_cxx17__normal_iteratorIPS6_S8_EEDpOT_ \ +// 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_ \ +// 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 \ +// 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 \ // RUN: -e _ZNSt3_V28__rotateIPcEET_S2_S2_S2_St26random_access_iterator_tag \ // RUN: -e _ZN9__gnu_cxx12__to_xstringINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEcEET_PFiPT0_mPKS8_P13__va_list_tagEmSB_z \ // RUN: -e _ZN9__gnu_cxx12__to_xstringINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEcEET_PFiPT0_mPKS8_St9__va_listEmSB_z \