From 1253739fcae5e66b5e7c90080058a7585e245d49 Mon Sep 17 00:00:00 2001 From: mochalins <117967760+mochalins@users.noreply.github.com> Date: Fri, 17 Jan 2025 16:58:36 +0900 Subject: [PATCH] fix: Windows Poll UB --- src/backend/windows.zig | 55 +++++++++++++++++++++++++---------------- src/serialport.zig | 12 +++++---- 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/src/backend/windows.zig b/src/backend/windows.zig index 1963202..e673dca 100644 --- a/src/backend/windows.zig +++ b/src/backend/windows.zig @@ -22,6 +22,12 @@ pub const BaudRate = enum(windows.DWORD) { _, }; +/// Reused structures necessary between non-blocking poll calls. +pub const PollContinuation = struct { + events: EventMask, + overlapped: windows.OVERLAPPED, +}; + pub const ReadError = windows.ReadFileError || windows.OpenError || @@ -119,25 +125,25 @@ pub fn flush(port: std.fs.File, options: serialport.FlushOptions) !void { } } -pub fn poll(port: std.fs.File, overlapped_: *?windows.OVERLAPPED) !bool { +pub fn poll(port: std.fs.File, continuation: *?PollContinuation) !bool { var comstat: ComStat = undefined; if (ClearCommError(port.handle, null, &comstat) == 0) { return windows.unexpectedError(windows.GetLastError()); } if (comstat.cbInQue > 0) return true; - var events: EventMask = undefined; - if (overlapped_.*) |*overlapped| { + if (continuation.*) |*cont| { if (windows.GetOverlappedResult( port.handle, - overlapped, + &cont.overlapped, false, ) catch |e| switch (e) { error.WouldBlock => return false, else => return e, } != 0) { - overlapped_.* = null; - return true; + const was_rx = cont.events.RXCHAR; + cont.* = null; + return was_rx; } else { switch (windows.GetLastError()) { windows.Win32Error.IO_PENDING => return false, @@ -145,29 +151,36 @@ pub fn poll(port: std.fs.File, overlapped_: *?windows.OVERLAPPED) !bool { } } } else { - overlapped_.* = .{ - .Internal = 0, - .InternalHigh = 0, - .DUMMYUNIONNAME = .{ - .DUMMYSTRUCTNAME = .{ - .Offset = 0, - .OffsetHigh = 0, + continuation.* = .{ + .events = undefined, + .overlapped = .{ + .Internal = 0, + .InternalHigh = 0, + .DUMMYUNIONNAME = .{ + .DUMMYSTRUCTNAME = .{ + .Offset = 0, + .OffsetHigh = 0, + }, }, + .hEvent = try windows.CreateEventEx( + null, + "", + windows.CREATE_EVENT_MANUAL_RESET, + windows.EVENT_ALL_ACCESS, + ), }, - .hEvent = try windows.CreateEventEx( - null, - "", - windows.CREATE_EVENT_MANUAL_RESET, - windows.EVENT_ALL_ACCESS, - ), }; - if (WaitCommEvent(port.handle, &events, &overlapped_.*.?) == 0) { + if (WaitCommEvent( + port.handle, + &continuation.?.events, + &continuation.?.overlapped, + ) == 0) { switch (windows.GetLastError()) { windows.Win32Error.IO_PENDING => return false, else => |e| return windows.unexpectedError(e), } } - return events.RXCHAR; + return continuation.?.events.RXCHAR; } } diff --git a/src/serialport.zig b/src/serialport.zig index 517cbcb..38b008e 100644 --- a/src/serialport.zig +++ b/src/serialport.zig @@ -29,7 +29,7 @@ const PortImpl = switch (builtin.target.os.tag) { }, .windows => struct { file: std.fs.File, - poll_overlapped: ?std.os.windows.OVERLAPPED = null, + poll_continuation: ?windows.PollContinuation = null, }, else => @compileError("unsupported OS"), }; @@ -109,10 +109,12 @@ pub const Port = struct { pub fn poll(self: *@This()) !bool { switch (comptime builtin.target.os.tag) { .linux, .macos => return backend.poll(self._impl.file), - .windows => return windows.poll( - self._impl.file, - &self._impl.poll_overlapped, - ), + .windows => { + return windows.poll( + self._impl.file, + &self._impl.poll_continuation, + ); + }, else => @compileError("unsupported OS"), } }