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,8 +85,13 @@ 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; memcpy(&Cmd, P, sizeof(T)); @@ -128,6 +134,14 @@ return StringRef(P, 16); } +static uintptr_t SaturatingAddHelper(uintptr_t Arg1, uintptr_t Arg2, + uintptr_t Arg3, bool *Overflowed) { + uintptr_t Ret = llvm::SaturatingAdd(Arg1, Arg2, Overflowed); + if (*Overflowed) + return Ret; + return llvm::SaturatingAdd(Ret, Arg3, Overflowed); +} + static unsigned getCPUType(const MachOObjectFile &O) { return O.getHeader().cputype; } @@ -188,16 +202,44 @@ } static Expected -getLoadCommandInfo(const MachOObjectFile &Obj, const char *Ptr, - uint32_t LoadCommandIndex) { - if (auto CmdOrErr = getStructOrErr(Obj, Ptr)) { - if (CmdOrErr->cmdsize + Ptr > Obj.getData().end()) +getLoadCommandInfo(const MachOObjectFile &Obj, const char *BasePtr, + uintptr_t Offset, uint32_t LoadCommandIndex, + bool CheckLoadCmdEnd = false) { + bool Overflowed = false; + uintptr_t Ptr = 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, reinterpret_cast(Ptr))) { + const auto HeaderSize = Obj.is64Bit() ? sizeof(MachO::mach_header_64) + : sizeof(MachO::mach_header); + + if (Ptr >= reinterpret_cast(Obj.getData().end())) return malformedError("load command " + Twine(LoadCommandIndex) + " extends past end of file"); + + if (CheckLoadCmdEnd) { + uintptr_t CmdEnd = llvm::SaturatingAdd( + Ptr, sizeof(MachO::load_command), &Overflowed); + if (Overflowed) + return malformedError("load command " + Twine(LoadCommandIndex) + + "extends past uintptr_t type limit"); + uintptr_t LoadCmdsEnd = SaturatingAddHelper( + reinterpret_cast(Obj.getData().begin()), HeaderSize, + Obj.getHeader().sizeofcmds, &Overflowed); + if (CmdEnd > LoadCmdsEnd) + return malformedError( + "load command " + Twine(LoadCommandIndex + 1) + + " extends past the end of all load commands in the file"); + } + if (CmdOrErr->cmdsize < 8) return malformedError("load command " + Twine(LoadCommandIndex) + " with size less than 8 bytes"); - return MachOObjectFile::LoadCommandInfo({Ptr, *CmdOrErr}); + return MachOObjectFile::LoadCommandInfo( + {reinterpret_cast(Ptr), *CmdOrErr}); } else return CmdOrErr.takeError(); } @@ -206,22 +248,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 +1313,15 @@ if (Err) return; SizeOfHeaders += getHeader().sizeofcmds; - if (getData().data() + SizeOfHeaders > getData().end()) { + bool Overflowed = false; + uintptr_t LoadCmdsStart = llvm::SaturatingAdd( + SizeOfHeaders, reinterpret_cast(getData().data()), + &Overflowed); + if (Overflowed) { + Err = malformedError("load commands extends past uintptr_t type limit"); + return; + } + if (LoadCmdsStart > reinterpret_cast(getData().end())) { Err = malformedError("load commands extend past the end of the file"); return; }