Commit 0b3508073c

Carter Snook <cartersnook04@gmail.com>
2024-06-14 22:41:43
std: fix buffer overflows from improper WTF encoding
Closes #20288
1 parent 5692979
Changed files (3)
lib
lib/std/fs/Dir.zig
@@ -1786,6 +1786,9 @@ pub fn symLink(
         // when converting to an NT namespaced path. CreateSymbolicLink in
         // symLinkW will handle the necessary conversion.
         var target_path_w: windows.PathSpace = undefined;
+        if (try std.unicode.checkWtf8ToWtf16LeOverflow(target_path, &target_path_w.data)) {
+            return error.NameTooLong;
+        }
         target_path_w.len = try std.unicode.wtf8ToWtf16Le(&target_path_w.data, target_path);
         target_path_w.data[target_path_w.len] = 0;
         // However, we need to canonicalize any path separators to `\`, since if
lib/std/posix/test.zig
@@ -22,6 +22,15 @@ const tmpDir = std.testing.tmpDir;
 const Dir = std.fs.Dir;
 const ArenaAllocator = std.heap.ArenaAllocator;
 
+// https://github.com/ziglang/zig/issues/20288
+test "WTF-8 to WTF-16 conversion buffer overflows" {
+    if (native_os != .windows) return error.SkipZigTest;
+
+    const input_wtf8 = "\u{10FFFF}" ** 16385;
+    try expectError(error.NameTooLong, posix.chdir(input_wtf8));
+    try expectError(error.NameTooLong, posix.chdirZ(input_wtf8));
+}
+
 test "chdir smoke test" {
     if (native_os == .wasi) return error.SkipZigTest;
 
lib/std/posix.zig
@@ -3111,8 +3111,10 @@ pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
         @compileError("WASI does not support os.chdir");
     } else if (native_os == .windows) {
         var wtf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined;
-        const len = try std.unicode.wtf8ToWtf16Le(wtf16_dir_path[0..], dir_path);
-        if (len > wtf16_dir_path.len) return error.NameTooLong;
+        if (try std.unicode.checkWtf8ToWtf16LeOverflow(dir_path, &wtf16_dir_path)) {
+            return error.NameTooLong;
+        }
+        const len = try std.unicode.wtf8ToWtf16Le(&wtf16_dir_path, dir_path);
         return chdirW(wtf16_dir_path[0..len]);
     } else {
         const dir_path_c = try toPosixPath(dir_path);
@@ -3126,9 +3128,12 @@ pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
 /// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
 pub fn chdirZ(dir_path: [*:0]const u8) ChangeCurDirError!void {
     if (native_os == .windows) {
+        const dir_path_span = mem.span(dir_path);
         var wtf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined;
-        const len = try std.unicode.wtf8ToWtf16Le(wtf16_dir_path[0..], mem.span(dir_path));
-        if (len > wtf16_dir_path.len) return error.NameTooLong;
+        if (try std.unicode.checkWtf8ToWtf16LeOverflow(dir_path_span, &wtf16_dir_path)) {
+            return error.NameTooLong;
+        }
+        const len = try std.unicode.wtf8ToWtf16Le(&wtf16_dir_path, dir_path_span);
         return chdirW(wtf16_dir_path[0..len]);
     } else if (native_os == .wasi and !builtin.link_libc) {
         return chdir(mem.span(dir_path));