aboutsummaryrefslogtreecommitdiff
path: root/lib/std/debug/Pdb.zig
diff options
context:
space:
mode:
authormlugg <mlugg@mlugg.co.uk>2025-09-18 12:54:33 +0100
committermlugg <mlugg@mlugg.co.uk>2025-09-30 13:44:55 +0100
commit9434bab3134edadae7ae7e575f6b025cafc6a59a (patch)
tree141c3ad4208426f1cb4e613ce92a7fd7f57ea044 /lib/std/debug/Pdb.zig
parent23d6381e8b5fb63bc9cba5cc5c78b7946ca4746a (diff)
downloadzig-9434bab3134edadae7ae7e575f6b025cafc6a59a.tar.gz
zig-9434bab3134edadae7ae7e575f6b025cafc6a59a.zip
std: work around crash parsing LLVM PDB
This crash exists on master, and seems to have existed since 2019; I think it's just very rare and depends on the exact binary generated. In theory, a stream block should always be a "data" block rather than a FPM block; the FPMs use blocks `1, 4097, 8193, ...` and `2, 4097, 8194, ...` respectively. However, I have observed LLVM emitting an otherwise valid PDB which maps FPM blocks into streams. This is not a bug in `std.debug.Pdb`, because `llvm-pdbutil` agrees with our stream indices. I think this is arguably an LLVM bug; however, we don't really lose anything from just weakening this check. To be fair, MSF doesn't have an explicit specification, and LLVM's documentation (which is the closest thing we have) does not explicitly state that FPM blocks cannot be mapped into streams, so perhaps this is actually valid. In the rare case that LLVM emits this, previously, stack traces would have been completely useless; now, stack traces will work okay.
Diffstat (limited to 'lib/std/debug/Pdb.zig')
-rw-r--r--lib/std/debug/Pdb.zig36
1 files changed, 18 insertions, 18 deletions
diff --git a/lib/std/debug/Pdb.zig b/lib/std/debug/Pdb.zig
index 66010ba377..c10b361f72 100644
--- a/lib/std/debug/Pdb.zig
+++ b/lib/std/debug/Pdb.zig
@@ -413,8 +413,7 @@ const Msf = struct {
return error.InvalidDebugInfo;
if (superblock.free_block_map_block != 1 and superblock.free_block_map_block != 2)
return error.InvalidDebugInfo;
- const file_len = try file_reader.getSize();
- if (superblock.num_blocks * superblock.block_size != file_len)
+ if (superblock.num_blocks * superblock.block_size != try file_reader.getSize())
return error.InvalidDebugInfo;
switch (superblock.block_size) {
// llvm only supports 4096 but we can handle any of these values
@@ -428,6 +427,7 @@ const Msf = struct {
try file_reader.seekTo(superblock.block_size * superblock.block_map_addr);
const dir_blocks = try gpa.alloc(u32, dir_block_count);
+ errdefer gpa.free(dir_blocks);
for (dir_blocks) |*b| {
b.* = try file_reader.interface.takeInt(u32, .little);
}
@@ -451,25 +451,25 @@ const Msf = struct {
const streams = try gpa.alloc(MsfStream, stream_count);
errdefer gpa.free(streams);
- for (streams, 0..) |*stream, i| {
- const size = stream_sizes[i];
+ for (streams, stream_sizes) |*stream, size| {
if (size == 0) {
stream.* = .empty;
- } else {
- const blocks = try gpa.alloc(u32, size);
- errdefer gpa.free(blocks);
- for (blocks) |*block| {
- const block_id = try directory.interface.takeInt(u32, .little);
- const n = (block_id % superblock.block_size);
- // 0 is for pdb.SuperBlock, 1 and 2 for FPMs.
- if (block_id == 0 or n == 1 or n == 2 or block_id * superblock.block_size > file_len)
- return error.InvalidBlockIndex;
- block.* = block_id;
- }
- const buffer = try gpa.alloc(u8, 64);
- errdefer gpa.free(buffer);
- stream.* = .init(superblock.block_size, file_reader, blocks, buffer);
+ continue;
+ }
+ const blocks = try gpa.alloc(u32, size);
+ errdefer gpa.free(blocks);
+ for (blocks) |*block| {
+ const block_id = try directory.interface.takeInt(u32, .little);
+ // Index 0 is reserved for the superblock.
+ // In theory, every page which is `n * block_size + 1` or `n * block_size + 2`
+ // is also reserved, for one of the FPMs. However, LLVM has been observed to map
+ // these into actual streams, so allow it for compatibility.
+ if (block_id == 0 or block_id >= superblock.num_blocks) return error.InvalidBlockIndex;
+ block.* = block_id;
}
+ const buffer = try gpa.alloc(u8, 64);
+ errdefer gpa.free(buffer);
+ stream.* = .init(superblock.block_size, file_reader, blocks, buffer);
}
const end = directory.logicalPos();