diff --git a/llvm/lib/ObjectYAML/MachOEmitter.cpp b/llvm/lib/ObjectYAML/MachOEmitter.cpp index 35d442e8e3437..46c91811d0a67 100644 --- a/llvm/lib/ObjectYAML/MachOEmitter.cpp +++ b/llvm/lib/ObjectYAML/MachOEmitter.cpp @@ -19,14 +19,26 @@ #include "llvm/Support/Error.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/LEB128.h" -#include "llvm/Support/YAMLTraits.h" #include "llvm/Support/SystemZ/zOSSupport.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; namespace { +static const char *getLoadCommandName(uint32_t cmd) { + switch (cmd) { +#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \ + case MachO::LCName: \ + return #LCName; +#include "llvm/BinaryFormat/MachO.def" + default: + return nullptr; + } +} + class MachOWriter { public: MachOWriter(MachOYAML::Object &Obj) : Obj(Obj), fileStart(0) { @@ -244,7 +256,8 @@ void MachOWriter::ZeroToOffset(raw_ostream &OS, size_t Offset) { } void MachOWriter::writeLoadCommands(raw_ostream &OS) { - for (auto &LC : Obj.LoadCommands) { + for (size_t i = 0; i < Obj.LoadCommands.size(); ++i) { + auto &LC = Obj.LoadCommands[i]; size_t BytesWritten = 0; llvm::MachO::macho_load_command Data = LC.Data; @@ -285,7 +298,25 @@ void MachOWriter::writeLoadCommands(raw_ostream &OS) { // Fill remaining bytes with 0. This will only get hit in partially // specified test cases. - auto BytesRemaining = LC.Data.load_command_data.cmdsize - BytesWritten; + // Prevent integer underflow if BytesWritten exceeds cmdsize. + if (BytesWritten > LC.Data.load_command_data.cmdsize) { + std::string Name; + const char *NameCStr = getLoadCommandName(LC.Data.load_command_data.cmd); + if (NameCStr) + Name = NameCStr; + else + Name = ("(0x" + Twine::utohexstr(LC.Data.load_command_data.cmd) + ")") + .str(); + + WithColor::warning() << "load command " << i << " " << Name + << " cmdsize too small (" + << LC.Data.load_command_data.cmdsize + << " bytes) for actual size (" << BytesWritten + << " bytes)\n"; + } + auto BytesRemaining = (BytesWritten < LC.Data.load_command_data.cmdsize) + ? LC.Data.load_command_data.cmdsize - BytesWritten + : 0; if (BytesRemaining > 0) { ZeroFillBytes(OS, BytesRemaining); } diff --git a/llvm/test/ObjectYAML/MachO/load-cmdsize-too-small.yaml b/llvm/test/ObjectYAML/MachO/load-cmdsize-too-small.yaml new file mode 100644 index 0000000000000..ef11711e0487e --- /dev/null +++ b/llvm/test/ObjectYAML/MachO/load-cmdsize-too-small.yaml @@ -0,0 +1,55 @@ +## Test that yaml2obj handles load commands with cmdsize smaller than the +## actual structure size without crashing (due to integer underflow). + +## Test with a known load command (LC_SEGMENT_64). +# RUN: yaml2obj %s --docnum=1 -o %t1 2>&1 | FileCheck %s --check-prefix=WARNING-KNOWN +# RUN: not llvm-readobj --file-headers %t1 2>&1 | FileCheck %s --check-prefix=MALFORMED + +# WARNING-KNOWN: warning: load command 0 LC_SEGMENT_64 cmdsize too small (56 bytes) for actual size (72 bytes) + +# MALFORMED: error: {{.*}}: truncated or malformed object (load command 0 LC_SEGMENT_64 cmdsize too small) + +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x01000007 + cpusubtype: 0x00000003 + filetype: 0x00000001 + ncmds: 1 + sizeofcmds: 56 + flags: 0x00002000 + reserved: 0x00000000 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 56 ## Should be 72 for LC_SEGMENT_64 + segname: '__TEXT' + vmaddr: 0x1000 + vmsize: 0x10 + fileoff: 0 + filesize: 0 + maxprot: 7 + initprot: 5 + nsects: 0 + flags: 0 +... + +## Test with an unknown load command value. +# RUN: yaml2obj %s --docnum=2 -o %t2 2>&1 | FileCheck %s --check-prefix=WARNING-UNKNOWN + +# WARNING-UNKNOWN: warning: load command 0 (0xdeadbeef) cmdsize too small (8 bytes) for actual size (20 bytes) + +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x01000007 + cpusubtype: 0x00000003 + filetype: 0x00000001 + ncmds: 1 + sizeofcmds: 20 + flags: 0x00002000 + reserved: 0x00000000 +LoadCommands: + - cmd: 0xDEADBEEF + cmdsize: 8 + PayloadBytes: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C] +...