// Parses LC_SEGMENT or LC_SEGMENT_64 load command, adds addresses of all
// sections to \param Sections, and optionally sets
// \param IsPageZeroSegment to true.
-template <typename SegmentCmd>
+template <typename Segment, typename Section>
static Error parseSegmentLoadCommand(
const MachOObjectFile *Obj, const MachOObjectFile::LoadCommandInfo &Load,
SmallVectorImpl<const char *> &Sections, bool &IsPageZeroSegment,
- uint32_t LoadCommandIndex, const char *CmdName) {
- const unsigned SegmentLoadSize = sizeof(SegmentCmd);
+ uint32_t LoadCommandIndex, const char *CmdName, uint64_t SizeOfHeaders) {
+ const unsigned SegmentLoadSize = sizeof(Segment);
if (Load.C.cmdsize < SegmentLoadSize)
return malformedError("load command " + Twine(LoadCommandIndex) +
" " + CmdName + " cmdsize too small");
- if (auto SegOrErr = getStructOrErr<SegmentCmd>(Obj, Load.Ptr)) {
- SegmentCmd S = SegOrErr.get();
- const unsigned SectionSize =
- Obj->is64Bit() ? sizeof(MachO::section_64) : sizeof(MachO::section);
+ if (auto SegOrErr = getStructOrErr<Segment>(Obj, Load.Ptr)) {
+ Segment S = SegOrErr.get();
+ const unsigned SectionSize = sizeof(Section);
+ uint64_t FileSize = Obj->getData().size();
if (S.nsects > std::numeric_limits<uint32_t>::max() / SectionSize ||
S.nsects * SectionSize > Load.C.cmdsize - SegmentLoadSize)
return malformedError("load command " + Twine(LoadCommandIndex) +
for (unsigned J = 0; J < S.nsects; ++J) {
const char *Sec = getSectionPtr(Obj, Load, J);
Sections.push_back(Sec);
+ Section s = getStruct<Section>(Obj, Sec);
+ if (Obj->getHeader().filetype != MachO::MH_DYLIB_STUB &&
+ Obj->getHeader().filetype != MachO::MH_DSYM &&
+ s.flags != MachO::S_ZEROFILL &&
+ s.flags != MachO::S_THREAD_LOCAL_ZEROFILL &&
+ s.offset > FileSize)
+ return malformedError("offset field of section " + Twine(J) + " in " +
+ CmdName + " command " + Twine(LoadCommandIndex) +
+ " extends past the end of the file");
+ if (Obj->getHeader().filetype != MachO::MH_DYLIB_STUB &&
+ Obj->getHeader().filetype != MachO::MH_DSYM &&
+ s.flags != MachO::S_ZEROFILL &&
+ s.flags != MachO::S_THREAD_LOCAL_ZEROFILL &&
+ S.fileoff == 0 && s.offset < SizeOfHeaders && s.size != 0)
+ return malformedError("offset field of section " + Twine(J) + " in " +
+ CmdName + " command " + Twine(LoadCommandIndex) +
+ " not past the headers of the file");
+ uint64_t BigSize = s.offset;
+ BigSize += s.size;
+ if (Obj->getHeader().filetype != MachO::MH_DYLIB_STUB &&
+ Obj->getHeader().filetype != MachO::MH_DSYM &&
+ s.flags != MachO::S_ZEROFILL &&
+ s.flags != MachO::S_THREAD_LOCAL_ZEROFILL &&
+ BigSize > FileSize)
+ return malformedError("offset field plus size field of section " +
+ Twine(J) + " in " + CmdName + " command " +
+ Twine(LoadCommandIndex) +
+ " extends past the end of the file");
+ if (Obj->getHeader().filetype != MachO::MH_DYLIB_STUB &&
+ Obj->getHeader().filetype != MachO::MH_DSYM &&
+ s.flags != MachO::S_ZEROFILL &&
+ s.flags != MachO::S_THREAD_LOCAL_ZEROFILL &&
+ s.size > S.filesize)
+ return malformedError("size field of section " +
+ Twine(J) + " in " + CmdName + " command " +
+ Twine(LoadCommandIndex) +
+ " greater than the segment");
+ if (Obj->getHeader().filetype != MachO::MH_DYLIB_STUB &&
+ Obj->getHeader().filetype != MachO::MH_DSYM &&
+ s.size != 0 && s.addr < S.vmaddr)
+ return malformedError("addr field of section " +
+ Twine(J) + " in " + CmdName + " command " +
+ Twine(LoadCommandIndex) +
+ " less than the segment's vmaddr");
+ BigSize = s.addr;
+ BigSize += s.size;
+ uint64_t BigEnd = S.vmaddr;
+ BigEnd += S.vmsize;
+ if (S.vmsize != 0 && s.size != 0 && BigSize > BigEnd)
+ return malformedError("addr field plus size of section " +
+ Twine(J) + " in " + CmdName + " command " +
+ Twine(LoadCommandIndex) + " greater than than "
+ "the segment's vmaddr plus vmsize");
+ if (s.reloff > FileSize)
+ return malformedError("reloff field of section " +
+ Twine(J) + " in " + CmdName + " command " +
+ Twine(LoadCommandIndex) +
+ " extends past the end of the file");
+ BigSize = s.nreloc;
+ BigSize *= sizeof(struct MachO::relocation_info);
+ BigSize += s.reloff;
+ if (BigSize > FileSize)
+ return malformedError("reloff field plus nreloc field times sizeof("
+ "struct relocation_info) of section " +
+ Twine(J) + " in " + CmdName + " command " +
+ Twine(LoadCommandIndex) +
+ " extends past the end of the file");
}
- uint64_t FileSize = Obj->getData().size();
if (S.fileoff > FileSize)
return malformedError("load command " + Twine(LoadCommandIndex) +
" fileoff field in " + CmdName +
" extends past the end of the file");
+ uint64_t BigSize = S.fileoff;
+ BigSize += S.filesize;
+ if (BigSize > FileSize)
+ return malformedError("load command " + Twine(LoadCommandIndex) +
+ " fileoff field plus filesize field in " +
+ CmdName + " extends past the end of the file");
+ if (S.vmsize != 0 && S.filesize > S.vmsize)
+ return malformedError("load command " + Twine(LoadCommandIndex) +
+ " fileoff field in " + CmdName +
+ " greater than vmsize field");
IsPageZeroSegment |= StringRef("__PAGEZERO").equals(S.segname);
} else
return SegOrErr.takeError();
DyldInfoLoadCmd(nullptr), UuidLoadCmd(nullptr),
HasPageZeroSegment(false) {
ErrorAsOutParameter ErrAsOutParam(&Err);
- uint64_t BigSize;
+ uint64_t SizeOfHeaders;
if (is64Bit()) {
parseHeader(this, Header64, Err);
- BigSize = sizeof(MachO::mach_header_64);
+ SizeOfHeaders = sizeof(MachO::mach_header_64);
} else {
parseHeader(this, Header, Err);
- BigSize = sizeof(MachO::mach_header);
+ SizeOfHeaders = sizeof(MachO::mach_header);
}
if (Err)
return;
- BigSize += getHeader().sizeofcmds;
- if (getData().data() + BigSize > getData().end()) {
+ SizeOfHeaders += getHeader().sizeofcmds;
+ if (getData().data() + SizeOfHeaders > getData().end()) {
Err = malformedError("load commands extend past the end of the file");
return;
}
}
UuidLoadCmd = Load.Ptr;
} else if (Load.C.cmd == MachO::LC_SEGMENT_64) {
- if ((Err = parseSegmentLoadCommand<MachO::segment_command_64>(
+ if ((Err = parseSegmentLoadCommand<MachO::segment_command_64,
+ MachO::section_64>(
this, Load, Sections, HasPageZeroSegment, I,
- "LC_SEGMENT_64")))
+ "LC_SEGMENT_64", SizeOfHeaders)))
return;
} else if (Load.C.cmd == MachO::LC_SEGMENT) {
- if ((Err = parseSegmentLoadCommand<MachO::segment_command>(
- this, Load, Sections, HasPageZeroSegment, I, "LC_SEGMENT")))
+ if ((Err = parseSegmentLoadCommand<MachO::segment_command,
+ MachO::section>(
+ this, Load, Sections, HasPageZeroSegment, I,
+ "LC_SEGMENT", SizeOfHeaders)))
return;
} else if (Load.C.cmd == MachO::LC_LOAD_DYLIB ||
Load.C.cmd == MachO::LC_LOAD_WEAK_DYLIB ||
RUN: not llvm-size %p/Inputs/macho-invalid-segment-fileoff 2>&1 | FileCheck -check-prefix INVALID-SEGMENT-FILEOFF-SIZE %s
INVALID-SEGMENT-FILEOFF-SIZE: macho-invalid-segment-fileoff truncated or malformed object (load command 0 fileoff field in LC_SEGMENT extends past the end of the file)
+
+RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-segment-filesize 2>&1 | FileCheck -check-prefix INVALID-SEGMENT-FILESIZE %s
+INVALID-SEGMENT-FILESIZE: macho-invalid-segment-filesize': truncated or malformed object (load command 0 fileoff field plus filesize field in LC_SEGMENT extends past the end of the file)
+
+RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-segment-vmsize 2>&1 | FileCheck -check-prefix INVALID-SEGMENT-VMSIZE %s
+INVALID-SEGMENT-VMSIZE: macho-invalid-segment-vmsize': truncated or malformed object (load command 0 fileoff field in LC_SEGMENT greater than vmsize field)
+
+RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-section-offset 2>&1 | FileCheck -check-prefix INVALID-SECTION-FILEOFF %s
+INVALID-SECTION-FILEOFF: macho-invalid-section-offset': truncated or malformed object (offset field of section 0 in LC_SEGMENT command 0 extends past the end of the file)
+
+RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-section-offset-in-headers 2>&1 | FileCheck -check-prefix INVALID-SECTION-FILEOFF-IN-HEADERS %s
+INVALID-SECTION-FILEOFF-IN-HEADERS: macho-invalid-section-offset-in-headers': truncated or malformed object (offset field of section 0 in LC_SEGMENT command 0 not past the headers of the file)
+
+RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-section-offset-size 2>&1 | FileCheck -check-prefix INVALID-SECTION-FILEOFF-SIZE %s
+INVALID-SECTION-FILEOFF-SIZE: macho-invalid-section-offset-size': truncated or malformed object (offset field plus size field of section 0 in LC_SEGMENT command 0 extends past the end of the file)
+
+RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-section-size-filesize 2>&1 | FileCheck -check-prefix INVALID-SECTION-SIZE-FILESIZE %s
+INVALID-SECTION-SIZE-FILESIZE: macho-invalid-section-size-filesize': truncated or malformed object (size field of section 0 in LC_SEGMENT command 0 greater than the segment)
+
+RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-section-addr 2>&1 | FileCheck -check-prefix INVALID-SECTION-ADDR %s
+INVALID-SECTION-ADDR: macho-invalid-section-addr': truncated or malformed object (addr field of section 0 in LC_SEGMENT command 0 less than the segment's vmaddr)
+
+RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-section-addr-size 2>&1 | FileCheck -check-prefix INVALID-SECTION-ADDR-SIZE %s
+INVALID-SECTION-ADDR-SIZE: macho-invalid-section-addr-size': truncated or malformed object (addr field plus size of section 0 in LC_SEGMENT command 0 greater than than the segment's vmaddr plus vmsize)
+
+RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-section-reloff 2>&1 | FileCheck -check-prefix INVALID-SECTION-RELOFF %s
+INVALID-SECTION-RELOFF: macho-invalid-section-reloff': truncated or malformed object (reloff field of section 0 in LC_SEGMENT command 0 extends past the end of the file)
+
+RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-section-reloff-nrelocs 2>&1 | FileCheck -check-prefix INVALID-SECTION-RELOFF-NRELOCS %s
+INVALID-SECTION-RELOFF-NRELOCS: macho-invalid-section-reloff-nrelocs': truncated or malformed object (reloff field plus nreloc field times sizeof(struct relocation_info) of section 0 in LC_SEGMENT command 0 extends past the end of the file)
// These test checks that llvm-objdump will not crash with malformed Mach-O
// files. So the check line is not all that important but the bug fixes to
// make sure llvm-objdump is robust is what matters.
-# RUN: llvm-objdump -macho -objc-meta-data \
-# RUN: %p/Inputs/malformed-machos/mem-crup-0001.macho \
+# RUN: not llvm-objdump -macho -objc-meta-data \
+# RUN: %p/Inputs/malformed-machos/mem-crup-0001.macho 2>&1 \
# RUN: | FileCheck -check-prefix=m0001 %s
-# m0001: (method_t extends past the end of the section)
+# m0001: mem-crup-0001.macho': truncated or malformed object (addr field plus size of section 2 in LC_SEGMENT_64 command 0 greater than than the segment's vmaddr plus vmsize)
# RUN: llvm-objdump -macho -objc-meta-data \
# RUN: %p/Inputs/malformed-machos/mem-crup-0006.macho \
# m0010: 00000000000010e0 0x10e8 _OBJC_CLASS_
-# RUN: llvm-objdump -macho -objc-meta-data \
-# RUN: %p/Inputs/malformed-machos/mem-crup-0040.macho \
+# RUN: not llvm-objdump -macho -objc-meta-data \
+# RUN: %p/Inputs/malformed-machos/mem-crup-0040.macho 2>&1 \
# RUN: | FileCheck -check-prefix=m0040 %s
-# m0040: 00000000000010a0 0xf39 -[tiny_dylib init]
+# m0040: mem-crup-0040.macho': truncated or malformed object (offset field plus size field of section 2 in LC_SEGMENT_64 command 1 extends past the end of the file)
-# RUN: llvm-objdump -macho -objc-meta-data \
-# RUN: %p/Inputs/malformed-machos/mem-crup-0080.macho \
+# RUN: not llvm-objdump -macho -objc-meta-data \
+# RUN: %p/Inputs/malformed-machos/mem-crup-0080.macho 2>&1 \
# RUN: | FileCheck -check-prefix=m0080 %s
-# m0080: data 0xf960000 (struct class_ro_t *)
+# m0080: mem-crup-0080.macho': truncated or malformed object (addr field plus size of section 2 in LC_SEGMENT_64 command 1 greater than than the segment's vmaddr plus vmsize)
# RUN: llvm-objdump -macho -objc-meta-data \
# RUN: %p/Inputs/malformed-machos/mem-crup-0261.macho