Merge remote-tracking branch 'origin/GP-6712_dev747368_ext4_fixes'

This commit is contained in:
Ryan Kurtz
2026-04-20 13:40:39 -04:00
3 changed files with 49 additions and 22 deletions
@@ -40,6 +40,8 @@ public class Ext4FileSystem extends AbstractFileSystem<Ext4File> {
public static final Charset EXT4_DEFAULT_CHARSET = StandardCharsets.UTF_8; 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 int blockSize;
private ByteProvider provider; private ByteProvider provider;
private String volumeName; private String volumeName;
@@ -58,24 +60,17 @@ public class Ext4FileSystem extends AbstractFileSystem<Ext4File> {
this.volumeName = superBlock.getVolumeName(); this.volumeName = superBlock.getVolumeName();
this.uuid = NumericUtilities.convertBytesToString(superBlock.getS_uuid()); this.uuid = NumericUtilities.convertBytesToString(superBlock.getS_uuid());
long blockCount = superBlock.getS_blocks_count(); this.blockSize = superBlock.getBlockSize();
int s_log_block_size = superBlock.getS_log_block_size(); long numGroups = superBlock.getNumGroups();
this.blockSize = (int) Math.pow(2, (10 + s_log_block_size)); if (numGroups > Integer.MAX_VALUE - 1000 /*ensure we can alloc jvm array */) {
throw new IOException("Bad numgroups: " + numGroups);
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++;
} }
int groupDescriptorOffset = blockSize + (superBlock.getS_first_data_block() * blockSize); int groupDescriptorOffset = blockSize + (superBlock.getS_first_data_block() * blockSize);
reader.setPointerIndex(groupDescriptorOffset); reader.setPointerIndex(groupDescriptorOffset);
monitor.initialize(numGroups); monitor.initialize(numGroups);
monitor.setMessage("Reading inode tables"); monitor.setMessage("Reading inode tables");
Ext4GroupDescriptor[] groupDescriptors = new Ext4GroupDescriptor[numGroups]; Ext4GroupDescriptor[] groupDescriptors = new Ext4GroupDescriptor[(int) numGroups];
for (int i = 0; i < numGroups; i++) { for (int i = 0; i < numGroups; i++) {
groupDescriptors[i] = new Ext4GroupDescriptor(reader, superBlock.is64Bit()); groupDescriptors[i] = new Ext4GroupDescriptor(reader, superBlock.is64Bit());
monitor.increment(); monitor.increment();
@@ -297,6 +292,10 @@ public class Ext4FileSystem extends AbstractFileSystem<Ext4File> {
private Ext4Inode[] getInodes(BinaryReader reader, Ext4GroupDescriptor[] groupDescriptors, private Ext4Inode[] getInodes(BinaryReader reader, Ext4GroupDescriptor[] groupDescriptors,
TaskMonitor monitor) throws IOException, CancelledException { 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 inodeCount = superBlock.getS_inodes_count();
int inodesPerGroup = superBlock.getS_inodes_per_group(); int inodesPerGroup = superBlock.getS_inodes_per_group();
Ext4Inode[] inodes = new Ext4Inode[inodeCount + 1]; Ext4Inode[] inodes = new Ext4Inode[inodeCount + 1];
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 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.BinaryReader;
import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.ByteProvider;
import ghidra.formats.gfilesystem.FSRLRoot; import ghidra.formats.gfilesystem.*;
import ghidra.formats.gfilesystem.FileSystemService;
import ghidra.formats.gfilesystem.factory.GFileSystemFactoryByteProvider; import ghidra.formats.gfilesystem.factory.GFileSystemFactoryByteProvider;
import ghidra.formats.gfilesystem.factory.GFileSystemProbeByteProvider; import ghidra.formats.gfilesystem.factory.GFileSystemProbeByteProvider;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
@@ -34,10 +33,16 @@ public class Ext4FileSystemFactory
FileSystemService fsService, TaskMonitor monitor) FileSystemService fsService, TaskMonitor monitor)
throws IOException, CancelledException { throws IOException, CancelledException {
Ext4FileSystem fs = new Ext4FileSystem(targetFSRL, byteProvider); try {
fs.mountFS(monitor); Ext4FileSystem fs = new Ext4FileSystem(targetFSRL, byteProvider);
fs.mountFS(monitor);
return fs; return fs;
}
catch (IOException e) {
FSUtilities.uncheckedClose(byteProvider, null);
throw e;
}
} }
@Override @Override
@@ -125,15 +125,15 @@ public class Ext4SuperBlock implements StructConverter {
} }
public Ext4SuperBlock( BinaryReader reader ) throws IOException { 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_blocks_count_lo = reader.readNextInt();
s_r_blocks_count_lo = reader.readNextInt(); s_r_blocks_count_lo = reader.readNextInt();
s_free_blocks_count_lo = reader.readNextInt(); s_free_blocks_count_lo = reader.readNextInt();
s_free_inodes_count = reader.readNextInt(); s_free_inodes_count = reader.readNextInt();
s_first_data_block = 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_log_cluster_size = reader.readNextInt();
s_blocks_per_group = reader.readNextInt(); s_blocks_per_group = reader.readNextUnsignedIntExact();
s_clusters_per_group = reader.readNextInt(); s_clusters_per_group = reader.readNextInt();
s_inodes_per_group = reader.readNextInt(); s_inodes_per_group = reader.readNextInt();
s_mtime = reader.readNextInt(); s_mtime = reader.readNextInt();
@@ -254,6 +254,14 @@ public class Ext4SuperBlock implements StructConverter {
return s_log_block_size; 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() { public int getS_log_cluster_size() {
return s_log_cluster_size; return s_log_cluster_size;
} }
@@ -262,6 +270,21 @@ public class Ext4SuperBlock implements StructConverter {
return s_blocks_per_group; 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() { public int getS_clusters_per_group() {
return s_clusters_per_group; return s_clusters_per_group;
} }