Skip to content

Commit fe9e34d

Browse files
committed
fix crash in html tokenizer
1 parent 51c2dbf commit fe9e34d

7 files changed

Lines changed: 85 additions & 73 deletions

File tree

build.zig

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,23 +107,26 @@ fn setupFuzzStep(
107107
.name = "superfuzz-afl",
108108
.root_source_file = b.path("src/fuzz/afl.zig"),
109109
.target = target,
110-
.optimize = .Debug,
111-
.single_threaded = true,
110+
.optimize = .ReleaseSafe,
112111
});
113112

114113
afl_obj.root_module.addImport("superhtml", superhtml);
115114
afl_obj.root_module.stack_check = false; // not linking with compiler-rt
116115
afl_obj.root_module.link_libc = true; // afl runtime depends on libc
117116

118-
const afl_fuzz = afl.addInstrumentedExe(b, afl_obj);
119-
fuzz.dependOn(&b.addInstallFile(afl_fuzz, "superfuzz-afl").step);
117+
// const afl_fuzz = afl.addInstrumentedExe(
118+
// b,
119+
// target,
120+
// .ReleaseSafe,
121+
// afl_obj,
122+
// );
123+
// fuzz.dependOn(&b.addInstallFile(afl_fuzz, "superfuzz-afl").step);
120124

121125
const super_fuzz = b.addExecutable(.{
122126
.name = "superfuzz",
123127
.root_source_file = b.path("src/fuzz.zig"),
124128
.target = target,
125-
.optimize = .Debug,
126-
.single_threaded = true,
129+
.optimize = .ReleaseSafe,
127130
});
128131

129132
super_fuzz.root_module.addImport("superhtml", superhtml);
@@ -134,7 +137,6 @@ fn setupFuzzStep(
134137
.root_source_file = b.path("src/fuzz/astgen.zig"),
135138
.target = target,
136139
.optimize = .Debug,
137-
.single_threaded = true,
138140
});
139141

140142
supergen.root_module.addImport("superhtml", superhtml);
@@ -286,7 +288,7 @@ fn getVersion(b: *std.Build) Version {
286288
0 => return .{ .tag = git_describe },
287289
2 => {
288290
// Untagged development build (e.g. 0.8.0-684-gbbe2cca1a).
289-
var it = std.mem.split(u8, git_describe, "-");
291+
var it = std.mem.splitScalar(u8, git_describe, '-');
290292
const tagged_ancestor = it.next() orelse unreachable;
291293
const commit_height = it.next() orelse unreachable;
292294
const commit_id = it.next() orelse unreachable;

build.zig.zon

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
.hash = "1220165019c862f801952d1529b990ec72de3d3b53c3a81be23500b1a10cac7266c2",
1616
},
1717
.@"zig-afl-kit" = .{
18-
.url = "git+https://github.com/kristoff-it/zig-afl-kit#8784c51828b0fc9774a57d83d345f4ab73c23ffd",
19-
.hash = "12205cb4a9a2c74fe9c501ff290367e22e93e69811e4fd8bdb8a614b3fdabd2b5fe7",
18+
.url = "git+https://github.com/kristoff-it/zig-afl-kit#f003bfe714f2964c90939fdc940d5993190a66ec",
19+
.hash = "1220f2d8402bb7bbc4786b9c0aad73910929ea209cbd3b063842371d68abfed33c1e",
2020
},
2121
},
2222
.paths = .{

src/cli/lsp/logic.zig

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,18 @@ pub fn loadFile(
1515
uri: []const u8,
1616
language: super.Language,
1717
) !void {
18+
errdefer @panic("error while loading document!");
19+
1820
var res: lsp.types.PublishDiagnosticsParams = .{
1921
.uri = uri,
2022
.diagnostics = &.{},
2123
};
2224

23-
var doc = try Document.init(
25+
const doc = try Document.init(
2426
self.gpa,
2527
new_text,
2628
language,
2729
);
28-
errdefer doc.deinit(self.gpa);
2930

3031
log.debug("document init", .{});
3132

src/fuzz/afl.c

Lines changed: 0 additions & 31 deletions
This file was deleted.

src/fuzz/afl.zig

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,46 +4,43 @@ const astgen = @import("astgen.zig");
44

55
pub const std_options = .{ .log_level = .err };
66

7-
const mem = std.mem;
7+
export fn zig_fuzz_init() void {}
88

9-
// const toggle_me = std.mem.backend_can_use_eql_bytes;
10-
// comptime {
11-
// std.debug.assert(toggle_me == false);
12-
// }
9+
export fn zig_fuzz_test(buf: [*]u8, len: isize) void {
10+
var gpa_impl: std.heap.GeneralPurposeAllocator(.{}) = .{};
11+
defer std.debug.assert(gpa_impl.deinit() == .ok);
1312

14-
var gpa_impl: std.heap.GeneralPurposeAllocator(.{}) = .{};
15-
export fn zig_fuzz_test_direct(buf: [*]u8, len: isize) void {
1613
const gpa = gpa_impl.allocator();
1714
const src = buf[0..@intCast(len)];
1815

1916
const html_ast = super.html.Ast.init(gpa, src, .superhtml) catch unreachable;
2017
defer html_ast.deinit(gpa);
2118

22-
if (html_ast.errors.len == 0) {
23-
const super_ast = super.Ast.init(gpa, html_ast, src) catch unreachable;
24-
defer super_ast.deinit(gpa);
25-
}
26-
2719
// if (html_ast.errors.len == 0) {
28-
// var out = std.ArrayList(u8).init(gpa);
29-
// defer out.deinit();
30-
// html_ast.render(src, out.writer()) catch unreachable;
20+
// const super_ast = super.Ast.init(gpa, html_ast, src) catch unreachable;
21+
// defer super_ast.deinit(gpa);
22+
// }
3123

32-
// eqlIgnoreWhitespace(src, out.items);
24+
if (html_ast.errors.len == 0) {
25+
var out = std.ArrayList(u8).init(gpa);
26+
defer out.deinit();
27+
html_ast.render(src, out.writer()) catch unreachable;
3328

34-
// var full_circle = std.ArrayList(u8).init(gpa);
35-
// defer full_circle.deinit();
36-
// html_ast.render(out.items, full_circle.writer()) catch unreachable;
29+
eqlIgnoreWhitespace(src, out.items);
3730

38-
// std.debug.assert(std.mem.eql(u8, out.items, full_circle.items));
31+
var full_circle = std.ArrayList(u8).init(gpa);
32+
defer full_circle.deinit();
33+
html_ast.render(out.items, full_circle.writer()) catch unreachable;
3934

40-
// const super_ast = super.Ast.init(gpa, html_ast, src) catch unreachable;
41-
// defer super_ast.deinit(gpa);
42-
// }
35+
std.debug.assert(std.mem.eql(u8, out.items, full_circle.items));
4336

37+
const super_ast = super.Ast.init(gpa, html_ast, src) catch unreachable;
38+
defer super_ast.deinit(gpa);
39+
}
4440
}
4541

4642
export fn zig_fuzz_test_astgen(buf: [*]u8, len: isize) void {
43+
var gpa_impl: std.heap.GeneralPurposeAllocator(.{}) = .{};
4744
const gpa = gpa_impl.allocator();
4845
const astgen_src = buf[0..@intCast(len)];
4946

src/html/Ast.zig

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,7 @@ pub fn render(ast: Ast, src: []const u8, w: anytype) !void {
695695

696696
log.debug("retokenizing: '{s}'", .{current.open.slice(src)});
697697
const name = tt.next(src[0..current.open.end]).?.tag_name.slice(src);
698+
log.debug("tag name: '{s}'", .{name});
698699
if (std.ascii.eqlIgnoreCase("pre", name)) {
699700
pre += 1;
700701
}
@@ -707,11 +708,26 @@ pub fn render(ast: Ast, src: []const u8, w: anytype) !void {
707708
src[current.open.end - 2],
708709
);
709710

711+
// if (std.mem.eql(u8, name, "path")) @breakpoint();
712+
710713
while (tt.next(src[0..current.open.end])) |maybe_attr| {
711714
log.debug("tt: {s}", .{@tagName(maybe_attr)});
712715
log.debug("tt: {any}", .{maybe_attr});
713716
switch (maybe_attr) {
714-
else => unreachable,
717+
else => {
718+
log.debug(
719+
"got unexpected {any}",
720+
.{maybe_attr},
721+
);
722+
unreachable;
723+
},
724+
.tag_name => {
725+
log.debug(
726+
"got unexpected tag_name '{s}'",
727+
.{maybe_attr.tag_name.slice(src)},
728+
);
729+
unreachable;
730+
},
715731
.tag => break,
716732
.attr => |attr| {
717733
if (vertical) {
@@ -1122,6 +1138,30 @@ test "arrow span" {
11221138
try std.testing.expectFmt(expected, "{s}", .{ast.formatter(case)});
11231139
}
11241140

1141+
test "self-closing tag complex example" {
1142+
const case =
1143+
\\extend template="base.html"/>
1144+
\\
1145+
\\<div id="content">
1146+
\\<svg viewBox="0 0 24 24">
1147+
\\<path d="M14.4,6H20V16H13L12.6,14H7V21H5V4H14L14.4,6M14,14H16V12H18V10H16V8H14V10L13,8V6H11V8H9V6H7V8H9V10H7V12H9V10H11V12H13V10L14,12V14M11,10V8H13V10H11M14,10H16V12H14V10Z" />
1148+
\\</svg>
1149+
\\</div>
1150+
;
1151+
const expected =
1152+
\\extend template="base.html"/>
1153+
\\<div id="content">
1154+
\\ <svg viewBox="0 0 24 24">
1155+
\\ <path d="M14.4,6H20V16H13L12.6,14H7V21H5V4H14L14.4,6M14,14H16V12H18V10H16V8H14V10L13,8V6H11V8H9V6H7V8H9V10H7V12H9V10H11V12H13V10L14,12V14M11,10V8H13V10H11M14,10H16V12H14V10Z"/>
1156+
\\ </svg>
1157+
\\</div>
1158+
;
1159+
const ast = try Ast.init(std.testing.allocator, case, .html);
1160+
defer ast.deinit(std.testing.allocator);
1161+
1162+
try std.testing.expectFmt(expected, "{s}", .{ast.formatter(case)});
1163+
}
1164+
11251165
pub const Cursor = struct {
11261166
ast: Ast,
11271167
idx: u32,

src/html/Tokenizer.zig

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2248,11 +2248,14 @@ fn next2(self: *Tokenizer, src: []const u8) ?struct {
22482248

22492249
// Anything else
22502250
// Start a new attribute in the current tag token. Set that attribute name and value to the empty string. Reconsume in the attribute name state.
2251-
else => self.state = .{
2252-
.attribute_name = .{
2253-
.tag = state,
2254-
.name_start = self.idx - 1,
2255-
},
2251+
else => {
2252+
self.idx -= 1;
2253+
self.state = .{
2254+
.attribute_name = .{
2255+
.tag = state,
2256+
.name_start = self.idx,
2257+
},
2258+
};
22562259
},
22572260
}
22582261
},
@@ -2864,7 +2867,7 @@ fn next2(self: *Tokenizer, src: []const u8) ?struct {
28642867
else => unreachable,
28652868
};
28662869

2867-
if (self.return_attrs) {
2870+
if (self.return_attrs and tag.attr_count == 0) {
28682871
const deferred: Token = if (tag.kind == .end_self) .{
28692872
.parse_error = .{
28702873
.tag = .end_tag_with_trailing_solidus,

0 commit comments

Comments
 (0)