GP-6875: Check Mach-O headear for invalid number of load commands

This commit is contained in:
Ryan Kurtz
2026-05-27 08:13:20 -04:00
parent 38d1beaa59
commit 34afe864bc
2 changed files with 49 additions and 20 deletions

View File

@@ -51,6 +51,8 @@ public class MachHeader implements StructConverter {
private long _machHeaderStartIndex = 0; private long _machHeaderStartIndex = 0;
private boolean _parsed = false; private boolean _parsed = false;
private static final int MAX_LOAD_COMMANDS = 32_768;
/** /**
* Returns true if the specified ByteProvider starts with a Mach header magic signature. * Returns true if the specified ByteProvider starts with a Mach header magic signature.
* *
@@ -74,7 +76,7 @@ public class MachHeader implements StructConverter {
* *
* @param provider the ByteProvider * @param provider the ByteProvider
* @throws IOException if an I/O error occurs while reading from the ByteProvider * @throws IOException if an I/O error occurs while reading from the ByteProvider
* @throws MachException if an invalid MachHeader is detected * @throws MachException if an invalid header is detected
*/ */
public MachHeader(ByteProvider provider) throws IOException, MachException { public MachHeader(ByteProvider provider) throws IOException, MachException {
this(provider, 0); this(provider, 0);
@@ -88,7 +90,7 @@ public class MachHeader implements StructConverter {
* @param machHeaderStartIndexInProvider the index into the ByteProvider where the MachHeader * @param machHeaderStartIndexInProvider the index into the ByteProvider where the MachHeader
* begins * begins
* @throws IOException if an I/O error occurs while reading from the ByteProvider * @throws IOException if an I/O error occurs while reading from the ByteProvider
* @throws MachException if an invalid MachHeader is detected * @throws MachException if an invalid header is detected
*/ */
public MachHeader(ByteProvider provider, long machHeaderStartIndexInProvider) public MachHeader(ByteProvider provider, long machHeaderStartIndexInProvider)
throws IOException, MachException { throws IOException, MachException {
@@ -96,17 +98,17 @@ public class MachHeader implements StructConverter {
} }
/** /**
* Creatse a new {@link MachHeader}. Assumes the MachHeader starts at index * Creates a new {@link MachHeader}. Assumes the MachHeader starts at index
* <i>machHeaderStartIndexInProvider</i> in the ByteProvider. * <i>machHeaderStartIndexInProvider</i> in the ByteProvider.
* *
* @param provider the ByteProvider * @param provider the ByteProvider
* @param machHeaderStartIndexInProvider the index into the ByteProvider where the MachHeader * @param machHeaderStartIndexInProvider the index into the ByteProvider where the MachHeader
* begins. * begins.
* @param isRemainingMachoRelativeToStartIndex true if the rest of the macho uses relative * @param isRemainingMachoRelativeToStartIndex true if the rest of the Mach-O uses relative
* indexin (this is common in UBI and kernel cache files); otherwise, false if the rest of the * indexes (this is common in UBI and kernel cache files); otherwise, false if the rest of the
* file uses absolute indexing from 0 (this is common in DYLD cache files) * file uses absolute indexing from 0 (this is common in DYLD cache files)
* @throws IOException if an I/O error occurs while reading from the ByteProvider * @throws IOException if an I/O error occurs while reading from the ByteProvider
* @throws MachException if an invalid MachHeader is detected * @throws MachException if an invalid header is detected
*/ */
public MachHeader(ByteProvider provider, long machHeaderStartIndexInProvider, public MachHeader(ByteProvider provider, long machHeaderStartIndexInProvider,
boolean isRemainingMachoRelativeToStartIndex) throws IOException, MachException { boolean isRemainingMachoRelativeToStartIndex) throws IOException, MachException {
@@ -144,7 +146,7 @@ public class MachHeader implements StructConverter {
* *
* @return This {@link MachHeader}, for convenience * @return This {@link MachHeader}, for convenience
* @throws IOException If there was an IO-related error * @throws IOException If there was an IO-related error
* @throws MachException if the load command is invalid * @throws MachException if a problem was detected with the load commands
*/ */
public MachHeader parse() throws IOException, MachException { public MachHeader parse() throws IOException, MachException {
return parse(null); return parse(null);
@@ -157,13 +159,15 @@ public class MachHeader implements StructConverter {
* if a split DYLD cache is not being used. * if a split DYLD cache is not being used.
* @return This {@link MachHeader}, for convenience * @return This {@link MachHeader}, for convenience
* @throws IOException If there was an IO-related error * @throws IOException If there was an IO-related error
* @throws MachException if the load command is invalid * @throws MachException if a problem was detected with the load commands
*/ */
public MachHeader parse(SplitDyldCache splitDyldCache) throws IOException, MachException { public MachHeader parse(SplitDyldCache splitDyldCache) throws IOException, MachException {
if (_parsed) { if (_parsed) {
return this; return this;
} }
validateNumLoadCommands();
// We must parse segment load commands first, so find and store their indexes separately // We must parse segment load commands first, so find and store their indexes separately
long currentIndex = _commandIndex; long currentIndex = _commandIndex;
List<Long> segmentIndexes = new ArrayList<>(); List<Long> segmentIndexes = new ArrayList<>();
@@ -198,8 +202,11 @@ public class MachHeader implements StructConverter {
* *
* @return A {@link List} of this {@link MachHeader}'s {@link SegmentCommand segments} * @return A {@link List} of this {@link MachHeader}'s {@link SegmentCommand segments}
* @throws IOException If there was an IO-related error * @throws IOException If there was an IO-related error
* @throws MachException if a problem was detected with the load commands
*/ */
public List<SegmentCommand> parseSegments() throws IOException { public List<SegmentCommand> parseSegments() throws IOException, MachException {
validateNumLoadCommands();
List<SegmentCommand> segments = new ArrayList<>(); List<SegmentCommand> segments = new ArrayList<>();
_reader.setPointerIndex(_commandIndex); _reader.setPointerIndex(_commandIndex);
for (int i = 0; i < nCmds; ++i) { for (int i = 0; i < nCmds; ++i) {
@@ -223,8 +230,11 @@ public class MachHeader implements StructConverter {
* @return A {@link List} of this {@link MachHeader}'s * @return A {@link List} of this {@link MachHeader}'s
* {@link DynamicLibraryCommand reexport load commands} * {@link DynamicLibraryCommand reexport load commands}
* @throws IOException If there was an IO-related error * @throws IOException If there was an IO-related error
* @throws MachException if a problem was detected with the load commands
*/ */
public List<DynamicLibraryCommand> parseReexports() throws IOException { public List<DynamicLibraryCommand> parseReexports() throws IOException, MachException {
validateNumLoadCommands();
List<DynamicLibraryCommand> cmds = new ArrayList<>(); List<DynamicLibraryCommand> cmds = new ArrayList<>();
_reader.setPointerIndex(_commandIndex); _reader.setPointerIndex(_commandIndex);
for (int i = 0; i < nCmds; ++i) { for (int i = 0; i < nCmds; ++i) {
@@ -248,9 +258,12 @@ public class MachHeader implements StructConverter {
* @param loadCommandType The type of {@link LoadCommand} to check for * @param loadCommandType The type of {@link LoadCommand} to check for
* @return True if this {@link MachHeader} contains the given {@link LoadCommand} type * @return True if this {@link MachHeader} contains the given {@link LoadCommand} type
* @throws IOException If there was an IO-related error * @throws IOException If there was an IO-related error
* @throws MachException if a problem was detected with the load commands
* @see LoadCommandTypes * @see LoadCommandTypes
*/ */
public boolean parseAndCheck(int loadCommandType) throws IOException { public boolean parseAndCheck(int loadCommandType) throws IOException, MachException {
validateNumLoadCommands();
_reader.setPointerIndex(_commandIndex); _reader.setPointerIndex(_commandIndex);
for (int i = 0; i < nCmds; ++i) { for (int i = 0; i < nCmds; ++i) {
int type = _reader.peekNextInt(); int type = _reader.peekNextInt();
@@ -434,6 +447,17 @@ public class MachHeader implements StructConverter {
return getDescription(); return getDescription();
} }
/**
* Validates the specified number of load commands
*
* @throws MachException if this {@link MachHeader} has an invalid number of load commands
*/
private void validateNumLoadCommands() throws MachException {
if (nCmds > MAX_LOAD_COMMANDS || nCmds < 0) {
throw new MachException("Invalid number of load commands (%d)".formatted(nCmds));
}
}
/** /**
* Sanitizes invalid segment/section names so they can be used as memory blocks and program tree * Sanitizes invalid segment/section names so they can be used as memory blocks and program tree
* modules. * modules.

View File

@@ -535,16 +535,21 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
* @throws IOException if an IO-related error occurred * @throws IOException if an IO-related error occurred
*/ */
private String detectCompilerName(MachHeader machHeader) throws IOException { private String detectCompilerName(MachHeader machHeader) throws IOException {
List<String> sectionNames = machHeader.parseSegments() try {
.stream() List<String> sectionNames = machHeader.parseSegments()
.flatMap(seg -> seg.getSections().stream()) .stream()
.map(section -> section.getSectionName()) .flatMap(seg -> seg.getSections().stream())
.toList(); .map(section -> section.getSectionName())
if (SwiftUtils.isSwift(sectionNames)) { .toList();
return SwiftUtils.SWIFT_COMPILER; if (SwiftUtils.isSwift(sectionNames)) {
return SwiftUtils.SWIFT_COMPILER;
}
if (GoRttiMapper.hasGolangSections(sectionNames)) {
return GoConstants.GOLANG_CSPEC_NAME;
}
} }
if (GoRttiMapper.hasGolangSections(sectionNames)) { catch (MachException e) {
return GoConstants.GOLANG_CSPEC_NAME; // fall thru
} }
return null; return null;
} }