// Copyright 2014 The Crashpad Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "snapshot/mac/process_types.h" #include #include #include #include #include #include #include "base/check_op.h" #include "base/cxx17_backports.h" #include "base/logging.h" #include "base/numerics/safe_math.h" #include "base/strings/stringprintf.h" #include "snapshot/mac/process_types/internal.h" #include "util/mac/mac_util.h" #include "util/process/process_memory_mac.h" #if !DOXYGEN namespace crashpad { namespace process_types { namespace internal { namespace { template bool ReadIntoAndZero(const ProcessMemoryMac* process_memory, mach_vm_address_t address, mach_vm_size_t size, T* specific) { DCHECK_LE(size, sizeof(*specific)); if (!process_memory->Read(address, size, specific)) { return false; } // Zero out the rest of the structure in case anything accesses fields without // checking the version or size. const size_t remaining = sizeof(*specific) - size; if (remaining > 0) { char* const start = reinterpret_cast(specific) + size; memset(start, 0, remaining); } return true; } template bool FieldAddressIfInRange(mach_vm_address_t address, size_t offset, mach_vm_address_t* field_address) { base::CheckedNumeric checked_field_address(address); checked_field_address += offset; typename T::Pointer local_field_address; if (!checked_field_address.AssignIfValid(&local_field_address)) { LOG(ERROR) << base::StringPrintf( "address 0x%llx + offset 0x%zx out of range", address, offset); return false; } *field_address = local_field_address; return true; } template bool ReadIntoVersioned(ProcessReaderMac* process_reader, mach_vm_address_t address, T* specific) { mach_vm_address_t field_address; if (!FieldAddressIfInRange( address, offsetof(T, version), &field_address)) { return false; } const ProcessMemoryMac* process_memory = process_reader->Memory(); decltype(specific->version) version; if (!process_memory->Read(field_address, sizeof(version), &version)) { return false; } const size_t size = T::ExpectedSizeForVersion(version); return ReadIntoAndZero(process_memory, address, size, specific); } template bool ReadIntoSized(ProcessReaderMac* process_reader, mach_vm_address_t address, T* specific) { mach_vm_address_t field_address; if (!FieldAddressIfInRange(address, offsetof(T, size), &field_address)) { return false; } const ProcessMemoryMac* process_memory = process_reader->Memory(); decltype(specific->size) size; if (!process_memory->Read(address + offsetof(T, size), sizeof(size), &size)) { return false; } if (size < T::MinimumSize()) { LOG(ERROR) << "small size " << size; return false; } size = std::min(static_cast(size), sizeof(*specific)); return ReadIntoAndZero(process_memory, address, size, specific); } } // namespace // static template size_t dyld_all_image_infos::ExpectedSizeForVersion( decltype(dyld_all_image_infos::version) version) { static constexpr size_t kSizeForVersion[] = { offsetof(dyld_all_image_infos, infoArrayCount), // 0 offsetof(dyld_all_image_infos, libSystemInitialized), // 1 offsetof(dyld_all_image_infos, jitInfo), // 2 offsetof(dyld_all_image_infos, dyldVersion), // 3 offsetof(dyld_all_image_infos, dyldVersion), // 4 offsetof(dyld_all_image_infos, coreSymbolicationShmPage), // 5 offsetof(dyld_all_image_infos, systemOrderFlag), // 6 offsetof(dyld_all_image_infos, uuidArrayCount), // 7 offsetof(dyld_all_image_infos, dyldAllImageInfosAddress), // 8 offsetof(dyld_all_image_infos, initialImageCount), // 9 offsetof(dyld_all_image_infos, errorKind), // 10 offsetof(dyld_all_image_infos, sharedCacheSlide), // 11 offsetof(dyld_all_image_infos, sharedCacheUUID), // 12 offsetof(dyld_all_image_infos, infoArrayChangeTimestamp), // 13 offsetof(dyld_all_image_infos, end_v14), // 14 std::numeric_limits::max(), // 15, see below offsetof(dyld_all_image_infos, end_v16), // 16 sizeof(dyld_all_image_infos), // 17 }; if (version >= base::size(kSizeForVersion)) { return kSizeForVersion[base::size(kSizeForVersion) - 1]; } static_assert(std::is_unsigned::value, "version must be unsigned"); if (version == 15) { // Disambiguate between the two different layouts for version 15. The // original one introduced in macOS 10.12 had the same size as version 14. // The revised one in macOS 10.13 grew. It’s safe to assume that the // dyld_all_image_infos structure came from the same system that’s now // interpreting it, so use an OS version check. const int macos_version_number = MacOSVersionNumber(); if (macos_version_number / 1'00 == 10'12) { return offsetof(dyld_all_image_infos, end_v14); } DCHECK_GE(macos_version_number, 10'13'00); DCHECK_LT(macos_version_number, 10'15'00); return offsetof(dyld_all_image_infos, platform); } size_t size = kSizeForVersion[version]; DCHECK_NE(size, std::numeric_limits::max()); return size; } // static template bool dyld_all_image_infos::ReadInto( ProcessReaderMac* process_reader, mach_vm_address_t address, dyld_all_image_infos* specific) { return ReadIntoVersioned(process_reader, address, specific); } // static template size_t crashreporter_annotations_t::ExpectedSizeForVersion( decltype(crashreporter_annotations_t::version) version) { if (version >= 5) { return sizeof(crashreporter_annotations_t); } if (version >= 4) { return offsetof(crashreporter_annotations_t, unknown_0); } return offsetof(crashreporter_annotations_t, message); } // static template bool crashreporter_annotations_t::ReadInto( ProcessReaderMac* process_reader, mach_vm_address_t address, crashreporter_annotations_t* specific) { return ReadIntoVersioned(process_reader, address, specific); } // static template bool CrashpadInfo::ReadInto(ProcessReaderMac* process_reader, mach_vm_address_t address, CrashpadInfo* specific) { return ReadIntoSized(process_reader, address, specific); } // Explicit template instantiation of the above. #define PROCESS_TYPE_FLAVOR_TRAITS(lp_bits) \ template size_t \ dyld_all_image_infos::ExpectedSizeForVersion( \ decltype(dyld_all_image_infos::version)); \ template bool dyld_all_image_infos::ReadInto( \ ProcessReaderMac*, \ mach_vm_address_t, \ dyld_all_image_infos*); \ template size_t \ crashreporter_annotations_t::ExpectedSizeForVersion( \ decltype(crashreporter_annotations_t::version)); \ template bool crashreporter_annotations_t::ReadInto( \ ProcessReaderMac*, \ mach_vm_address_t, \ crashreporter_annotations_t*); \ template bool CrashpadInfo::ReadInto( \ ProcessReaderMac*, mach_vm_address_t, CrashpadInfo*); #include "snapshot/mac/process_types/flavors.h" #undef PROCESS_TYPE_FLAVOR_TRAITS } // namespace internal } // namespace process_types } // namespace crashpad #endif // !DOXYGEN