139 lines
4.3 KiB
C
139 lines
4.3 KiB
C
// Native anonymous queue lifetime smoke test.
|
|
// Verifies queue message delivery and survival through a derived handle.
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <ntlite/syscall.h>
|
|
|
|
#define TEST_MESSAGE_TYPE 0x71756575u
|
|
#define TEST_MESSAGE_VALUE 0x123456789abcdef0ull
|
|
|
|
typedef struct TEST_QUEUE_MESSAGE {
|
|
uint32_t Type;
|
|
uint32_t Flags;
|
|
uint64_t Param[6];
|
|
} TEST_QUEUE_MESSAGE;
|
|
|
|
typedef char TEST_QUEUE_MESSAGE_SIZE_CHECK[
|
|
(sizeof(TEST_QUEUE_MESSAGE) == SYS_QUEUE_MESSAGE_SIZE) ? 1 : -1
|
|
];
|
|
|
|
// Prints one NUL-terminated string to the kernel console.
|
|
static void print(const char* s) {
|
|
SYS_WRITE_CONSOLE_REQUEST req = { sizeof(SYS_WRITE_CONSOLE_REQUEST), SysSvFromCstr(s) };
|
|
SysWriteConsole(&req);
|
|
}
|
|
|
|
// Prints one unsigned integer to the kernel console.
|
|
static void print_u64(uint64_t v) {
|
|
char buf[32];
|
|
int i = 0;
|
|
if (v == 0) {
|
|
print("0");
|
|
return;
|
|
}
|
|
while (v && i < (int)sizeof(buf)) {
|
|
buf[i++] = (char)('0' + (v % 10));
|
|
v /= 10;
|
|
}
|
|
while (i--) {
|
|
char c = buf[i];
|
|
SYS_WRITE_CONSOLE_REQUEST req = { sizeof(SYS_WRITE_CONSOLE_REQUEST), SysSvFromParts(&c, 1) };
|
|
SysWriteConsole(&req);
|
|
}
|
|
}
|
|
|
|
// Terminates the current test process.
|
|
static void exit_now(int code) {
|
|
while (true) SysExitProcess(code);
|
|
}
|
|
|
|
// Fails the test with one plain message.
|
|
static void fail(const char* msg) {
|
|
print("anonymous_queue_smoke: ");
|
|
print(msg);
|
|
print("\n");
|
|
exit_now(1);
|
|
}
|
|
|
|
// Fails the test with one status value.
|
|
static void fail_status(const char* what, KSTATUS status) {
|
|
print("anonymous_queue_smoke: ");
|
|
print(what);
|
|
print(" status=");
|
|
print_u64(status);
|
|
print("\n");
|
|
exit_now(1);
|
|
}
|
|
|
|
// Sends one fixed-size queue message.
|
|
static KSTATUS send_message(uint32_t queue, const TEST_QUEUE_MESSAGE* message) {
|
|
SYS_QUEUE_SEND_REQUEST req;
|
|
memset(&req, 0, sizeof(req));
|
|
req.Size = sizeof(req);
|
|
req.QueueHandle = queue;
|
|
req.Flags = SYS_QUEUE_FLAG_NONE;
|
|
req.TimeoutMilliseconds = 0;
|
|
req.Message = message;
|
|
return SysSendQueue(&req);
|
|
}
|
|
|
|
// Receives one fixed-size queue message.
|
|
static uint32_t recv_message(uint32_t queue, TEST_QUEUE_MESSAGE* message) {
|
|
SYS_QUEUE_RECV_REQUEST req;
|
|
memset(&req, 0, sizeof(req));
|
|
req.Size = sizeof(req);
|
|
req.QueueHandle = queue;
|
|
req.Flags = SYS_QUEUE_FLAG_NONE;
|
|
req.TimeoutMilliseconds = 0;
|
|
req.MessageOut = message;
|
|
return SysRecvQueue(&req);
|
|
}
|
|
|
|
// Verifies aliases keep an anonymous queue alive after the original handle closes.
|
|
static void check_alias_lifetime(void) {
|
|
SYS_HANDLE queue = SYS_HANDLE_INVALID;
|
|
SYS_HANDLE alias = SYS_HANDLE_INVALID;
|
|
KSTATUS status = SysCreateQueue(&queue);
|
|
if (!KSUCCESS(status)) fail_status("create queue", status);
|
|
|
|
SYS_HANDLE self = SYS_HANDLE_INVALID;
|
|
status = SysOpenProcess(SysGetCurrentPid(), SYS_RIGHT_QUERY | SYS_PROCESS_RIGHT_INSERT_HANDLE, &self);
|
|
if (!KSUCCESS(status)) fail_status("open self", status);
|
|
SYS_DUPLICATE_HANDLE_REQUEST duplicate = {0};
|
|
duplicate.Size = sizeof(duplicate);
|
|
duplicate.SourceHandle = queue;
|
|
duplicate.TargetProcessHandle = self;
|
|
duplicate.DesiredRights = SYS_QUEUE_RIGHT_SEND | SYS_QUEUE_RIGHT_RECEIVE;
|
|
duplicate.TargetHandleOut = &alias;
|
|
status = SysDuplicateHandle(&duplicate);
|
|
if (!KSUCCESS(status)) fail_status("duplicate queue", status);
|
|
(void)SysCloseHandle(self);
|
|
|
|
TEST_QUEUE_MESSAGE out = {0};
|
|
TEST_QUEUE_MESSAGE in = {0};
|
|
out.Type = TEST_MESSAGE_TYPE;
|
|
out.Param[0] = TEST_MESSAGE_VALUE;
|
|
status = send_message(queue, &out);
|
|
if (!KSUCCESS(status)) fail_status("send queue", status);
|
|
status = SysCloseHandle(queue);
|
|
if (!KSUCCESS(status)) fail_status("close original", status);
|
|
status = recv_message(alias, &in);
|
|
if (!KSUCCESS(status)) fail_status("recv via surviving alias", status);
|
|
if (in.Type != TEST_MESSAGE_TYPE || in.Param[0] != TEST_MESSAGE_VALUE)
|
|
fail("message corrupted");
|
|
|
|
status = recv_message(alias, &in);
|
|
if (status != NTLSTATUS_WOULD_BLOCK) fail_status("empty queue", status);
|
|
(void)SysCloseHandle(alias);
|
|
}
|
|
|
|
// Runs the anonymous queue lifetime smoke test.
|
|
int main(void) {
|
|
print("anonymous_queue_smoke: start\n");
|
|
check_alias_lifetime();
|
|
print("anonymous_queue_smoke: PASS\n");
|
|
exit_now(0);
|
|
return 0;
|
|
}
|