From f61c5f3f5242fb3e96891c82eb3dd16fdb8a2c7b Mon Sep 17 00:00:00 2001 From: Matt Chudleigh Date: Thu, 24 Nov 2022 08:13:22 -0800 Subject: Bug fix: Prevent uninitialized parse nodes If a parse node is reserved but never set the node remains uninitialized and can crash tools doing a linear scan of the nodes (like ZLS) when switching on the tag. --- lib/std/zig/parse.zig | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'lib/std') diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 0226ec2e1d..77ed67b3d2 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -131,11 +131,23 @@ const Parser = struct { return @intCast(Node.Index, i); } - fn reserveNode(p: *Parser) !usize { + fn reserveNode(p: *Parser, tag: Ast.Node.Tag) !usize { try p.nodes.resize(p.gpa, p.nodes.len + 1); + p.nodes.items(.tag)[p.nodes.len - 1] = tag; return p.nodes.len - 1; } + fn unreserveNode(p: *Parser, node_index: usize) void { + if (p.nodes.len == node_index) { + p.nodes.resize(p.gpa, p.nodes.len - 1) catch unreachable; + } else { + // There is zombie node left in the tree, let's make it as inoffensive as possible + // (sadly there's no no-op node) + p.nodes.items(.tag)[node_index] = .unreachable_literal; + p.nodes.items(.main_token)[node_index] = p.tok_i; + } + } + fn addExtra(p: *Parser, extra: anytype) Allocator.Error!Node.Index { const fields = std.meta.fields(@TypeOf(extra)); try p.extra_data.ensureUnusedCapacity(p.gpa, fields.len); @@ -637,13 +649,15 @@ const Parser = struct { return fn_proto; }, .l_brace => { - const fn_decl_index = try p.reserveNode(); - const body_block = try p.parseBlock(); - assert(body_block != 0); if (is_extern) { try p.warnMsg(.{ .tag = .extern_fn_body, .token = extern_export_inline_token }); return null_node; } + const fn_decl_index = try p.reserveNode(.fn_decl); + errdefer p.unreserveNode(fn_decl_index); + + const body_block = try p.parseBlock(); + assert(body_block != 0); return p.setNode(fn_decl_index, .{ .tag = .fn_decl, .main_token = p.nodes.items(.main_token)[fn_proto], @@ -724,7 +738,8 @@ const Parser = struct { const fn_token = p.eatToken(.keyword_fn) orelse return null_node; // We want the fn proto node to be before its children in the array. - const fn_proto_index = try p.reserveNode(); + const fn_proto_index = try p.reserveNode(.fn_proto); + errdefer p.unreserveNode(fn_proto_index); _ = p.eatToken(.identifier); const params = try p.parseParamDeclList(); -- cgit v1.2.3