authorgravatar for matt.chudleigh@gmail.comMatt Chudleigh <matt.chudleigh@gmail.com> 2022-11-24 08:13:22-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-09 14:42:42-07:00
logddfcf0246e8d464f921ee70e5a54fa75c3b4d94b
tree17a791ddde73ba13bf044904f9a117408affc7c7
parentc4cac21b72767ea706b196eb87cbe7d64c65c585

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.

1 files changed, 20 insertions(+), 5 deletions(-)

lib/std/zig/parse.zig+20-5
......@@ -131,11 +131,23 @@ const Parser = struct {
131131 return @intCast(Node.Index, i);
132132 }
133133
134 fn reserveNode(p: *Parser) !usize {
134 fn reserveNode(p: *Parser, tag: Ast.Node.Tag) !usize {
135135 try p.nodes.resize(p.gpa, p.nodes.len + 1);
136 p.nodes.items(.tag)[p.nodes.len - 1] = tag;
136137 return p.nodes.len - 1;
137138 }
138139
140 fn unreserveNode(p: *Parser, node_index: usize) void {
141 if (p.nodes.len == node_index) {
142 p.nodes.resize(p.gpa, p.nodes.len - 1) catch unreachable;
143 } else {
144 // There is zombie node left in the tree, let's make it as inoffensive as possible
145 // (sadly there's no no-op node)
146 p.nodes.items(.tag)[node_index] = .unreachable_literal;
147 p.nodes.items(.main_token)[node_index] = p.tok_i;
148 }
149 }
150
139151 fn addExtra(p: *Parser, extra: anytype) Allocator.Error!Node.Index {
140152 const fields = std.meta.fields(@TypeOf(extra));
141153 try p.extra_data.ensureUnusedCapacity(p.gpa, fields.len);
......@@ -637,13 +649,15 @@ const Parser = struct {
637649 return fn_proto;
638650 },
639651 .l_brace => {
640 const fn_decl_index = try p.reserveNode();
641 const body_block = try p.parseBlock();
642 assert(body_block != 0);
643652 if (is_extern) {
644653 try p.warnMsg(.{ .tag = .extern_fn_body, .token = extern_export_inline_token });
645654 return null_node;
646655 }
656 const fn_decl_index = try p.reserveNode(.fn_decl);
657 errdefer p.unreserveNode(fn_decl_index);
658
659 const body_block = try p.parseBlock();
660 assert(body_block != 0);
647661 return p.setNode(fn_decl_index, .{
648662 .tag = .fn_decl,
649663 .main_token = p.nodes.items(.main_token)[fn_proto],
......@@ -724,7 +738,8 @@ const Parser = struct {
724738 const fn_token = p.eatToken(.keyword_fn) orelse return null_node;
725739
726740 // We want the fn proto node to be before its children in the array.
727 const fn_proto_index = try p.reserveNode();
741 const fn_proto_index = try p.reserveNode(.fn_proto);
742 errdefer p.unreserveNode(fn_proto_index);
728743
729744 _ = p.eatToken(.identifier);
730745 const params = try p.parseParamDeclList();