ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
34.85k stars 2.55k forks source link

Windows: Prefer `ntdll` named pipe APIs over `kernel32` #19037

Open The-King-of-Toasters opened 8 months ago

The-King-of-Toasters commented 8 months ago

CreatePipe and CreateNamedPipe are used by child_process to... create anonymous and named pipes for redirecting i/o. From my research, these are abstractions around NtCreateNamedPipeFile and NtOpenFile for the writing end. As per #1840, we should prefer using these APIs over kernel32 ones.

CreatePipe

Here is wine's implementation. Using NtTrace we see the same calls being made, just out of order:

Example Program ```zig var in: HANDLE = undefined; var out: HANDLE = undefined; const res = CreatePipe(&in, &out, null, 1024); if (res == 0) std.debug.panic("huh, {}", .{kernel32.GetLastError()}); defer { std.os.close(in); std.os.close(out); } ```
NtOpenFile( FileHandle=0x7ff8c4c2b114 [0x00841f0fc32ecdcc], DesiredAccess=READ_CONTROL|WRITE_OWNER|SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE|0x800258, ObjectAttributes="\Device\NamedPipe\", IoStatusBlock=0x153fcb0 [0/1], ShareAccess=3, OpenOptions=0x20 ) => 0
NtCreateNamedPipeFile( NamedPipeHandle=0x7ff8c4c2c1d4 [0x00841f0fc32ecdcc], DesiredAccess=READ_CONTROL|WRITE_OWNER|SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE|0x800434, ObjectAttributes=0xf4:"", IoStatusBlock=0x153fcb0 [0/2], ShareAccess=3, CreateDisposition=2, CreateOptions=0x20, MessageType=false, MessageRead=false, NonBlocking=false, MaxInstances=1, InBufferSize=0x400, OutBufferSize=0x400, Timeout=0x153fca0 [-1.2e+09] ) => 0
NtOpenFile( FileHandle=0x7ff8c4c2b114 [0x00841f0fc32ecdcc], DesiredAccess=READ_CONTROL|WRITE_OWNER|SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE|0x800258, ObjectAttributes=0xf8:"", IoStatusBlock=0x153fcb0 [0/1], ShareAccess=3, OpenOptions=0x60 ) => 0
NtClose( Handle=0x00007ff8c4c2ac94 ) => 0
NtClose( Handle=0x00007ff8c4c2ac94 ) => 0

Some improvements we could make:

Not that I cannot find these pipes under the pipe device directory (ls \\.\pipe\ in powershell), unlike proper named pipes.

CreateNamedPipe

Again, here is Wine's impl and a trace log.

Sample ```zig const file = std.unicode.utf8ToUtf16LeStringLiteral("\\\\.\\pipe\\my_pipe"); const read_handle = windows.kernel32.CreateNamedPipeW( file.ptr, windows.PIPE_ACCESS_INBOUND | windows.FILE_FLAG_OVERLAPPED, windows.PIPE_TYPE_BYTE, 1, 4096, 4096, 0, null, ); defer std.os.close(read_handle); ```
NtCreateNamedPipeFile( NamedPipeHandle=0x7ff8c4c2c1d4 [0x00841f0fc32ecdcc], DesiredAccess=READ_CONTROL|WRITE_OWNER|SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE|0x800434, ObjectAttributes="\??\pipe\my_pipe", IoStatusBlock=0x14cfc78 [0/2], ShareAccess=2, CreateDisposition=3, CreateOptions=0, MessageType=false, MessageRead=false, NonBlocking=false, MaxInstances=1, InBufferSize=0x1000, OutBufferSize=0x1000, Timeout=0x14cfc20 [-500000] ) => 0
NtClose( Handle=0x00007ff8c4c2ac94 ) => 0

Pretty close to the real thing.

The-King-of-Toasters commented 8 months ago

I have a prototype of a CreatePipe impl that uses the Native API, works great when single-threaded. Unfortunately everything fails hard when using it via the zig build runner. I'll put it up on a draft PR and hopefully I can crack this case; my windbg-foo is failing me.

Edit: I'm silly and passed the wrong flags.