volume-manager: cover the GPT non-128 entry-size offset path (S1 review)

The S1 adversarial boundary review found the off = (i*entry_size) % 512
arithmetic tested only for 128-byte entries. Add a test with 256-byte entries
and the sole valid entry at index 1 (offset 256), exercising the non-zero-offset
path. No code change — the parser was already correct (off is always a multiple
of entry_size >= 128, so off + 128 <= 512); this closes the coverage gap.
This commit is contained in:
Daniel Samson
2026-08-09 23:37:52 +01:00
parent aca3d5855a
commit ea8ccf65d0
+30 -7
View File
@@ -351,9 +351,9 @@ fn gptEntry(type_nonzero: bool, unique_guid: u128, start: u64, end: u64) [128]u8
return e; return e;
} }
/// Lay out a 4-sector disk: protective MBR (LBA 0), GPT header with a correct /// Lay out a disk with `entry_size`-spaced GPT entries: protective MBR (LBA 0),
/// CRC (LBA 1), and the entry array (LBA 2). /// GPT header with a correct CRC (LBA 1), the entry array (LBA 2+).
fn buildGptDisk(disk: []u8, entries: []const [128]u8) void { fn buildGptDiskSized(disk: []u8, entries: []const [128]u8, entry_size: u32) void {
@memset(disk, 0); @memset(disk, 0);
disk[510] = 0x55; disk[510] = 0x55;
disk[511] = 0xAA; disk[511] = 0xAA;
@@ -365,16 +365,22 @@ fn buildGptDisk(disk: []u8, entries: []const [128]u8) void {
std.mem.writeInt(u32, h[12..16], 92, .little); // header_size std.mem.writeInt(u32, h[12..16], 92, .little); // header_size
std.mem.writeInt(u64, h[72..80], 2, .little); // partition_entry_lba std.mem.writeInt(u64, h[72..80], 2, .little); // partition_entry_lba
std.mem.writeInt(u32, h[80..84], @intCast(entries.len), .little); std.mem.writeInt(u32, h[80..84], @intCast(entries.len), .little);
std.mem.writeInt(u32, h[84..88], 128, .little); // size_of_partition_entry std.mem.writeInt(u32, h[84..88], entry_size, .little); // size_of_partition_entry
@memset(h[16..20], 0); @memset(h[16..20], 0);
std.mem.writeInt(u32, h[16..20], crc32(h[0..92]), .little); std.mem.writeInt(u32, h[16..20], crc32(h[0..92]), .little);
const ea = disk[2 * sector_bytes ..][0..sector_bytes]; const step: usize = @intCast(entry_size);
var i: usize = 0; var i: usize = 0;
while (i < entries.len and i < 4) : (i += 1) { while (i < entries.len) : (i += 1) {
@memcpy(ea[i * 128 ..][0..128], &entries[i]); const abs = 2 * sector_bytes + i * step;
@memcpy(disk[abs..][0..128], &entries[i]);
} }
} }
/// The common 128-byte-entry case.
fn buildGptDisk(disk: []u8, entries: []const [128]u8) void {
buildGptDiskSized(disk, entries, 128);
}
test "a GPT disk yields the partition GUID as the identity id" { test "a GPT disk yields the partition GUID as the identity id" {
var disk = [_]u8{0} ** (4 * sector_bytes); var disk = [_]u8{0} ** (4 * sector_bytes);
const guid: u128 = 0x112233445566778899AABBCCDDEEFF00; const guid: u128 = 0x112233445566778899AABBCCDDEEFF00;
@@ -468,3 +474,20 @@ test "an MBR FAT partition prefers the volume serial; a non-FAT partition keeps
try std.testing.expectEqual(Rung.mbr_index, v2.identity.rung); try std.testing.expectEqual(Rung.mbr_index, v2.identity.rung);
try std.testing.expectEqual((@as(u128, 0xDEADBEEF) << 8) | 0, v2.identity.key); try std.testing.expectEqual((@as(u128, 0xDEADBEEF) << 8) | 0, v2.identity.key);
} }
test "GPT with 256-byte entries reads the non-128 offset arithmetic correctly" {
// With entry_size 256, entry 1 lands at offset 256 of the same sector (LBA 2).
// Put the only valid entry at index 1 so the off = (i*entry_size) % 512 path
// (256, not 0) is exercised — the sharp edge the 128-byte tests never hit.
var disk = [_]u8{0} ** (5 * sector_bytes);
const entries = [_][128]u8{
gptEntry(false, 0, 0, 0), // index 0: unused (type GUID zero)
gptEntry(true, 0xF00D, 4096, 8191), // index 1: at offset 256
};
buildGptDiskSized(&disk, &entries, 256);
const rd = RamDisk{ .sectors = &disk };
const v = firstVolume(rd.reader(), 200000).?;
try std.testing.expectEqual(@as(u64, 4096), v.base_lba);
try std.testing.expectEqual(Rung.gpt_guid, v.identity.rung);
try std.testing.expectEqual(@as(u128, 0xF00D), v.identity.key);
}