diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/Ext4FileSystem.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/Ext4FileSystem.java index 2e81b503d5..cf6aed8f2e 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/Ext4FileSystem.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/Ext4FileSystem.java @@ -40,6 +40,8 @@ public class Ext4FileSystem extends AbstractFileSystem { public static final Charset EXT4_DEFAULT_CHARSET = StandardCharsets.UTF_8; + private static final int MAX_SANE_INODE_COUNT = 30_000_000; // should handle 500gb disk images + private int blockSize; private ByteProvider provider; private String volumeName; @@ -58,24 +60,17 @@ public class Ext4FileSystem extends AbstractFileSystem { this.volumeName = superBlock.getVolumeName(); this.uuid = NumericUtilities.convertBytesToString(superBlock.getS_uuid()); - long blockCount = superBlock.getS_blocks_count(); - int s_log_block_size = superBlock.getS_log_block_size(); - this.blockSize = (int) Math.pow(2, (10 + s_log_block_size)); - - int groupSize = blockSize * superBlock.getS_blocks_per_group(); - if (groupSize <= 0) { - throw new IOException("Invalid groupSize: " + groupSize); - } - int numGroups = (int) (blockCount / superBlock.getS_blocks_per_group()); - if (blockCount % superBlock.getS_blocks_per_group() != 0) { - numGroups++; + this.blockSize = superBlock.getBlockSize(); + long numGroups = superBlock.getNumGroups(); + if (numGroups > Integer.MAX_VALUE - 1000 /*ensure we can alloc jvm array */) { + throw new IOException("Bad numgroups: " + numGroups); } int groupDescriptorOffset = blockSize + (superBlock.getS_first_data_block() * blockSize); reader.setPointerIndex(groupDescriptorOffset); monitor.initialize(numGroups); monitor.setMessage("Reading inode tables"); - Ext4GroupDescriptor[] groupDescriptors = new Ext4GroupDescriptor[numGroups]; + Ext4GroupDescriptor[] groupDescriptors = new Ext4GroupDescriptor[(int) numGroups]; for (int i = 0; i < numGroups; i++) { groupDescriptors[i] = new Ext4GroupDescriptor(reader, superBlock.is64Bit()); monitor.increment(); @@ -297,6 +292,10 @@ public class Ext4FileSystem extends AbstractFileSystem { private Ext4Inode[] getInodes(BinaryReader reader, Ext4GroupDescriptor[] groupDescriptors, TaskMonitor monitor) throws IOException, CancelledException { + if (superBlock.getS_inodes_count() > MAX_SANE_INODE_COUNT) { + throw new IOException("Inode number too big: " + superBlock.getS_inodes_count()); + } + int inodeCount = superBlock.getS_inodes_count(); int inodesPerGroup = superBlock.getS_inodes_per_group(); Ext4Inode[] inodes = new Ext4Inode[inodeCount + 1]; diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/Ext4FileSystemFactory.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/Ext4FileSystemFactory.java index 8f24d9c4c3..cbcfb13e4a 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/Ext4FileSystemFactory.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/Ext4FileSystemFactory.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,8 +19,7 @@ import java.io.IOException; import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.ByteProvider; -import ghidra.formats.gfilesystem.FSRLRoot; -import ghidra.formats.gfilesystem.FileSystemService; +import ghidra.formats.gfilesystem.*; import ghidra.formats.gfilesystem.factory.GFileSystemFactoryByteProvider; import ghidra.formats.gfilesystem.factory.GFileSystemProbeByteProvider; import ghidra.util.exception.CancelledException; @@ -34,10 +33,16 @@ public class Ext4FileSystemFactory FileSystemService fsService, TaskMonitor monitor) throws IOException, CancelledException { - Ext4FileSystem fs = new Ext4FileSystem(targetFSRL, byteProvider); - fs.mountFS(monitor); + try { + Ext4FileSystem fs = new Ext4FileSystem(targetFSRL, byteProvider); + fs.mountFS(monitor); - return fs; + return fs; + } + catch (IOException e) { + FSUtilities.uncheckedClose(byteProvider, null); + throw e; + } } @Override diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/Ext4SuperBlock.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/Ext4SuperBlock.java index be694b4a13..607178ea17 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/Ext4SuperBlock.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/Ext4SuperBlock.java @@ -125,15 +125,15 @@ public class Ext4SuperBlock implements StructConverter { } public Ext4SuperBlock( BinaryReader reader ) throws IOException { - s_inodes_count = reader.readNextInt(); + s_inodes_count = reader.readNextUnsignedIntExact(); // error if more than 2.1billion s_blocks_count_lo = reader.readNextInt(); s_r_blocks_count_lo = reader.readNextInt(); s_free_blocks_count_lo = reader.readNextInt(); s_free_inodes_count = reader.readNextInt(); s_first_data_block = reader.readNextInt(); - s_log_block_size = reader.readNextInt(); + s_log_block_size = reader.readNextUnsignedIntExact(); s_log_cluster_size = reader.readNextInt(); - s_blocks_per_group = reader.readNextInt(); + s_blocks_per_group = reader.readNextUnsignedIntExact(); s_clusters_per_group = reader.readNextInt(); s_inodes_per_group = reader.readNextInt(); s_mtime = reader.readNextInt(); @@ -254,6 +254,14 @@ public class Ext4SuperBlock implements StructConverter { return s_log_block_size; } + public int getBlockSize() throws IOException { + if (s_log_block_size > 6) { + throw new IOException("Blocksize out of range: " + s_log_block_size); + } + int result = 1 << (10 + s_log_block_size); + return result; + } + public int getS_log_cluster_size() { return s_log_cluster_size; } @@ -262,6 +270,21 @@ public class Ext4SuperBlock implements StructConverter { return s_blocks_per_group; } + private static final int MAX_BLOCKS_PER_GROUP = 1 << 19; + + public long getNumGroups() throws IOException { + int bs = getBlockSize(); + if (s_blocks_per_group < bs || s_blocks_per_group > MAX_BLOCKS_PER_GROUP) { + throw new IOException("Bad blocks per group: " + s_blocks_per_group); + } + long blockCount = getS_blocks_count(); + long numGroups = blockCount / s_blocks_per_group; + if (blockCount % s_blocks_per_group != 0) { + numGroups++; + } + return numGroups; + } + public int getS_clusters_per_group() { return s_clusters_per_group; }