zig icon indicating copy to clipboard operation
zig copied to clipboard

flaky fs test: `Dir.rename directories` on aarch64-windows

Open squeek502 opened this issue 2 years ago • 3 comments

Zig Version

0.12.0-dev.309+402468b21

Steps to Reproduce and Observed Behavior

Occasionally, the rename directories test in fs/test.zig will fail on the aarch64-windows CI with AccessDenied when trying to rename a directory (example CI run). The flaky test was introduced in https://github.com/ziglang/zig/pull/16847, some discussion of this bug can be found there too.

run test std-aarch64-windows-gnu-Debug-libc: error: 'test.Dir.rename directories' failed: AccessDenied, path type: unc
C:\actions-runner1\_work\zig\zig\lib\std\os.zig:2709:27: 0x7ff65b9fb87f in renameatW (test.exe.obj)
        .ACCESS_DENIED => return error.AccessDenied,
                          ^
C:\actions-runner1\_work\zig\zig\lib\std\os.zig:2531:9: 0x7ff65b7b05db in renameat (test.exe.obj)
        return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE);
        ^
C:\actions-runner1\_work\zig\zig\lib\std\fs.zig:1924:9: 0x7ff65b7c1b0b in rename (test.exe.obj)
        return os.renameat(self.fd, old_sub_path, self.fd, new_sub_path);
        ^
C:\actions-runner1\_work\zig\zig\lib\std\fs\test.zig:731:13: 0x7ff65b7c2687 in impl (test.exe.obj)
            try ctx.dir.rename(test_dir_renamed_path, test_dir_renamed_again_path);
            ^
C:\actions-runner1\_work\zig\zig\lib\std\fs\test.zig:121:13: 0x7ff65b7c2b87 in testWithAllSupportedPathTypes__anon_96449 (test.exe.obj)
            return err;
            ^
C:\actions-runner1\_work\zig\zig\lib\std\fs\test.zig:712:5: 0x7ff65b7c2f4b in test.Dir.rename directories (test.exe.obj)
    try testWithAllSupportedPathTypes(struct {
    ^

This is reproducible on x86_64 Windows, too, but it has never failed in that CI environment.

Some necessary conditions for reproduction that I could find:

  • "Real-time Protection" of Windows Defender must be enabled on the system. It does not reproduce if "Real-time Protection" is turned off.
  • The path under test must be something like C:\<something non-standard>. That is, the error does not reproduce if the path is within e.g. C:\Users\<username>. There may be some other condition that is actually making the difference here, but I wasn't able to find it.
  • UNC paths must be used at some point. The bug does not reproduce if only drive-absolute paths are used.
  • At least one file must be in the directory being renamed. It does not reproduce if the directory being renamed is empty.

Reproduction code (from @jacobly0 here); it should fail on some iteration (but which iteration it fails on is not consistent):

const std = @import("std");

pub fn main() !void {
    const root = "\\_";
    const cwd = std.fs.cwd();

    try cwd.deleteTree("C:" ++ root);
    try cwd.makeDir("C:" ++ root);
    defer cwd.deleteTree("C:" ++ root) catch |err| {
        std.debug.print("cwd.deleteTree(\"C:\" ++ root) = {}\n", .{err});
    };

    for (0..1000) |i| {
        const a_path = switch (@as(u1, @truncate(i))) {
            0 => "C:" ++ root ++ "\\a",
            1 => "\\\\127.0.0.1\\C$" ++ root ++ "\\a",
        };

        try cwd.makeDir(a_path);

        {
            var dir = try cwd.openDir(a_path, .{});
            defer dir.close();

            const file = try dir.createFile("f", .{ .read = true });
            defer file.close();
        }

        const b_path = switch (@as(u1, @truncate(i))) {
            0 => "C:" ++ root ++ "\\b",
            1 => "\\\\127.0.0.1\\C$" ++ root ++ "\\b",
        };
        cwd.rename(a_path, b_path) catch |err| {
            std.debug.print("{}: cwd.rename(\"{}\", \"{}\") = {}\n", .{
                i,
                std.zig.fmtEscapes(a_path),
                std.zig.fmtEscapes(b_path),
                err,
            });
            var d = try cwd.openIterableDir("C:" ++ root, .{});
            defer d.close();
            var w = try d.walk(std.heap.page_allocator);
            defer w.deinit();
            while (try w.next()) |e| std.debug.print("{s}\n", .{e.path});
            return err;
        };

        {
            var dir = try cwd.openDir(b_path, .{});
            defer dir.close();

            try dir.deleteFile("f");
        }
        try cwd.deleteDir(b_path);
    }
}

Expected Behavior

No intermittent AccessDenied errors

squeek502 avatar Sep 13 '23 00:09 squeek502

Seem to have hit this in the Dir.rename files test here: https://github.com/ziglang/zig/actions/runs/6173023767/job/16763286854?pr=17143

I'm unable to reproduce this one, though. With all the conditions in the OP, I am still able to run that test case tens of thousands of times successfully.

Weirdly, too, the trace seems to point to the rename of the file when it's expecting FileNotFound:

C:\actions-runner\_work\zig\zig\lib\std\fs\test.zig:681:36: 0x7ff7b7bc485f in impl (test.exe.obj)
            try testing.expectError(error.FileNotFound, ctx.dir.rename(missing_file_path, something_else_path));

which makes very little sense to me. Even stranger, if that rename is returning AccessDenied then the test should be failing with TestUnexpectedError, not AccessDenied, so the stack trace might be wrong in this case?

squeek502 avatar Sep 14 '23 01:09 squeek502

Seems to also be flaky on x86_64-windows: https://github.com/ziglang/zig/actions/runs/8759547226/job/24042803455

mlugg avatar Apr 20 '24 00:04 mlugg

Never mind -- @jacobly0 let me know that this was just a configuration mistake on the CI runner. PR closed.

mlugg avatar Apr 20 '24 01:04 mlugg