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 @@ -35,6 +35,7 @@ #include "llvm/Support/Format.h" #include "llvm/Support/Host.h" #include "llvm/Support/LEB128.h" +#include "llvm/Support/MathExtras.h" #include "llvm/Support/MemoryBufferRef.h" #include "llvm/Support/Path.h" #include "llvm/Support/SwapByteOrder.h" @@ -84,7 +85,11 @@ template static Expected getStructOrErr(const MachOObjectFile &O, const char *P) { // Don't read before the beginning or past the end of the file - if (P < O.getData().begin() || P + sizeof(T) > O.getData().end()) + bool Overflowed = false; + uintptr_t Ret = llvm::SaturatingAdd(reinterpret_cast(P), + sizeof(T), &Overflowed); + if (P < O.getData().begin() || Overflowed || + Ret > reinterpret_cast(O.getData().end())) return malformedError("Structure read out-of-range"); T Cmd; @@ -188,12 +193,44 @@ } static Expected -getLoadCommandInfo(const MachOObjectFile &Obj, const char *Ptr, - uint32_t LoadCommandIndex) { +getLoadCommandInfo(const MachOObjectFile &Obj, const char *BasePtr, + uint32_t Offset, uint32_t LoadCommandIndex, + bool CheckLoadCmdEnd = false) { + bool Overflowed = false; + const char *Ptr = + reinterpret_cast(llvm::SaturatingAdd( + reinterpret_cast(BasePtr), Offset, &Overflowed)); + if (Overflowed) + return malformedError("load command " + Twine(LoadCommandIndex) + + " starts at an overflowed address"); if (auto CmdOrErr = getStructOrErr(Obj, Ptr)) { - if (CmdOrErr->cmdsize + Ptr > Obj.getData().end()) + const unsigned HeaderSize = Obj.is64Bit() ? sizeof(MachO::mach_header_64) + : sizeof(MachO::mach_header); + uintptr_t CmdStart = llvm::SaturatingAdd( + CmdOrErr->cmdsize, reinterpret_cast(Ptr), &Overflowed); + + if (Overflowed) + return malformedError("load command " + Twine(LoadCommandIndex) + + " results in overflowed address"); + + if (CmdStart >= reinterpret_cast(Obj.getData().end())) return malformedError("load command " + Twine(LoadCommandIndex) + " extends past end of file"); + + if (CheckLoadCmdEnd) { + uintptr_t CmdEnd = llvm::SaturatingAdd( + CmdStart, sizeof(MachO::load_command), &Overflowed); + if (Overflowed) + return malformedError("load command " + Twine(LoadCommandIndex) + + "extends past type limit"); + auto LoadCmdsEnd = + Obj.getData().data() + HeaderSize + Obj.getHeader().sizeofcmds; + if (CmdEnd > reinterpret_cast(LoadCmdsEnd)) + return malformedError( + "load command " + Twine(LoadCommandIndex + 1) + + " extends past the end all load commands in the file"); + } + if (CmdOrErr->cmdsize < 8) return malformedError("load command " + Twine(LoadCommandIndex) + " with size less than 8 bytes"); @@ -206,22 +243,14 @@ getFirstLoadCommandInfo(const MachOObjectFile &Obj) { unsigned HeaderSize = Obj.is64Bit() ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); - if (sizeof(MachO::load_command) > Obj.getHeader().sizeofcmds) - return malformedError("load command 0 extends past the end all load " - "commands in the file"); - return getLoadCommandInfo(Obj, getPtr(Obj, HeaderSize), 0); + return getLoadCommandInfo(Obj, getPtr(Obj, HeaderSize), 0, 0); } static Expected getNextLoadCommandInfo(const MachOObjectFile &Obj, uint32_t LoadCommandIndex, const MachOObjectFile::LoadCommandInfo &L) { - unsigned HeaderSize = Obj.is64Bit() ? sizeof(MachO::mach_header_64) - : sizeof(MachO::mach_header); - if (L.Ptr + L.C.cmdsize + sizeof(MachO::load_command) > - Obj.getData().data() + HeaderSize + Obj.getHeader().sizeofcmds) - return malformedError("load command " + Twine(LoadCommandIndex + 1) + - " extends past the end all load commands in the file"); - return getLoadCommandInfo(Obj, L.Ptr + L.C.cmdsize, LoadCommandIndex + 1); + return getLoadCommandInfo(Obj, L.Ptr, L.C.cmdsize, LoadCommandIndex + 1, + true); } template @@ -1279,7 +1308,12 @@ if (Err) return; SizeOfHeaders += getHeader().sizeofcmds; - if (getData().data() + SizeOfHeaders > getData().end()) { + bool Overflowed = false; + uintptr_t LoadCmdsEnd = llvm::SaturatingAdd( + SizeOfHeaders, reinterpret_cast(getData().data()), + &Overflowed); + assert(!Overflowed && "Load commands range extends past uint32_t type limit"); + if (LoadCmdsEnd > reinterpret_cast(getData().end())) { Err = malformedError("load commands extend past the end of the file"); return; }