return result;
}
+ if (sb.s_log_block_size > 2) {
+ kprintf("[EXT2] Invalid s_log_block_size: %u\n", sb.s_log_block_size);
+ kfree(em);
+ return result;
+ }
+
em->block_size = 1024U << sb.s_log_block_size;
/* Validate block_size is power of 2 and >= 1024 */
if (em->block_size < 1024 || em->block_size > 4096 ||
em->total_blocks = sb.s_blocks_count;
em->total_inodes = sb.s_inodes_count;
+ if (em->total_blocks == 0 || em->total_inodes == 0) {
+ kprintf("[EXT2] Invalid filesystem size: blocks=%u inodes=%u\n",
+ em->total_blocks, em->total_inodes);
+ kfree(em);
+ return result;
+ }
+ if (em->first_data_block >= em->total_blocks) {
+ kprintf("[EXT2] Invalid first_data_block %u for total blocks %u\n",
+ em->first_data_block, em->total_blocks);
+ kfree(em);
+ return result;
+ }
+
if (sb.s_rev_level >= 1 && sb.s_inode_size != 0) {
em->inode_size = sb.s_inode_size;
/* Validate inode size is reasonable */
kfree(em);
return result;
}
- em->num_groups = (sb.s_blocks_count + sb.s_blocks_per_group - 1) / sb.s_blocks_per_group;
+ uint64_t num_groups64 = ((uint64_t)sb.s_blocks_count + (uint64_t)sb.s_blocks_per_group - 1ULL) /
+ (uint64_t)sb.s_blocks_per_group;
+ if (num_groups64 == 0 || num_groups64 > 65536ULL) {
+ kprintf("[EXT2] Invalid num_groups: %u\n", (uint32_t)num_groups64);
+ kfree(em);
+ return result;
+ }
+ em->num_groups = (uint32_t)num_groups64;
/* Read Group Descriptor Table */
- /* Validate gdt_bytes doesn't overflow */
- if (em->num_groups > 65536) { /* Reasonable limit for num_groups */
- kprintf("[EXT2] Invalid num_groups: %u\n", em->num_groups);
+ uint64_t gdt_bytes64 = (uint64_t)em->num_groups * (uint64_t)sizeof(struct ext2_group_desc);
+ if (gdt_bytes64 == 0 || gdt_bytes64 > 16ULL * 1024ULL * 1024ULL) {
+ kprintf("[EXT2] GDT too large: %u bytes\n", (uint32_t)gdt_bytes64);
kfree(em);
return result;
}
- em->gdt_blocks = (em->num_groups * (uint32_t)sizeof(struct ext2_group_desc) +
- em->block_size - 1) / em->block_size;
- uint32_t gdt_bytes = em->num_groups * (uint32_t)sizeof(struct ext2_group_desc);
- /* Validate gdt_bytes doesn't overflow and is reasonable */
- if (gdt_bytes > 16 * 1024 * 1024) { /* Max 16MB for GDT */
- kprintf("[EXT2] GDT too large: %u bytes\n", gdt_bytes);
+ uint32_t gdt_bytes = (uint32_t)gdt_bytes64;
+ uint64_t gdt_blocks64 = (gdt_bytes64 + (uint64_t)em->block_size - 1ULL) / (uint64_t)em->block_size;
+ if (gdt_blocks64 == 0 || gdt_blocks64 > em->total_blocks) {
+ kprintf("[EXT2] GDT blocks %u exceed total blocks %u\n", (uint32_t)gdt_blocks64, em->total_blocks);
kfree(em);
return result;
}
+ em->gdt_blocks = (uint32_t)gdt_blocks64;
/* Validate gdt_blocks against device size */
- if (em->gdt_blocks > em->total_blocks) {
- kprintf("[EXT2] GDT blocks %u exceed total blocks %u\n", em->gdt_blocks, em->total_blocks);
+ uint64_t gdt_end_block = (uint64_t)em->first_data_block + 1ULL + gdt_blocks64;
+ if (gdt_end_block > em->total_blocks) {
+ kprintf("[EXT2] GDT end %u exceeds total blocks %u\n", (uint32_t)gdt_end_block, em->total_blocks);
kfree(em);
return result;
}
return 0x0FFFFFFF;
}
+static int fat_cluster_valid(struct fat_mount* fm, uint32_t cluster) {
+ if (!fm || fm->total_clusters == 0) return 0;
+ return cluster >= 2 && (uint64_t)cluster < (uint64_t)fm->total_clusters + 2ULL;
+}
+
static uint32_t fat_cluster_to_lba(struct fat_mount* fm, uint32_t cluster) {
return fm->data_lba + (cluster - 2) * fm->sectors_per_cluster;
}
static uint32_t fat_follow_chain(struct fat_mount* fm, uint32_t start, uint32_t n) {
uint32_t c = start;
for (uint32_t i = 0; i < n; i++) {
- if (c < 2 || fat_is_eoc(fm, c)) return 0;
+ if (!fat_cluster_valid(fm, c) || fat_is_eoc(fm, c)) return 0;
c = fat_get_entry(fm, c);
}
- return (c >= 2 && !fat_is_eoc(fm, c)) ? c : (n == 0 ? start : 0);
+ if (n == 0) return fat_cluster_valid(fm, start) ? start : 0;
+ return (fat_cluster_valid(fm, c) && !fat_is_eoc(fm, c)) ? c : 0;
}
/* Count clusters in chain. */
static uint32_t fat_chain_length(struct fat_mount* fm, uint32_t start) {
- if (start < 2) return 0;
+ if (!fat_cluster_valid(fm, start)) return 0;
uint32_t count = 0;
uint32_t c = start;
- while (c >= 2 && !fat_is_eoc(fm, c) && count < fm->total_clusters) {
+ while (fat_cluster_valid(fm, c) && !fat_is_eoc(fm, c) && count < fm->total_clusters) {
count++;
c = fat_get_entry(fm, c);
}
/* Free a cluster chain starting at 'start'. */
static void fat_free_chain(struct fat_mount* fm, uint32_t start) {
uint32_t c = start;
- while (c >= 2 && !fat_is_eoc(fm, c)) {
+ uint32_t steps = 0;
+ while (fat_cluster_valid(fm, c) && !fat_is_eoc(fm, c) && steps < fm->total_clusters) {
uint32_t next = fat_get_entry(fm, c);
(void)fat_set_entry(fm, c, 0);
+ if (!fat_cluster_valid(fm, next) || fat_is_eoc(fm, next)) {
+ c = next;
+ break;
+ }
c = next;
+ steps++;
}
- if (c >= 2) {
+ if (fat_cluster_valid(fm, c)) {
(void)fat_set_entry(fm, c, 0);
}
}
/* Total data sectors & cluster count determine FAT type */
uint32_t total_sectors = bpb->total_sectors_16 ? bpb->total_sectors_16 : bpb->total_sectors_32;
- uint32_t data_sectors = total_sectors - (fm->data_lba - partition_lba);
+ uint32_t used_sectors = fm->data_lba - partition_lba;
+ if (used_sectors >= total_sectors) {
+ kprintf("[FAT] Invalid layout: used sectors %u exceed total sectors %u\n",
+ used_sectors, total_sectors);
+ kfree(fm);
+ return result;
+ }
+ uint32_t data_sectors = total_sectors - used_sectors;
fm->total_clusters = data_sectors / fm->sectors_per_cluster;
+ if (fm->total_clusters == 0) {
+ kprintf("[FAT] Invalid cluster count: 0\n");
+ kfree(fm);
+ return result;
+ }
/* Microsoft FAT spec: type is determined by cluster count */
if (fm->total_clusters < 4085) {
fm->type = FAT_TYPE_32;
}
+ if (fm->type == FAT_TYPE_32 && !fat_cluster_valid(fm, fm->root_cluster)) {
+ kprintf("[FAT] Invalid FAT32 root_cluster %u\n", fm->root_cluster);
+ kfree(fm);
+ return result;
+ }
+
/* Build root node */
struct fat_node* root = (struct fat_node*)kmalloc(sizeof(struct fat_node));
if (!root) {
return 0;
}
+static int fat_mount_layout_valid_test(uint32_t partition_lba,
+ uint32_t data_lba,
+ uint32_t total_sectors,
+ uint8_t sectors_per_cluster,
+ int is_fat32,
+ uint32_t root_cluster) {
+ if (sectors_per_cluster == 0) return -1;
+ uint32_t used_sectors = data_lba - partition_lba;
+ if (used_sectors >= total_sectors) return -1;
+ uint32_t data_sectors = total_sectors - used_sectors;
+ uint32_t total_clusters = data_sectors / sectors_per_cluster;
+ if (total_clusters == 0) return -1;
+ if (is_fat32) {
+ if (root_cluster < 2) return -1;
+ if ((uint64_t)root_cluster >= (uint64_t)total_clusters + 2ULL) return -1;
+ }
+ return 0;
+}
+
+static int ext2_mount_geometry_valid_test(uint32_t s_log_block_size,
+ uint32_t total_blocks,
+ uint32_t total_inodes,
+ uint32_t first_data_block,
+ uint32_t blocks_per_group) {
+ if (blocks_per_group == 0) return -1;
+ if (s_log_block_size > 2) return -1;
+ uint32_t block_size = 1024U << s_log_block_size;
+ if (block_size < 1024 || block_size > 4096 ||
+ (block_size & (block_size - 1)) != 0) return -1;
+ if (total_blocks == 0 || total_inodes == 0) return -1;
+ if (first_data_block >= total_blocks) return -1;
+ uint64_t num_groups = ((uint64_t)total_blocks + (uint64_t)blocks_per_group - 1ULL) /
+ (uint64_t)blocks_per_group;
+ if (num_groups == 0 || num_groups > 65536ULL) return -1;
+ uint64_t gdt_bytes = num_groups * 32ULL;
+ if (gdt_bytes == 0 || gdt_bytes > 16ULL * 1024ULL * 1024ULL) return -1;
+ uint64_t gdt_blocks = (gdt_bytes + (uint64_t)block_size - 1ULL) / (uint64_t)block_size;
+ if (gdt_blocks == 0 || gdt_blocks > total_blocks) return -1;
+ if ((uint64_t)first_data_block + 1ULL + gdt_blocks > total_blocks) return -1;
+ return 0;
+}
+
/* ---- Minimal test framework ---- */
static int g_tests_run = 0;
static int g_tests_passed = 0;
ASSERT_EQ(init_resolve_mount_device_test("/dev/doesnotexist", &bdev, &lba), -1);
}
+TEST(fat_layout_rejects_used_sectors_overflow) {
+ ASSERT_EQ(fat_mount_layout_valid_test(2048, 4096, 2048, 1, 0, 0), -1);
+}
+
+TEST(fat_layout_rejects_zero_clusters) {
+ ASSERT_EQ(fat_mount_layout_valid_test(2048, 2049, 2, 4, 0, 0), -1);
+}
+
+TEST(fat_layout_rejects_invalid_fat32_root_cluster) {
+ ASSERT_EQ(fat_mount_layout_valid_test(2048, 4096, 16384, 1, 1, 20000), -1);
+}
+
+TEST(ext2_geometry_rejects_invalid_log_block_size) {
+ ASSERT_EQ(ext2_mount_geometry_valid_test(3, 8192, 1024, 1, 8192), -1);
+}
+
+TEST(ext2_geometry_rejects_invalid_first_data_block) {
+ ASSERT_EQ(ext2_mount_geometry_valid_test(1, 4096, 1024, 4096, 1024), -1);
+}
+
+TEST(ext2_geometry_rejects_gdt_end_overflow) {
+ ASSERT_EQ(ext2_mount_geometry_valid_test(0, 64, 128, 63, 1), -1);
+}
+
/* ======== MAIN ======== */
int main(void) {
printf("\n=========================================\n");
RUN(resolve_mount_device_blockdev_devpath);
RUN(resolve_mount_device_partition);
RUN(resolve_mount_device_unknown);
+ RUN(fat_layout_rejects_used_sectors_overflow);
+ RUN(fat_layout_rejects_zero_clusters);
+ RUN(fat_layout_rejects_invalid_fat32_root_cluster);
+ RUN(ext2_geometry_rejects_invalid_log_block_size);
+ RUN(ext2_geometry_rejects_invalid_first_data_block);
+ RUN(ext2_geometry_rejects_gdt_end_overflow);
printf("\n %d/%d passed, %d failed\n", g_tests_passed, g_tests_run, g_tests_failed);