commit 2301f2ecdf3a4f823c1a66482af2574738ffb554 (tree)
parent 47cc233f220bcfb7b7183692861651423d965117
Author: Ryan Liptak <squeek502@hotmail.com>
Date: Mon, 12 Jan 2026 20:28:03 -0800
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
Diffstat:
3 files changed, 16 insertions(+), 13 deletions(-)
diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig
@@ -5550,7 +5550,7 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov
// FileDispositionInformation if the return value lets us know that some aspect of it is not supported.
const rc = rc: {
// Deletion with posix semantics if the filesystem supports it.
- const info: w.FILE.DISPOSITION.INFORMATION.EX = .{ .Flags = .{
+ var info: w.FILE.DISPOSITION.INFORMATION.EX = .{ .Flags = .{
.DELETE = true,
.POSIX_SEMANTICS = true,
.IGNORE_READONLY_ATTRIBUTE = true,
@@ -5585,7 +5585,7 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov
// Deletion with file pending semantics, which requires waiting or moving
// files to get them removed (from here).
- const file_dispo: w.FILE.DISPOSITION.INFORMATION = .{
+ var file_dispo: w.FILE.DISPOSITION.INFORMATION = .{
.DeleteFile = w.TRUE,
};
@@ -5801,7 +5801,7 @@ fn dirRenameWindowsInner(
// The strategy here is just to try using FileRenameInformationEx and fall back to
// FileRenameInformation if the return value lets us know that some aspect of it is not supported.
const need_fallback = need_fallback: {
- const rename_info: w.FILE.RENAME_INFORMATION = .init(.{
+ var rename_info: w.FILE.RENAME_INFORMATION = .init(.{
.Flags = .{
.REPLACE_IF_EXISTS = replace_if_exists,
.POSIX_SEMANTICS = true,
@@ -5834,7 +5834,7 @@ fn dirRenameWindowsInner(
};
if (need_fallback) {
- const rename_info: w.FILE.RENAME_INFORMATION = .init(.{
+ var rename_info: w.FILE.RENAME_INFORMATION = .init(.{
.Flags = .{ .REPLACE_IF_EXISTS = replace_if_exists },
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir.handle,
.FileName = new_path_w,
@@ -7104,7 +7104,7 @@ fn fileSetLength(userdata: ?*anyopaque, file: File, length: u64) File.SetLengthE
if (is_windows) {
var io_status_block: windows.IO_STATUS_BLOCK = undefined;
- const eof_info: windows.FILE.END_OF_FILE_INFORMATION = .{
+ var eof_info: windows.FILE.END_OF_FILE_INFORMATION = .{
.EndOfFile = signed_len,
};
@@ -7195,7 +7195,7 @@ fn fileSetPermissions(userdata: ?*anyopaque, file: File, permissions: File.Permi
switch (native_os) {
.windows => {
var io_status_block: windows.IO_STATUS_BLOCK = undefined;
- const info: windows.FILE.BASIC_INFORMATION = .{
+ var info: windows.FILE.BASIC_INFORMATION = .{
.CreationTime = 0,
.LastAccessTime = 0,
.LastWriteTime = 0,
diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig
@@ -265,8 +265,8 @@ pub const FILE = struct {
return ri.FileName[0..@divExact(ri.FileNameLength, @sizeOf(WCHAR))];
}
- pub fn toBuffer(fri: *const RENAME_INFORMATION) []const u8 {
- const start: [*]const u8 = @ptrCast(fri);
+ pub fn toBuffer(fri: *RENAME_INFORMATION) []u8 {
+ const start: [*]u8 = @ptrCast(fri);
// The ABI size of the documented struct is 24 bytes, and attempting to use any size
// less than that will trigger INFO_LENGTH_MISMATCH, so enforce a minimum in cases where,
// 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
// FileDispositionInformation if the return value lets us know that some aspect of it is not supported.
const need_fallback = need_fallback: {
// Deletion with posix semantics if the filesystem supports it.
- const info: FILE.DISPOSITION.INFORMATION.EX = .{ .Flags = .{
+ var info: FILE.DISPOSITION.INFORMATION.EX = .{ .Flags = .{
.DELETE = true,
.POSIX_SEMANTICS = true,
.IGNORE_READONLY_ATTRIBUTE = true,
@@ -3163,7 +3163,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
if (need_fallback) {
// Deletion with file pending semantics, which requires waiting or moving
// files to get them removed (from here).
- const file_dispo: FILE.DISPOSITION.INFORMATION = .{
+ var file_dispo: FILE.DISPOSITION.INFORMATION = .{
.DeleteFile = TRUE,
};
rc = ntdll.NtSetInformationFile(
@@ -3242,7 +3242,7 @@ pub fn RenameFile(
// The strategy here is just to try using FileRenameInformationEx and fall back to
// FileRenameInformation if the return value lets us know that some aspect of it is not supported.
const need_fallback = need_fallback: {
- const rename_info: FILE.RENAME_INFORMATION = .init(.{
+ var rename_info: FILE.RENAME_INFORMATION = .init(.{
.Flags = .{
.REPLACE_IF_EXISTS = replace_if_exists,
.POSIX_SEMANTICS = true,
@@ -3275,7 +3275,7 @@ pub fn RenameFile(
};
if (need_fallback) {
- const rename_info: FILE.RENAME_INFORMATION = .init(.{
+ var rename_info: FILE.RENAME_INFORMATION = .init(.{
.Flags = .{ .REPLACE_IF_EXISTS = replace_if_exists },
.RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir_fd,
.FileName = new_path_w,
diff --git a/lib/std/os/windows/ntdll.zig b/lib/std/os/windows/ntdll.zig
@@ -203,7 +203,10 @@ pub extern "ntdll" fn NtReadFile(
pub extern "ntdll" fn NtSetInformationFile(
FileHandle: HANDLE,
IoStatusBlock: *IO_STATUS_BLOCK,
- FileInformation: *const anyopaque,
+ /// This can't be const as providing read-only memory could result in ACCESS_VIOLATION
+ /// in certain scenarios. This has been seen when using FILE_DISPOSITION_INFORMATION_EX
+ /// and targeting x86-windows.
+ FileInformation: *anyopaque,
Length: ULONG,
FileInformationClass: FILE.INFORMATION_CLASS,
) callconv(.winapi) NTSTATUS;