From c614d283b7aecbc9bee0b7bdb7902d2796b04a8c Mon Sep 17 00:00:00 2001 From: Kevin Enderby Date: Fri, 12 Aug 2016 20:10:25 +0000 Subject: Next set of additional error checks for invalid Mach-O files. This contains the two missing checks for LC_SEGMENT load command fields. And checks for the Mach-O sections fields that would make them invalid. With the new checks, some of the existing malformed file checks now trips one of these instead of the issue it was having before so those tests were adjusted. llvm-svn: 278557 --- llvm/lib/Object/MachOObjectFile.cpp | 113 ++++++++++++++++++++++++++++++------ 1 file changed, 96 insertions(+), 17 deletions(-) (limited to 'llvm/lib/Object/MachOObjectFile.cpp') diff --git a/llvm/lib/Object/MachOObjectFile.cpp b/llvm/lib/Object/MachOObjectFile.cpp index 863081f..f34e687 100644 --- a/llvm/lib/Object/MachOObjectFile.cpp +++ b/llvm/lib/Object/MachOObjectFile.cpp @@ -219,19 +219,19 @@ static void parseHeader(const MachOObjectFile *Obj, T &Header, // 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 +template static Error parseSegmentLoadCommand( const MachOObjectFile *Obj, const MachOObjectFile::LoadCommandInfo &Load, SmallVectorImpl &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(Obj, Load.Ptr)) { - SegmentCmd S = SegOrErr.get(); - const unsigned SectionSize = - Obj->is64Bit() ? sizeof(MachO::section_64) : sizeof(MachO::section); + if (auto SegOrErr = getStructOrErr(Obj, Load.Ptr)) { + Segment S = SegOrErr.get(); + const unsigned SectionSize = sizeof(Section); + uint64_t FileSize = Obj->getData().size(); if (S.nsects > std::numeric_limits::max() / SectionSize || S.nsects * SectionSize > Load.C.cmdsize - SegmentLoadSize) return malformedError("load command " + Twine(LoadCommandIndex) + @@ -240,12 +240,88 @@ static Error parseSegmentLoadCommand( for (unsigned J = 0; J < S.nsects; ++J) { const char *Sec = getSectionPtr(Obj, Load, J); Sections.push_back(Sec); + Section s = getStruct
(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(); @@ -273,18 +349,18 @@ MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian, 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; } @@ -366,13 +442,16 @@ MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian, } UuidLoadCmd = Load.Ptr; } else if (Load.C.cmd == MachO::LC_SEGMENT_64) { - if ((Err = parseSegmentLoadCommand( + if ((Err = parseSegmentLoadCommand( this, Load, Sections, HasPageZeroSegment, I, - "LC_SEGMENT_64"))) + "LC_SEGMENT_64", SizeOfHeaders))) return; } else if (Load.C.cmd == MachO::LC_SEGMENT) { - if ((Err = parseSegmentLoadCommand( - this, Load, Sections, HasPageZeroSegment, I, "LC_SEGMENT"))) + if ((Err = parseSegmentLoadCommand( + 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 || -- cgit v1.1