authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2026-01-12 20:28:03-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-13 20:45:38+01:00
log2301f2ecdf3a4f823c1a66482af2574738ffb554
treeac05f814a34d07aa704f04cc7a5e34a6fd38c320
parent47cc233f220bcfb7b7183692861651423d965117

NtSetInformationFile: remove const from FileInformation buffer param

When targeting x86-windows, this parameter referring to read-only memory can result in an ACCESS_VIOLATION error, and this has been seen when using FILE_DISPOSITION_INFORMATION_EX. It's unclear how exactly this ACCESS_VIOLATION is occurring, though, as the memory does not actually change before/after the call. Closes https://codeberg.org/ziglang/zig/issues/30802

3 files changed, 16 insertions(+), 13 deletions(-)

lib/std/Io/Threaded.zig+6-6
...@@ -5550,7 +5550,7 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov...@@ -5550,7 +5550,7 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov
5550 // FileDispositionInformation if the return value lets us know that some aspect of it is not supported.5550 // FileDispositionInformation if the return value lets us know that some aspect of it is not supported.
5551 const rc = rc: {5551 const rc = rc: {
5552 // Deletion with posix semantics if the filesystem supports it.5552 // Deletion with posix semantics if the filesystem supports it.
5553 const info: w.FILE.DISPOSITION.INFORMATION.EX = .{ .Flags = .{5553 var info: w.FILE.DISPOSITION.INFORMATION.EX = .{ .Flags = .{
5554 .DELETE = true,5554 .DELETE = true,
5555 .POSIX_SEMANTICS = true,5555 .POSIX_SEMANTICS = true,
5556 .IGNORE_READONLY_ATTRIBUTE = true,5556 .IGNORE_READONLY_ATTRIBUTE = true,
...@@ -5585,7 +5585,7 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov...@@ -5585,7 +5585,7 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov
55855585
5586 // Deletion with file pending semantics, which requires waiting or moving5586 // Deletion with file pending semantics, which requires waiting or moving
5587 // files to get them removed (from here).5587 // files to get them removed (from here).
5588 const file_dispo: w.FILE.DISPOSITION.INFORMATION = .{5588 var file_dispo: w.FILE.DISPOSITION.INFORMATION = .{
5589 .DeleteFile = w.TRUE,5589 .DeleteFile = w.TRUE,
5590 };5590 };
55915591
...@@ -5801,7 +5801,7 @@ fn dirRenameWindowsInner(...@@ -5801,7 +5801,7 @@ fn dirRenameWindowsInner(
5801 // The strategy here is just to try using FileRenameInformationEx and fall back to5801 // The strategy here is just to try using FileRenameInformationEx and fall back to
5802 // FileRenameInformation if the return value lets us know that some aspect of it is not supported.5802 // FileRenameInformation if the return value lets us know that some aspect of it is not supported.
5803 const need_fallback = need_fallback: {5803 const need_fallback = need_fallback: {
5804 const rename_info: w.FILE.RENAME_INFORMATION = .init(.{5804 var rename_info: w.FILE.RENAME_INFORMATION = .init(.{
5805 .Flags = .{5805 .Flags = .{
5806 .REPLACE_IF_EXISTS = replace_if_exists,5806 .REPLACE_IF_EXISTS = replace_if_exists,
5807 .POSIX_SEMANTICS = true,5807 .POSIX_SEMANTICS = true,
...@@ -5834,7 +5834,7 @@ fn dirRenameWindowsInner(...@@ -5834,7 +5834,7 @@ fn dirRenameWindowsInner(
5834 };5834 };
58355835
5836 if (need_fallback) {5836 if (need_fallback) {
5837 const rename_info: w.FILE.RENAME_INFORMATION = .init(.{5837 var rename_info: w.FILE.RENAME_INFORMATION = .init(.{
5838 .Flags = .{ .REPLACE_IF_EXISTS = replace_if_exists },5838 .Flags = .{ .REPLACE_IF_EXISTS = replace_if_exists },
5839 .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir.handle,5839 .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir.handle,
5840 .FileName = new_path_w,5840 .FileName = new_path_w,
...@@ -7104,7 +7104,7 @@ fn fileSetLength(userdata: ?*anyopaque, file: File, length: u64) File.SetLengthE...@@ -7104,7 +7104,7 @@ fn fileSetLength(userdata: ?*anyopaque, file: File, length: u64) File.SetLengthE
71047104
7105 if (is_windows) {7105 if (is_windows) {
7106 var io_status_block: windows.IO_STATUS_BLOCK = undefined;7106 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
7107 const eof_info: windows.FILE.END_OF_FILE_INFORMATION = .{7107 var eof_info: windows.FILE.END_OF_FILE_INFORMATION = .{
7108 .EndOfFile = signed_len,7108 .EndOfFile = signed_len,
7109 };7109 };
71107110
...@@ -7195,7 +7195,7 @@ fn fileSetPermissions(userdata: ?*anyopaque, file: File, permissions: File.Permi...@@ -7195,7 +7195,7 @@ fn fileSetPermissions(userdata: ?*anyopaque, file: File, permissions: File.Permi
7195 switch (native_os) {7195 switch (native_os) {
7196 .windows => {7196 .windows => {
7197 var io_status_block: windows.IO_STATUS_BLOCK = undefined;7197 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
7198 const info: windows.FILE.BASIC_INFORMATION = .{7198 var info: windows.FILE.BASIC_INFORMATION = .{
7199 .CreationTime = 0,7199 .CreationTime = 0,
7200 .LastAccessTime = 0,7200 .LastAccessTime = 0,
7201 .LastWriteTime = 0,7201 .LastWriteTime = 0,
lib/std/os/windows.zig+6-6
...@@ -265,8 +265,8 @@ pub const FILE = struct {...@@ -265,8 +265,8 @@ pub const FILE = struct {
265 return ri.FileName[0..@divExact(ri.FileNameLength, @sizeOf(WCHAR))];265 return ri.FileName[0..@divExact(ri.FileNameLength, @sizeOf(WCHAR))];
266 }266 }
267267
268 pub fn toBuffer(fri: *const RENAME_INFORMATION) []const u8 {268 pub fn toBuffer(fri: *RENAME_INFORMATION) []u8 {
269 const start: [*]const u8 = @ptrCast(fri);269 const start: [*]u8 = @ptrCast(fri);
270 // The ABI size of the documented struct is 24 bytes, and attempting to use any size270 // The ABI size of the documented struct is 24 bytes, and attempting to use any size
271 // less than that will trigger INFO_LENGTH_MISMATCH, so enforce a minimum in cases where,271 // less than that will trigger INFO_LENGTH_MISMATCH, so enforce a minimum in cases where,
272 // for example, FileNameLength is 1 so only 22 bytes are technically needed.272 // for example, FileNameLength is 1 so only 22 bytes are technically needed.
...@@ -3134,7 +3134,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil...@@ -3134,7 +3134,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
3134 // FileDispositionInformation if the return value lets us know that some aspect of it is not supported.3134 // FileDispositionInformation if the return value lets us know that some aspect of it is not supported.
3135 const need_fallback = need_fallback: {3135 const need_fallback = need_fallback: {
3136 // Deletion with posix semantics if the filesystem supports it.3136 // Deletion with posix semantics if the filesystem supports it.
3137 const info: FILE.DISPOSITION.INFORMATION.EX = .{ .Flags = .{3137 var info: FILE.DISPOSITION.INFORMATION.EX = .{ .Flags = .{
3138 .DELETE = true,3138 .DELETE = true,
3139 .POSIX_SEMANTICS = true,3139 .POSIX_SEMANTICS = true,
3140 .IGNORE_READONLY_ATTRIBUTE = true,3140 .IGNORE_READONLY_ATTRIBUTE = true,
...@@ -3163,7 +3163,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil...@@ -3163,7 +3163,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
3163 if (need_fallback) {3163 if (need_fallback) {
3164 // Deletion with file pending semantics, which requires waiting or moving3164 // Deletion with file pending semantics, which requires waiting or moving
3165 // files to get them removed (from here).3165 // files to get them removed (from here).
3166 const file_dispo: FILE.DISPOSITION.INFORMATION = .{3166 var file_dispo: FILE.DISPOSITION.INFORMATION = .{
3167 .DeleteFile = TRUE,3167 .DeleteFile = TRUE,
3168 };3168 };
3169 rc = ntdll.NtSetInformationFile(3169 rc = ntdll.NtSetInformationFile(
...@@ -3242,7 +3242,7 @@ pub fn RenameFile(...@@ -3242,7 +3242,7 @@ pub fn RenameFile(
3242 // The strategy here is just to try using FileRenameInformationEx and fall back to3242 // The strategy here is just to try using FileRenameInformationEx and fall back to
3243 // FileRenameInformation if the return value lets us know that some aspect of it is not supported.3243 // FileRenameInformation if the return value lets us know that some aspect of it is not supported.
3244 const need_fallback = need_fallback: {3244 const need_fallback = need_fallback: {
3245 const rename_info: FILE.RENAME_INFORMATION = .init(.{3245 var rename_info: FILE.RENAME_INFORMATION = .init(.{
3246 .Flags = .{3246 .Flags = .{
3247 .REPLACE_IF_EXISTS = replace_if_exists,3247 .REPLACE_IF_EXISTS = replace_if_exists,
3248 .POSIX_SEMANTICS = true,3248 .POSIX_SEMANTICS = true,
...@@ -3275,7 +3275,7 @@ pub fn RenameFile(...@@ -3275,7 +3275,7 @@ pub fn RenameFile(
3275 };3275 };
32763276
3277 if (need_fallback) {3277 if (need_fallback) {
3278 const rename_info: FILE.RENAME_INFORMATION = .init(.{3278 var rename_info: FILE.RENAME_INFORMATION = .init(.{
3279 .Flags = .{ .REPLACE_IF_EXISTS = replace_if_exists },3279 .Flags = .{ .REPLACE_IF_EXISTS = replace_if_exists },
3280 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir_fd,3280 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir_fd,
3281 .FileName = new_path_w,3281 .FileName = new_path_w,
lib/std/os/windows/ntdll.zig+4-1
...@@ -203,7 +203,10 @@ pub extern "ntdll" fn NtReadFile(...@@ -203,7 +203,10 @@ pub extern "ntdll" fn NtReadFile(
203pub extern "ntdll" fn NtSetInformationFile(203pub extern "ntdll" fn NtSetInformationFile(
204 FileHandle: HANDLE,204 FileHandle: HANDLE,
205 IoStatusBlock: *IO_STATUS_BLOCK,205 IoStatusBlock: *IO_STATUS_BLOCK,
206 FileInformation: *const anyopaque,206 /// This can't be const as providing read-only memory could result in ACCESS_VIOLATION
207 /// in certain scenarios. This has been seen when using FILE_DISPOSITION_INFORMATION_EX
208 /// and targeting x86-windows.
209 FileInformation: *anyopaque,
207 Length: ULONG,210 Length: ULONG,
208 FileInformationClass: FILE.INFORMATION_CLASS,211 FileInformationClass: FILE.INFORMATION_CLASS,
209) callconv(.winapi) NTSTATUS;212) callconv(.winapi) NTSTATUS;