From 2fce7f119d0949bb948fed584027f77cf5a7efa2 Mon Sep 17 00:00:00 2001 From: Ryan Kurtz Date: Mon, 31 Aug 2026 06:19:29 -0400 Subject: [PATCH] GP-7175: Checking count values in Mach-O BindingTable.java and RebaseTable.java --- .../format/macho/commands/DyldInfoCommand.java | 6 +++--- .../format/macho/commands/dyld/BindingTable.java | 11 +++++++++-- .../format/macho/commands/dyld/RebaseTable.java | 15 ++++++++++++++- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldInfoCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldInfoCommand.java index 3150114041..ce94886249 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldInfoCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldInfoCommand.java @@ -18,8 +18,7 @@ package ghidra.app.util.bin.format.macho.commands; import java.io.IOException; import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.macho.MachConstants; -import ghidra.app.util.bin.format.macho.MachHeader; +import ghidra.app.util.bin.format.macho.*; import ghidra.app.util.bin.format.macho.commands.dyld.*; import ghidra.app.util.importer.MessageLog; import ghidra.program.flatapi.FlatProgramAPI; @@ -61,9 +60,10 @@ public class DyldInfoCommand extends LoadCommand { * references. Note that this might be in a different underlying provider. * @param header The {@link MachHeader header} associated with this load command * @throws IOException if an IO-related error occurs while parsing + * @throws MachException if the {@link DyldInfoCommand} is invalid */ DyldInfoCommand(BinaryReader loadCommandReader, BinaryReader dataReader, MachHeader header) - throws IOException { + throws IOException, MachException { super(loadCommandReader); rebaseOff = loadCommandReader.readNextUnsignedInt(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/BindingTable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/BindingTable.java index 7a9f5e7ba8..f8ad444bd3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/BindingTable.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/BindingTable.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.List; import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.macho.MachException; import ghidra.app.util.bin.format.macho.MachHeader; import ghidra.app.util.bin.format.macho.commands.DyldInfoCommandConstants; import ghidra.program.model.data.LEB128; @@ -31,10 +32,11 @@ import ghidra.program.model.data.LEB128; */ public class BindingTable extends OpcodeTable { + private static final int COUNT_LIMIT = 100_000; + private List bindings = new ArrayList<>(); private List threadedBindings; - /** * Creates an empty {@link BindingTable} */ @@ -50,9 +52,10 @@ public class BindingTable extends OpcodeTable { * @param tableSize The size of the table, in bytes * @param lazy True if this is a lazy binding table; otherwise, false * @throws IOException if an IO-related error occurs while parsing + * @throws MachException if the {@link BindingTable} is invalid */ public BindingTable(BinaryReader reader, MachHeader header, long tableSize, boolean lazy) - throws IOException { + throws IOException, MachException { this(); int pointerSize = header.getAddressSize(); @@ -143,6 +146,10 @@ public class BindingTable extends OpcodeTable { case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: { // 0xC0 ulebOffsets.add(reader.getPointerIndex() - origIndex); long count = reader.readNext(LEB128::unsigned); + if (count < 0 || count > COUNT_LIMIT) { + throw new MachException("BIND_OPCODE count exceeds limit (%s): %s" + .formatted(COUNT_LIMIT, count)); + } ulebOffsets.add(reader.getPointerIndex() - origIndex); long skip = reader.readNext(LEB128::unsigned); for ( int i = 0 ; i < count ; ++i ) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/RebaseTable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/RebaseTable.java index 980bbb4e7e..a4c1aa32c8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/RebaseTable.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/RebaseTable.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.List; import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.macho.MachException; import ghidra.app.util.bin.format.macho.MachHeader; import ghidra.program.model.data.LEB128; @@ -30,6 +31,8 @@ import ghidra.program.model.data.LEB128; */ public class RebaseTable extends OpcodeTable { + private static final int COUNT_LIMIT = 100_000; + private List rebases = new ArrayList<>(); /** @@ -46,8 +49,10 @@ public class RebaseTable extends OpcodeTable { * @param header The header * @param tableSize The size of the table, in bytes * @throws IOException if an IO-related error occurs while parsing + * @throws MachException if the {@link RebaseTable} is invalid */ - public RebaseTable(BinaryReader reader, MachHeader header, long tableSize) throws IOException { + public RebaseTable(BinaryReader reader, MachHeader header, long tableSize) + throws IOException, MachException { this(); int pointerSize = header.getAddressSize(); @@ -94,6 +99,10 @@ public class RebaseTable extends OpcodeTable { case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: { // 0x60 ulebOffsets.add(reader.getPointerIndex() - origIndex); int count = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + if (count < 0 || count > COUNT_LIMIT) { + throw new MachException("REBASE_OPCODE count exceeds limit (%s): %s" + .formatted(COUNT_LIMIT, count)); + } for (int i = 0; i < count; ++i) { rebases.add(new Rebase(rebase)); rebase.segmentOffset += pointerSize; @@ -109,6 +118,10 @@ public class RebaseTable extends OpcodeTable { case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: { // 0x80 ulebOffsets.add(reader.getPointerIndex() - origIndex); int count = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + if (count < 0 || count > COUNT_LIMIT) { + throw new MachException("REBASE_OPCODE count exceeds limit (%s): %s" + .formatted(COUNT_LIMIT, count)); + } ulebOffsets.add(reader.getPointerIndex() - origIndex); int skip = reader.readNextUnsignedVarIntExact(LEB128::unsigned); for (int i = 0; i < count; ++i) {