diff --git a/llvm/include/llvm/BinaryFormat/MachO.h b/llvm/include/llvm/BinaryFormat/MachO.h --- a/llvm/include/llvm/BinaryFormat/MachO.h +++ b/llvm/include/llvm/BinaryFormat/MachO.h @@ -1015,6 +1015,14 @@ uint32_t symbols_format; ///< 0 => uncompressed, 1 => zlib compressed }; +/// dyld_chained_starts_in_image is embedded in LC_DYLD_CHAINED_FIXUPS payload. +/// Each each seg_info_offset entry is the offset into this struct for that +/// segment followed by pool of dyld_chain_starts_in_segment data. +struct dyld_chained_starts_in_image { + uint32_t seg_count; + uint32_t seg_info_offset[1]; +}; + // Byte order swapping functions for MachO structs inline void swapStruct(fat_header &mh) { diff --git a/llvm/include/llvm/Object/MachO.h b/llvm/include/llvm/Object/MachO.h --- a/llvm/include/llvm/Object/MachO.h +++ b/llvm/include/llvm/Object/MachO.h @@ -683,6 +683,9 @@ ArrayRef getDyldInfoBindOpcodes() const; ArrayRef getDyldInfoWeakBindOpcodes() const; ArrayRef getDyldInfoLazyBindOpcodes() const; + /// If the optional is None, no header was found, but the object was well-formed. + Expected> + getChainedFixupsHeader() const; Expected> getDyldChainedFixupTargets() const; ArrayRef getDyldInfoExportsTrie() const; SmallVector getFunctionStarts() const; diff --git a/llvm/lib/Object/MachOObjectFile.cpp b/llvm/lib/Object/MachOObjectFile.cpp --- a/llvm/lib/Object/MachOObjectFile.cpp +++ b/llvm/lib/Object/MachOObjectFile.cpp @@ -3259,13 +3259,13 @@ bool Parse) : MachOAbstractFixupEntry(E, O) { ErrorAsOutParameter e(E); - if (Parse) { - if (auto FixupTargetsOrErr = O->getDyldChainedFixupTargets()) - FixupTargets = *FixupTargetsOrErr; - else { - *E = FixupTargetsOrErr.takeError(); - return; - } + if (!Parse) + return; + if (auto FixupTargetsOrErr = O->getDyldChainedFixupTargets()) + FixupTargets = *FixupTargetsOrErr; + else { + *E = FixupTargetsOrErr.takeError(); + return; } } @@ -4763,11 +4763,11 @@ return makeArrayRef(Ptr, DyldInfo.lazy_bind_size); } -Expected> -MachOObjectFile::getDyldChainedFixupTargets() const { +Expected> +MachOObjectFile::getChainedFixupsHeader() const { // Load the dyld chained fixups load command. if (!DyldChainedFixupsLoadCmd) - return std::vector(); + return llvm::None; auto DyldChainedFixupsOrErr = getStructOrErr( *this, DyldChainedFixupsLoadCmd); if (!DyldChainedFixupsOrErr) @@ -4775,11 +4775,10 @@ MachO::linkedit_data_command DyldChainedFixups = DyldChainedFixupsOrErr.get(); // If the load command is present but the data offset has been zeroed out, - // as is the case for dylib stubs, return an empty list of targets. + // as is the case for dylib stubs, return None (no error). uint64_t CFHeaderOffset = DyldChainedFixups.dataoff; - std::vector Targets; if (CFHeaderOffset == 0) - return Targets; + return DyldChainedFixupsOrErr.takeError(); // Load the dyld chained fixups header. const char *CFHeaderPtr = getPtr(*this, CFHeaderOffset); @@ -4798,6 +4797,35 @@ Twine("bad chained fixups: unknown imports format: ") + Twine(CFHeader.imports_format)); + // Validate the image format. + // + // Load the image starts. + uint64_t CFImageStartsOffset = (CFHeaderOffset + CFHeader.starts_offset); + if (CFHeader.starts_offset < sizeof(MachO::dyld_chained_fixups_header)) { + return malformedError(Twine("bad chained fixups: image starts offset ") + + Twine(CFHeader.starts_offset) + + " overlaps with chained fixups header"); + } + uint32_t EndOffset = DyldChainedFixups.dataoff + DyldChainedFixups.datasize; + if (CFImageStartsOffset + sizeof(MachO::dyld_chained_starts_in_image) > + EndOffset) { + return malformedError(Twine("bad chained fixups: image starts end ") + + Twine(CFImageStartsOffset + + sizeof(MachO::dyld_chained_starts_in_image)) + + " extends past end " + Twine(EndOffset)); + } + + return CFHeader; +} + +Expected> +MachOObjectFile::getDyldChainedFixupTargets() const { + auto CFHeaderOrErr = getChainedFixupsHeader(); + if (!CFHeaderOrErr) + return CFHeaderOrErr.takeError(); + std::vector Targets; + if (!(*CFHeaderOrErr)) + return Targets; return Targets; } diff --git a/llvm/test/Object/AArch64/chained-fixups-header.test b/llvm/test/Object/AArch64/chained-fixups-header.test --- a/llvm/test/Object/AArch64/chained-fixups-header.test +++ b/llvm/test/Object/AArch64/chained-fixups-header.test @@ -10,3 +10,15 @@ RUN: | yaml2obj | not llvm-objdump --macho --dyld_info - 2>&1 \ RUN: | FileCheck %s --check-prefix=HEADER2 HEADER2: truncated or malformed object (bad chained fixups: unknown imports format: 171) + +RUN: cat %p/../Inputs/MachO/chained-fixups.yaml \ +RUN: | sed 's/20000000/01000000/' \ +RUN: | yaml2obj | not llvm-objdump --macho --dyld_info - 2>&1 \ +RUN: | FileCheck %s --check-prefix=HEADER3 +HEADER3: truncated or malformed object (bad chained fixups: image starts offset 1 overlaps with chained fixups header) + +RUN: cat %p/../Inputs/MachO/chained-fixups.yaml \ +RUN: | sed 's/20000000/FF000000/' \ +RUN: | yaml2obj | not llvm-objdump --macho --dyld_info - 2>&1 \ +RUN: | FileCheck %s --check-prefix=HEADER4 +HEADER4: truncated or malformed object (bad chained fixups: image starts end 33031 extends past end 32856)