Commit a463dc7d6c

Veikka Tuominen <git@vexu.eu>
2022-07-25 20:25:09
AstGen: disable null bytes and empty stings in some places
Namely: * test names * identifiers * library names * import strings
1 parent 2f54129
Changed files (5)
lib
std
zig
system
src
test
cases
standalone
issue_9812
lib/std/zig/system/darwin.zig
@@ -87,6 +87,6 @@ pub const DarwinSDK = struct {
     }
 };
 
-test "" {
+test {
     _ = macos;
 }
src/AstGen.zig
@@ -3497,6 +3497,12 @@ fn fnDecl(
 
     const lib_name: u32 = if (fn_proto.lib_name) |lib_name_token| blk: {
         const lib_name_str = try astgen.strLitAsString(lib_name_token);
+        const lib_name_slice = astgen.string_bytes.items[lib_name_str.index..][0..lib_name_str.len];
+        if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) {
+            return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{});
+        } else if (lib_name_str.len == 0) {
+            return astgen.failTok(lib_name_token, "library name cannot be empty", .{});
+        }
         break :blk lib_name_str.index;
     } else 0;
 
@@ -3750,6 +3756,12 @@ fn globalVarDecl(
 
     const lib_name: u32 = if (var_decl.lib_name) |lib_name_token| blk: {
         const lib_name_str = try astgen.strLitAsString(lib_name_token);
+        const lib_name_slice = astgen.string_bytes.items[lib_name_str.index..][0..lib_name_str.len];
+        if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) {
+            return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{});
+        } else if (lib_name_str.len == 0) {
+            return astgen.failTok(lib_name_token, "library name cannot be empty", .{});
+        }
         break :blk lib_name_str.index;
     } else 0;
 
@@ -7239,6 +7251,12 @@ fn builtinCall(
             }
             const str_lit_token = main_tokens[operand_node];
             const str = try astgen.strLitAsString(str_lit_token);
+            const str_slice = astgen.string_bytes.items[str.index..][0..str.len];
+            if (mem.indexOfScalar(u8, str_slice, 0) != null) {
+                return astgen.failTok(str_lit_token, "import path cannot contain null bytes", .{});
+            } else if (str.len == 0) {
+                return astgen.failTok(str_lit_token, "import path cannot be empty", .{});
+            }
             const result = try gz.addStrTok(.import, str.index, str_lit_token);
             const gop = try astgen.imports.getOrPut(astgen.gpa, str.index);
             if (!gop.found_existing) {
@@ -9260,6 +9278,11 @@ fn identifierTokenString(astgen: *AstGen, token: Ast.TokenIndex) InnerError![]co
     var buf: ArrayListUnmanaged(u8) = .{};
     defer buf.deinit(astgen.gpa);
     try astgen.parseStrLit(token, &buf, ident_name, 1);
+    if (mem.indexOfScalar(u8, buf.items, 0) != null) {
+        return astgen.failTok(token, "identifier cannot contain null bytes", .{});
+    } else if (buf.items.len == 0) {
+        return astgen.failTok(token, "identifier cannot be empty", .{});
+    }
     const duped = try astgen.arena.dupe(u8, buf.items);
     return duped;
 }
@@ -9279,7 +9302,14 @@ fn appendIdentStr(
     if (!mem.startsWith(u8, ident_name, "@")) {
         return buf.appendSlice(astgen.gpa, ident_name);
     } else {
-        return astgen.parseStrLit(token, buf, ident_name, 1);
+        const start = buf.items.len;
+        try astgen.parseStrLit(token, buf, ident_name, 1);
+        const slice = buf.items[start..];
+        if (mem.indexOfScalar(u8, slice, 0) != null) {
+            return astgen.failTok(token, "identifier cannot contain null bytes", .{});
+        } else if (slice.len == 0) {
+            return astgen.failTok(token, "identifier cannot be empty", .{});
+        }
     }
 }
 
@@ -9723,6 +9753,12 @@ fn testNameString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !u32 {
     const token_bytes = astgen.tree.tokenSlice(str_lit_token);
     try string_bytes.append(gpa, 0); // Indicates this is a test.
     try astgen.parseStrLit(str_lit_token, string_bytes, token_bytes, 0);
+    const slice = string_bytes.items[str_index + 1 ..];
+    if (mem.indexOfScalar(u8, slice, 0) != null) {
+        return astgen.failTok(str_lit_token, "test name cannot contain null bytes", .{});
+    } else if (slice.len == 0) {
+        return astgen.failTok(str_lit_token, "empty test name must be omitted", .{});
+    }
     try string_bytes.append(gpa, 0);
     return str_index;
 }
test/cases/compile_errors/stage1/obj/wrong_panic_signature_runtime_function.zig
@@ -1,4 +1,4 @@
-test "" {}
+test {}
 
 pub fn panic() void {}
 
test/cases/compile_errors/invalid_identifiers.zig
@@ -0,0 +1,33 @@
+extern "" var a: u32;
+extern "" fn b() void;
+
+extern "\x00" var c: u32;
+extern "\x00" fn d() void;
+
+test "" {}
+test "\x00" {}
+
+const e = @import("");
+const f = @import("\x00");
+
+comptime {
+    const @"" = undefined;
+}
+comptime {
+    const @"\x00" = undefined;
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :1:8: error: library name cannot be empty
+// :2:8: error: library name cannot be empty
+// :4:8: error: library name cannot contain null bytes
+// :5:8: error: library name cannot contain null bytes
+// :7:6: error: empty test name must be omitted
+// :8:6: error: test name cannot contain null bytes
+// :10:19: error: import path cannot be empty
+// :11:19: error: import path cannot contain null bytes
+// :14:11: error: identifier cannot be empty
+// :17:11: error: identifier cannot contain null bytes
test/standalone/issue_9812/main.zig
@@ -14,7 +14,7 @@ const Error = error{
     InvalidCmdLine,
 };
 
-test "" {
+test {
     const allocator = std.heap.c_allocator;
 
     const args = try std.process.argsAlloc(allocator);