Skip to content

Commit 296471b

Browse files
committed
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. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@278557 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent f5a4670 commit 296471b

13 files changed

+135
-26
lines changed

lib/Object/MachOObjectFile.cpp

+96-17
Original file line numberDiff line numberDiff line change
@@ -219,19 +219,19 @@ static void parseHeader(const MachOObjectFile *Obj, T &Header,
219219
// Parses LC_SEGMENT or LC_SEGMENT_64 load command, adds addresses of all
220220
// sections to \param Sections, and optionally sets
221221
// \param IsPageZeroSegment to true.
222-
template <typename SegmentCmd>
222+
template <typename Segment, typename Section>
223223
static Error parseSegmentLoadCommand(
224224
const MachOObjectFile *Obj, const MachOObjectFile::LoadCommandInfo &Load,
225225
SmallVectorImpl<const char *> &Sections, bool &IsPageZeroSegment,
226-
uint32_t LoadCommandIndex, const char *CmdName) {
227-
const unsigned SegmentLoadSize = sizeof(SegmentCmd);
226+
uint32_t LoadCommandIndex, const char *CmdName, uint64_t SizeOfHeaders) {
227+
const unsigned SegmentLoadSize = sizeof(Segment);
228228
if (Load.C.cmdsize < SegmentLoadSize)
229229
return malformedError("load command " + Twine(LoadCommandIndex) +
230230
" " + CmdName + " cmdsize too small");
231-
if (auto SegOrErr = getStructOrErr<SegmentCmd>(Obj, Load.Ptr)) {
232-
SegmentCmd S = SegOrErr.get();
233-
const unsigned SectionSize =
234-
Obj->is64Bit() ? sizeof(MachO::section_64) : sizeof(MachO::section);
231+
if (auto SegOrErr = getStructOrErr<Segment>(Obj, Load.Ptr)) {
232+
Segment S = SegOrErr.get();
233+
const unsigned SectionSize = sizeof(Section);
234+
uint64_t FileSize = Obj->getData().size();
235235
if (S.nsects > std::numeric_limits<uint32_t>::max() / SectionSize ||
236236
S.nsects * SectionSize > Load.C.cmdsize - SegmentLoadSize)
237237
return malformedError("load command " + Twine(LoadCommandIndex) +
@@ -240,12 +240,88 @@ static Error parseSegmentLoadCommand(
240240
for (unsigned J = 0; J < S.nsects; ++J) {
241241
const char *Sec = getSectionPtr(Obj, Load, J);
242242
Sections.push_back(Sec);
243+
Section s = getStruct<Section>(Obj, Sec);
244+
if (Obj->getHeader().filetype != MachO::MH_DYLIB_STUB &&
245+
Obj->getHeader().filetype != MachO::MH_DSYM &&
246+
s.flags != MachO::S_ZEROFILL &&
247+
s.flags != MachO::S_THREAD_LOCAL_ZEROFILL &&
248+
s.offset > FileSize)
249+
return malformedError("offset field of section " + Twine(J) + " in " +
250+
CmdName + " command " + Twine(LoadCommandIndex) +
251+
" extends past the end of the file");
252+
if (Obj->getHeader().filetype != MachO::MH_DYLIB_STUB &&
253+
Obj->getHeader().filetype != MachO::MH_DSYM &&
254+
s.flags != MachO::S_ZEROFILL &&
255+
s.flags != MachO::S_THREAD_LOCAL_ZEROFILL &&
256+
S.fileoff == 0 && s.offset < SizeOfHeaders && s.size != 0)
257+
return malformedError("offset field of section " + Twine(J) + " in " +
258+
CmdName + " command " + Twine(LoadCommandIndex) +
259+
" not past the headers of the file");
260+
uint64_t BigSize = s.offset;
261+
BigSize += s.size;
262+
if (Obj->getHeader().filetype != MachO::MH_DYLIB_STUB &&
263+
Obj->getHeader().filetype != MachO::MH_DSYM &&
264+
s.flags != MachO::S_ZEROFILL &&
265+
s.flags != MachO::S_THREAD_LOCAL_ZEROFILL &&
266+
BigSize > FileSize)
267+
return malformedError("offset field plus size field of section " +
268+
Twine(J) + " in " + CmdName + " command " +
269+
Twine(LoadCommandIndex) +
270+
" extends past the end of the file");
271+
if (Obj->getHeader().filetype != MachO::MH_DYLIB_STUB &&
272+
Obj->getHeader().filetype != MachO::MH_DSYM &&
273+
s.flags != MachO::S_ZEROFILL &&
274+
s.flags != MachO::S_THREAD_LOCAL_ZEROFILL &&
275+
s.size > S.filesize)
276+
return malformedError("size field of section " +
277+
Twine(J) + " in " + CmdName + " command " +
278+
Twine(LoadCommandIndex) +
279+
" greater than the segment");
280+
if (Obj->getHeader().filetype != MachO::MH_DYLIB_STUB &&
281+
Obj->getHeader().filetype != MachO::MH_DSYM &&
282+
s.size != 0 && s.addr < S.vmaddr)
283+
return malformedError("addr field of section " +
284+
Twine(J) + " in " + CmdName + " command " +
285+
Twine(LoadCommandIndex) +
286+
" less than the segment's vmaddr");
287+
BigSize = s.addr;
288+
BigSize += s.size;
289+
uint64_t BigEnd = S.vmaddr;
290+
BigEnd += S.vmsize;
291+
if (S.vmsize != 0 && s.size != 0 && BigSize > BigEnd)
292+
return malformedError("addr field plus size of section " +
293+
Twine(J) + " in " + CmdName + " command " +
294+
Twine(LoadCommandIndex) + " greater than than "
295+
"the segment's vmaddr plus vmsize");
296+
if (s.reloff > FileSize)
297+
return malformedError("reloff field of section " +
298+
Twine(J) + " in " + CmdName + " command " +
299+
Twine(LoadCommandIndex) +
300+
" extends past the end of the file");
301+
BigSize = s.nreloc;
302+
BigSize *= sizeof(struct MachO::relocation_info);
303+
BigSize += s.reloff;
304+
if (BigSize > FileSize)
305+
return malformedError("reloff field plus nreloc field times sizeof("
306+
"struct relocation_info) of section " +
307+
Twine(J) + " in " + CmdName + " command " +
308+
Twine(LoadCommandIndex) +
309+
" extends past the end of the file");
243310
}
244-
uint64_t FileSize = Obj->getData().size();
245311
if (S.fileoff > FileSize)
246312
return malformedError("load command " + Twine(LoadCommandIndex) +
247313
" fileoff field in " + CmdName +
248314
" extends past the end of the file");
315+
uint64_t BigSize = S.fileoff;
316+
BigSize += S.filesize;
317+
if (BigSize > FileSize)
318+
return malformedError("load command " + Twine(LoadCommandIndex) +
319+
" fileoff field plus filesize field in " +
320+
CmdName + " extends past the end of the file");
321+
if (S.vmsize != 0 && S.filesize > S.vmsize)
322+
return malformedError("load command " + Twine(LoadCommandIndex) +
323+
" fileoff field in " + CmdName +
324+
" greater than vmsize field");
249325
IsPageZeroSegment |= StringRef("__PAGEZERO").equals(S.segname);
250326
} else
251327
return SegOrErr.takeError();
@@ -273,18 +349,18 @@ MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian,
273349
DyldInfoLoadCmd(nullptr), UuidLoadCmd(nullptr),
274350
HasPageZeroSegment(false) {
275351
ErrorAsOutParameter ErrAsOutParam(&Err);
276-
uint64_t BigSize;
352+
uint64_t SizeOfHeaders;
277353
if (is64Bit()) {
278354
parseHeader(this, Header64, Err);
279-
BigSize = sizeof(MachO::mach_header_64);
355+
SizeOfHeaders = sizeof(MachO::mach_header_64);
280356
} else {
281357
parseHeader(this, Header, Err);
282-
BigSize = sizeof(MachO::mach_header);
358+
SizeOfHeaders = sizeof(MachO::mach_header);
283359
}
284360
if (Err)
285361
return;
286-
BigSize += getHeader().sizeofcmds;
287-
if (getData().data() + BigSize > getData().end()) {
362+
SizeOfHeaders += getHeader().sizeofcmds;
363+
if (getData().data() + SizeOfHeaders > getData().end()) {
288364
Err = malformedError("load commands extend past the end of the file");
289365
return;
290366
}
@@ -366,13 +442,16 @@ MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian,
366442
}
367443
UuidLoadCmd = Load.Ptr;
368444
} else if (Load.C.cmd == MachO::LC_SEGMENT_64) {
369-
if ((Err = parseSegmentLoadCommand<MachO::segment_command_64>(
445+
if ((Err = parseSegmentLoadCommand<MachO::segment_command_64,
446+
MachO::section_64>(
370447
this, Load, Sections, HasPageZeroSegment, I,
371-
"LC_SEGMENT_64")))
448+
"LC_SEGMENT_64", SizeOfHeaders)))
372449
return;
373450
} else if (Load.C.cmd == MachO::LC_SEGMENT) {
374-
if ((Err = parseSegmentLoadCommand<MachO::segment_command>(
375-
this, Load, Sections, HasPageZeroSegment, I, "LC_SEGMENT")))
451+
if ((Err = parseSegmentLoadCommand<MachO::segment_command,
452+
MachO::section>(
453+
this, Load, Sections, HasPageZeroSegment, I,
454+
"LC_SEGMENT", SizeOfHeaders)))
376455
return;
377456
} else if (Load.C.cmd == MachO::LC_LOAD_DYLIB ||
378457
Load.C.cmd == MachO::LC_LOAD_WEAK_DYLIB ||
162 Bytes
Binary file not shown.
163 Bytes
Binary file not shown.
152 Bytes
Binary file not shown.
Binary file not shown.
Binary file not shown.
152 Bytes
Binary file not shown.
Binary file not shown.
Binary file not shown.
84 Bytes
Binary file not shown.
84 Bytes
Binary file not shown.

test/Object/macho-invalid.test

+30
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,33 @@ INVALID-SEGMENT-FILEOFF-NM: macho-invalid-segment-fileoff truncated or malformed
112112

113113
RUN: not llvm-size %p/Inputs/macho-invalid-segment-fileoff 2>&1 | FileCheck -check-prefix INVALID-SEGMENT-FILEOFF-SIZE %s
114114
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)
115+
116+
RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-segment-filesize 2>&1 | FileCheck -check-prefix INVALID-SEGMENT-FILESIZE %s
117+
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)
118+
119+
RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-segment-vmsize 2>&1 | FileCheck -check-prefix INVALID-SEGMENT-VMSIZE %s
120+
INVALID-SEGMENT-VMSIZE: macho-invalid-segment-vmsize': truncated or malformed object (load command 0 fileoff field in LC_SEGMENT greater than vmsize field)
121+
122+
RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-section-offset 2>&1 | FileCheck -check-prefix INVALID-SECTION-FILEOFF %s
123+
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)
124+
125+
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
126+
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)
127+
128+
RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-section-offset-size 2>&1 | FileCheck -check-prefix INVALID-SECTION-FILEOFF-SIZE %s
129+
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)
130+
131+
RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-section-size-filesize 2>&1 | FileCheck -check-prefix INVALID-SECTION-SIZE-FILESIZE %s
132+
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)
133+
134+
RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-section-addr 2>&1 | FileCheck -check-prefix INVALID-SECTION-ADDR %s
135+
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)
136+
137+
RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-section-addr-size 2>&1 | FileCheck -check-prefix INVALID-SECTION-ADDR-SIZE %s
138+
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)
139+
140+
RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-section-reloff 2>&1 | FileCheck -check-prefix INVALID-SECTION-RELOFF %s
141+
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)
142+
143+
RUN: not llvm-objdump -macho -private-headers %p/Inputs/macho-invalid-section-reloff-nrelocs 2>&1 | FileCheck -check-prefix INVALID-SECTION-RELOFF-NRELOCS %s
144+
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)

test/tools/llvm-objdump/X86/malformed-machos.test

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
// These test checks that llvm-objdump will not crash with malformed Mach-O
22
// files. So the check line is not all that important but the bug fixes to
33
// make sure llvm-objdump is robust is what matters.
4-
# RUN: llvm-objdump -macho -objc-meta-data \
5-
# RUN: %p/Inputs/malformed-machos/mem-crup-0001.macho \
4+
# RUN: not llvm-objdump -macho -objc-meta-data \
5+
# RUN: %p/Inputs/malformed-machos/mem-crup-0001.macho 2>&1 \
66
# RUN: | FileCheck -check-prefix=m0001 %s
77

8-
# m0001: (method_t extends past the end of the section)
8+
# 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)
99

1010
# RUN: llvm-objdump -macho -objc-meta-data \
1111
# RUN: %p/Inputs/malformed-machos/mem-crup-0006.macho \
@@ -19,17 +19,17 @@
1919

2020
# m0010: 00000000000010e0 0x10e8 _OBJC_CLASS_
2121

22-
# RUN: llvm-objdump -macho -objc-meta-data \
23-
# RUN: %p/Inputs/malformed-machos/mem-crup-0040.macho \
22+
# RUN: not llvm-objdump -macho -objc-meta-data \
23+
# RUN: %p/Inputs/malformed-machos/mem-crup-0040.macho 2>&1 \
2424
# RUN: | FileCheck -check-prefix=m0040 %s
2525

26-
# m0040: 00000000000010a0 0xf39 -[tiny_dylib init]
26+
# 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)
2727

28-
# RUN: llvm-objdump -macho -objc-meta-data \
29-
# RUN: %p/Inputs/malformed-machos/mem-crup-0080.macho \
28+
# RUN: not llvm-objdump -macho -objc-meta-data \
29+
# RUN: %p/Inputs/malformed-machos/mem-crup-0080.macho 2>&1 \
3030
# RUN: | FileCheck -check-prefix=m0080 %s
3131

32-
# m0080: data 0xf960000 (struct class_ro_t *)
32+
# 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)
3333

3434
# RUN: llvm-objdump -macho -objc-meta-data \
3535
# RUN: %p/Inputs/malformed-machos/mem-crup-0261.macho

0 commit comments

Comments
 (0)