Fix pipe reads; add tests for NtReadFile & pipes

This commit is contained in:
2025-10-05 23:24:50 -06:00
parent 85cf4a74c7
commit 01ed50c4b4
10 changed files with 304 additions and 31 deletions

View File

@@ -29,7 +29,7 @@ int main(void) {
}
SetLastError(0);
void *inPlace = HeapReAlloc(processHeap, HEAP_REALLOC_IN_PLACE_ONLY, grown, 128);
void *inPlace = HeapReAlloc(processHeap, HEAP_REALLOC_IN_PLACE_ONLY, grown, 2048);
TEST_CHECK(inPlace == NULL);
TEST_CHECK_EQ(ERROR_NOT_ENOUGH_MEMORY, GetLastError());

88
test/test_ntreadfile.c Normal file
View File

@@ -0,0 +1,88 @@
#include "test_assert.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winternl.h>
#include <string.h>
#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#endif
#ifndef STATUS_END_OF_FILE
#define STATUS_END_OF_FILE ((NTSTATUS)0xC0000011L)
#endif
static const char *kTempFileName = "ntreadfile_fixture.tmp";
typedef NTSTATUS(NTAPI *NtReadFile_t)(HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID, PIO_STATUS_BLOCK, PVOID, ULONG,
PLARGE_INTEGER, PULONG);
static NtReadFile_t load_ntreadfile(void) {
HMODULE mod = GetModuleHandleW(L"ntdll.dll");
if (!mod) {
mod = LoadLibraryW(L"ntdll.dll");
}
TEST_CHECK(mod != NULL);
FARPROC proc = GetProcAddress(mod, "NtReadFile");
TEST_CHECK(proc != NULL);
NtReadFile_t fn = NULL;
TEST_CHECK(sizeof(fn) == sizeof(proc));
memcpy(&fn, &proc, sizeof(fn));
return fn;
}
static void write_fixture(HANDLE file, const char *data, DWORD length) {
DWORD written = 0;
TEST_CHECK(WriteFile(file, data, length, &written, NULL));
TEST_CHECK_EQ(length, written);
TEST_CHECK(SetFilePointer(file, 0, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER);
}
int main(void) {
NtReadFile_t fn = load_ntreadfile();
DeleteFileA(kTempFileName);
HANDLE file = CreateFileA(kTempFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
TEST_CHECK(file != INVALID_HANDLE_VALUE);
const char payload[] = "hello";
write_fixture(file, payload, (DWORD)(sizeof(payload) - 1));
HANDLE event = CreateEventA(NULL, TRUE, TRUE, NULL);
TEST_CHECK(event != NULL);
IO_STATUS_BLOCK iosb;
memset(&iosb, 0, sizeof(iosb));
char buffer[6];
memset(buffer, 0, sizeof(buffer));
SetLastError(ERROR_GEN_FAILURE);
DWORD before = GetLastError();
NTSTATUS status = fn(file, event, NULL, NULL, &iosb, buffer, 5, NULL, NULL);
TEST_CHECK_EQ((NTSTATUS)STATUS_SUCCESS, status);
TEST_CHECK_EQ(5u, (unsigned int)iosb.Information);
TEST_CHECK(memcmp(buffer, payload, 5) == 0);
TEST_CHECK_EQ(before, GetLastError());
TEST_CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(event, 0));
TEST_CHECK(ResetEvent(event));
LARGE_INTEGER useCurrent;
useCurrent.QuadPart = -2;
IO_STATUS_BLOCK eofIosb;
memset(&eofIosb, 0, sizeof(eofIosb));
memset(buffer, 0, sizeof(buffer));
SetLastError(ERROR_GEN_FAILURE);
before = GetLastError();
status = fn(file, event, NULL, NULL, &eofIosb, buffer, sizeof(buffer), &useCurrent, NULL);
TEST_CHECK_EQ((NTSTATUS)STATUS_END_OF_FILE, status);
TEST_CHECK_EQ(0u, (unsigned int)eofIosb.Information);
TEST_CHECK_EQ(before, GetLastError());
TEST_CHECK(buffer[0] == 0);
TEST_CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(event, 0));
TEST_CHECK(CloseHandle(event));
TEST_CHECK(CloseHandle(file));
TEST_CHECK(DeleteFileA(kTempFileName));
return 0;
}

96
test/test_pipe_io.c Normal file
View File

@@ -0,0 +1,96 @@
#include "test_assert.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winternl.h>
#include <string.h>
#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#endif
#ifndef STATUS_PIPE_BROKEN
#define STATUS_PIPE_BROKEN ((NTSTATUS)0xC000014BL)
#endif
typedef NTSTATUS(NTAPI *NtReadFile_t)(HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID, PIO_STATUS_BLOCK, PVOID, ULONG,
PLARGE_INTEGER, PULONG);
static NtReadFile_t load_ntreadfile(void) {
HMODULE mod = GetModuleHandleW(L"ntdll.dll");
if (!mod) {
TEST_CHECK_EQ(ERROR_MOD_NOT_FOUND, GetLastError());
mod = LoadLibraryW(L"ntdll.dll");
}
TEST_CHECK(mod != NULL);
FARPROC proc = GetProcAddress(mod, "NtReadFile");
TEST_CHECK(proc != NULL);
NtReadFile_t fn = NULL;
TEST_CHECK(sizeof(fn) == sizeof(proc));
memcpy(&fn, &proc, sizeof(fn));
return fn;
}
static void write_bytes(HANDLE handle, const char *data, size_t length) {
DWORD written = 0;
TEST_CHECK(WriteFile(handle, data, (DWORD)length, &written, NULL));
TEST_CHECK_EQ((DWORD)length, written);
}
int main(void) {
NtReadFile_t fn = load_ntreadfile();
HANDLE readPipe = NULL;
HANDLE writePipe = NULL;
TEST_CHECK(CreatePipe(&readPipe, &writePipe, NULL, 0));
const char msgReadFile[] = "pipe-read";
write_bytes(writePipe, msgReadFile, strlen(msgReadFile));
char buffer[64];
memset(buffer, 0, sizeof(buffer));
DWORD bytesRead = 0;
TEST_CHECK(ReadFile(readPipe, buffer, sizeof(buffer), &bytesRead, NULL));
TEST_CHECK_EQ((DWORD)strlen(msgReadFile), bytesRead);
TEST_CHECK(memcmp(buffer, msgReadFile, bytesRead) == 0);
const char msgNtRead[] = "ntread";
write_bytes(writePipe, msgNtRead, strlen(msgNtRead));
HANDLE event = CreateEventA(NULL, TRUE, FALSE, NULL);
TEST_CHECK(event != NULL);
IO_STATUS_BLOCK iosb;
memset(&iosb, 0, sizeof(iosb));
memset(buffer, 0, sizeof(buffer));
NTSTATUS status = fn(readPipe, event, NULL, NULL, &iosb, buffer, sizeof(buffer), NULL, NULL);
TEST_CHECK_EQ((NTSTATUS)STATUS_SUCCESS, status);
TEST_CHECK_EQ((ULONG_PTR)strlen(msgNtRead), iosb.Information);
TEST_CHECK(memcmp(buffer, msgNtRead, iosb.Information) == 0);
TEST_CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(event, 0));
TEST_CHECK(CloseHandle(writePipe));
writePipe = NULL;
bytesRead = 123;
SetLastError(ERROR_GEN_FAILURE);
BOOL ok = ReadFile(readPipe, buffer, sizeof(buffer), &bytesRead, NULL);
TEST_CHECK(!ok);
TEST_CHECK_EQ(0u, (unsigned int)bytesRead);
TEST_CHECK_EQ(ERROR_BROKEN_PIPE, GetLastError());
TEST_CHECK(ResetEvent(event));
memset(&iosb, 0, sizeof(iosb));
status = fn(readPipe, event, NULL, NULL, &iosb, buffer, sizeof(buffer), NULL, NULL);
TEST_CHECK_EQ((NTSTATUS)STATUS_PIPE_BROKEN, status);
TEST_CHECK_EQ(0u, (unsigned int)iosb.Information);
TEST_CHECK_EQ(WAIT_TIMEOUT, WaitForSingleObject(event, 0));
TEST_CHECK(CloseHandle(event));
TEST_CHECK(CloseHandle(readPipe));
if (writePipe) {
TEST_CHECK(CloseHandle(writePipe));
}
return 0;
}