// User-mode smoke test for server-owned POSIX spawn and child lifecycle semantics. #include #include #include #include #include #include #include #include #include #include #include #define CONTROL_READ_FD 40 #define ACTION_OPEN_FD 41 #define ACTION_DUP_FD 42 #define CONTROL_WRITE_FD 43 #define CONTROL_WRITE_A_FD 44 #define EXEC_PARENT_READ_FD 45 #define EXEC_CHILD_WRITE_FD 46 #define EXEC_CHILD_READ_FD 47 #define EXEC_PARENT_WRITE_FD 48 #define EXEC_RETAIN_FD 49 #define EXEC_CLOEXEC_FD 50 static const char* gExecFile = "posix_process_exec.tmp"; static volatile uint32_t gVforkChildTouched; static void print(const char* text) { (void)write(1, text, strlen(text)); } // Terminates the smoke test with one diagnostic. static void fail(const char* message) { print("posix_process_smoke: "); print(message); print("\n"); exit(1); } // Returns whether the command-line compatibility view contains one child marker. static bool child_mode(const char* marker) { const char* command = posix_get_command_line(); return command && strstr(command, marker) != (char*)0; } // Moves one descriptor to an exact number and closes the source alias. static void move_fd(int source, int target) { if (source < 0 || dup2(source, target) != target) fail("control dup2 failed"); if (source != target && close(source) != 0) fail("control source close failed"); } // Builds one inherited control pipe with a close-on-spawn parent write endpoint. static void make_control_pipe(int writeFd) { int pipefd[2]; if (pipe(pipefd) != 0) fail("control pipe failed"); move_fd(pipefd[0], CONTROL_READ_FD); move_fd(pipefd[1], writeFd); if (fcntl(writeFd, F_SETFD, FD_CLOEXEC) != 0) fail("control CLOEXEC failed"); } // Resolves the current executable path from the compatibility command line. static void get_self_path(char* path, size_t capacity) { const char* command = posix_get_command_line(); size_t length = 0; while (command && command[length] && command[length] != ' ' && command[length] != '\t' && length + 1u < capacity) { path[length] = command[length]; ++length; } path[length] = '\0'; if (length == 0 || (command[length] && command[length] != ' ' && command[length] != '\t')) fail("could not resolve self path"); } // Converts one positive 32-bit value to decimal without depending on stdio. static void format_u32(uint32_t value, char* text, size_t capacity) { char reverse[16]; size_t count = 0; if (!text || capacity == 0) return; do { reverse[count++] = (char)('0' + value % 10u); value /= 10u; } while (value && count < sizeof(reverse)); if (count + 1u > capacity) fail("numeric format overflow"); for (size_t i = 0; i < count; ++i) text[i] = reverse[count - i - 1u]; text[count] = '\0'; } // Parses one nonzero decimal 32-bit value used by exec smoke stages. static uint32_t parse_u32(const char* text) { if (!text || !text[0]) return 0; uint32_t value = 0; for (size_t i = 0; text[i]; ++i) { if (text[i] < '0' || text[i] > '9') return 0; uint32_t digit = (uint32_t)(text[i] - '0'); if (value > (UINT32_MAX - digit) / 10u) return 0; value = value * 10u + digit; } return value; } // Reports one exec-stage failure before preserving its distinct exit code. static void exec_stage_fail(uint32_t code, const char* stage) { char codeText[16]; format_u32(code, codeText, sizeof(codeText)); print("posix_process_smoke: exec stage failed: "); print(stage); print(" exit="); print(codeText); print("\n"); _exit((int)code); } // Exists only so exec can prove caught dispositions reset to default. static void exec_caught_handler(int signalNumber) { (void)signalNumber; } // First child stage captures state and replaces its native generation. static void run_exec_stage1(const char* self) { pid_t stablePid = getpid(); uint32_t nativePid = SysGetCurrentPid(); if (stablePid <= 0 || nativePid == 0) _exit(120); int retained = open(gExecFile, O_CREAT | O_TRUNC | O_RDWR, 0666); if (retained < 0) _exit(121); move_fd(retained, EXEC_RETAIN_FD); int cloexec = dup(EXEC_RETAIN_FD); if (cloexec < 0) _exit(122); move_fd(cloexec, EXEC_CLOEXEC_FD); if (fcntl(EXEC_CLOEXEC_FD, F_SETFD, FD_CLOEXEC) != 0) _exit(123); struct sigaction ignored; memset(&ignored, 0, sizeof(ignored)); ignored.sa_handler = SIG_IGN; if (sigaction(SIGUSR1, &ignored, (struct sigaction*)0) != 0) _exit(124); struct sigaction caught; memset(&caught, 0, sizeof(caught)); caught.sa_handler = exec_caught_handler; if (sigaction(SIGUSR2, &caught, (struct sigaction*)0) != 0) _exit(125); sigset_t blocked; if (sigemptyset(&blocked) != 0 || sigaddset(&blocked, SIGTERM) != 0 || sigprocmask(SIG_BLOCK, &blocked, (sigset_t*)0) != 0) _exit(126); char stableText[16]; char nativeText[16]; format_u32((uint32_t)stablePid, stableText, sizeof(stableText)); format_u32(nativePid, nativeText, sizeof(nativeText)); char* nextArgv[] = { (char*)self, "--p7-exec2", stableText, nativeText, (char*)0 }; char* nextEnv[] = { "P7_ENV=replaced", (char*)0 }; (void)execve(self, nextArgv, nextEnv); { char errnoText[16]; format_u32((uint32_t)errno, errnoText, sizeof(errnoText)); print("posix_process_smoke: execve stage1 returned errno="); print(errnoText); print("\n"); _exit(127); } } // Second child stage validates one committed replacement and performs a repeated exec. static void run_exec_stage2(int argc, char** argv) { if (argc != 4) exec_stage_fail(128, "argv"); uint32_t stablePid = parse_u32(argv[2]); uint32_t oldNativePid = parse_u32(argv[3]); uint32_t nativePid = SysGetCurrentPid(); if (stablePid == 0 || oldNativePid == 0 || (uint32_t)getpid() != stablePid || nativePid == 0 || nativePid == oldNativePid) exec_stage_fail(129, "stable/native pid"); const char* marker = getenv("P7_ENV"); if (!marker || strcmp(marker, "replaced") != 0) exec_stage_fail(130, "environment"); if (fcntl(EXEC_RETAIN_FD, F_GETFD) < 0) exec_stage_fail(131, "retained fd"); errno = 0; if (fcntl(EXEC_CLOEXEC_FD, F_GETFD) != -1 || errno != EBADF) exec_stage_fail(132, "cloexec fd"); struct sigaction action; memset(&action, 0, sizeof(action)); if (sigaction(SIGUSR1, (const struct sigaction*)0, &action) != 0 || action.sa_handler != SIG_IGN) exec_stage_fail(133, "ignored disposition"); if (sigaction(SIGUSR2, (const struct sigaction*)0, &action) != 0 || action.sa_handler != SIG_DFL) exec_stage_fail(134, "caught disposition reset"); sigset_t blocked = 0; if (sigprocmask(SIG_BLOCK, (const sigset_t*)0, &blocked) != 0 || sigismember(&blocked, SIGTERM) != 1) exec_stage_fail(135, "blocked mask"); if (write(EXEC_CHILD_WRITE_FD, "E", 1) != 1) exec_stage_fail(136, "marker write"); char release = 0; if (read(EXEC_CHILD_READ_FD, &release, 1) != 1 || release != 'R') _exit(137); char stableText[16]; char nativeText[16]; format_u32(stablePid, stableText, sizeof(stableText)); format_u32(nativePid, nativeText, sizeof(nativeText)); char self[512]; get_self_path(self, sizeof(self)); char* nextArgv[] = { self, "--p7-exec3", stableText, nativeText, (char*)0 }; char* nextEnv[] = { "P7_ENV=second", (char*)0 }; (void)execve(self, nextArgv, nextEnv); _exit(138); } // Final child stage proves repeated native generations retain the same POSIX PID. static void run_exec_stage3(int argc, char** argv) { if (argc != 4) _exit(139); uint32_t stablePid = parse_u32(argv[2]); uint32_t oldNativePid = parse_u32(argv[3]); uint32_t nativePid = SysGetCurrentPid(); if (stablePid == 0 || oldNativePid == 0 || (uint32_t)getpid() != stablePid || nativePid == 0 || nativePid == oldNativePid) _exit(140); const char* marker = getenv("P7_ENV"); if (!marker || strcmp(marker, "second") != 0 || fcntl(EXEC_RETAIN_FD, F_GETFD) < 0) _exit(141); if (write(EXEC_CHILD_WRITE_FD, "D", 1) != 1) _exit(142); (void)close(EXEC_RETAIN_FD); (void)close(EXEC_CHILD_WRITE_FD); (void)close(EXEC_CHILD_READ_FD); (void)unlink(gExecFile); _exit(47); } // Executes with Linux-style null argv/envp. The replacement retains the spawned child parent association. static void run_null_exec_stage(const char* self) { (void)execve(self, (char* const*)0, (char* const*)0); _exit(143); } // Exercises replacement, rollback, wait isolation, repeated generations, and null vectors. static void run_exec_parent(const char* self) { pid_t beforePid = getpid(); uint32_t beforeNativePid = SysGetCurrentPid(); char* missingArgv[] = { "/system/p7_missing_image.exe", (char*)0 }; errno = 0; if (execve(missingArgv[0], missingArgv, (char* const*)0) != -1 || getpid() != beforePid || SysGetCurrentPid() != beforeNativePid) fail("failed exec did not preserve old image"); int childToParent[2]; int parentToChild[2]; if (pipe(childToParent) != 0 || pipe(parentToChild) != 0) fail("exec control pipe failed"); move_fd(childToParent[0], EXEC_PARENT_READ_FD); move_fd(childToParent[1], EXEC_CHILD_WRITE_FD); move_fd(parentToChild[0], EXEC_CHILD_READ_FD); move_fd(parentToChild[1], EXEC_PARENT_WRITE_FD); posix_spawn_file_actions_t actions; if (posix_spawn_file_actions_init(&actions) != 0 || posix_spawn_file_actions_addclose(&actions, EXEC_PARENT_READ_FD) != 0 || posix_spawn_file_actions_addclose(&actions, EXEC_PARENT_WRITE_FD) != 0) fail("exec spawn actions failed"); char* childArgv[] = { (char*)self, "--p7-exec1", (char*)0 }; pid_t child = (pid_t)-1; int error = posix_spawn(&child, self, &actions, (const posix_spawnattr_t*)0, childArgv, (char* const*)0); if (error != 0 || child <= 0) fail("exec child spawn failed"); if (posix_spawn_file_actions_destroy(&actions) != 0) fail("exec actions destroy failed"); if (close(EXEC_CHILD_WRITE_FD) != 0 || close(EXEC_CHILD_READ_FD) != 0) fail("exec parent child-end close failed"); char marker = 0; if (read(EXEC_PARENT_READ_FD, &marker, 1) != 1 || marker != 'E') { int earlyStatus = -1; pid_t early = waitpid(child, &earlyStatus, WNOHANG); if (early == child && WIFEXITED(earlyStatus)) { char exitText[16]; format_u32((uint32_t)WEXITSTATUS(earlyStatus), exitText, sizeof(exitText)); print("posix_process_smoke: exec child exited before marker exit="); print(exitText); print("\n"); } else if (early == 0) { print("posix_process_smoke: exec child still running after marker channel closed\n"); } else { print("posix_process_smoke: exec child wait diagnostic unavailable\n"); } fail("exec replacement marker missing"); } int childStatus = -1; if (waitpid(child, &childStatus, WNOHANG) != 0) fail("exec retirement became waitable"); if (write(EXEC_PARENT_WRITE_FD, "R", 1) != 1) fail("exec replacement release failed"); marker = 0; if (read(EXEC_PARENT_READ_FD, &marker, 1) != 1 || marker != 'D') fail("repeated exec marker missing"); if (close(EXEC_PARENT_READ_FD) != 0 || close(EXEC_PARENT_WRITE_FD) != 0) fail("exec parent pipe close failed"); if (waitpid(child, &childStatus, 0) != child || !WIFEXITED(childStatus) || WEXITSTATUS(childStatus) != 47) fail("repeated exec child wait failed"); char* nullArgv[] = { (char*)self, "--p7-null", (char*)0 }; child = (pid_t)-1; error = posix_spawn(&child, self, (const posix_spawn_file_actions_t*)0, (const posix_spawnattr_t*)0, nullArgv, (char* const*)0); if (error != 0 || child <= 0) fail("null-vector child spawn failed"); childStatus = -1; if (waitpid(child, &childStatus, 0) != child || !WIFEXITED(childStatus) || WEXITSTATUS(childStatus) != 48) fail("null argv/env exec failed"); } // Validates the independent native generation created by vfork()+execve(). static void run_vfork_exec_stage(int argc, char** argv) { if (argc != 4) _exit(150); uint32_t stablePid = parse_u32(argv[2]); uint32_t parentNativePid = parse_u32(argv[3]); if (stablePid == 0 || parentNativePid == 0 || (uint32_t)getpid() != stablePid || SysGetCurrentPid() == 0 || SysGetCurrentPid() == parentNativePid) _exit(151); _exit(52); } // Exercises both terminal vfork paths and the child-zero/parent-PID continuation split. static void run_vfork_parent(const char* self) { gVforkChildTouched = 0; pid_t child = vfork(); if (child < 0) fail("vfork _exit creation failed"); if (child == 0) { gVforkChildTouched = 1u; _exit(51); } if (!gVforkChildTouched) fail("vfork parent resumed before child terminal path"); int status = -1; if (waitpid(child, &status, 0) != child || !WIFEXITED(status) || WEXITSTATUS(status) != 51) fail("vfork _exit wait failed"); uint32_t parentNativePid = SysGetCurrentPid(); child = vfork(); if (child < 0) fail("vfork exec creation failed"); if (child == 0) { char stableText[16]; char parentNativeText[16]; format_u32((uint32_t)getpid(), stableText, sizeof(stableText)); format_u32(parentNativePid, parentNativeText, sizeof(parentNativeText)); char* nextArgv[] = { (char*)self, "--p7-vfork-exec", stableText, parentNativeText, (char*)0 }; (void)execve(self, nextArgv, (char* const*)0); _exit(152); } status = -1; if (waitpid(child, &status, 0) != child || !WIFEXITED(status) || WEXITSTATUS(status) != 52) fail("vfork exec wait failed"); } // Validates server-owned bootstrap state, then blocks until the parent releases the child. static void run_full_child(void) { pid_t pid = getpid(); pid_t parent = getppid(); if (pid <= 0 || parent <= 0 || parent == pid) _exit(101); if (getpgrp() != pid) _exit(102); const char* marker = getenv("P5_ENV"); if (!marker || strcmp(marker, "server-owned") != 0) _exit(103); const char* expectedCwd = getenv("P5_CWD"); char cwd[512]; if (!expectedCwd || !getcwd(cwd, sizeof(cwd)) || strcmp(cwd, expectedCwd) != 0) _exit(104); errno = 0; if (fcntl(ACTION_OPEN_FD, F_GETFD) != -1 || errno != EBADF) _exit(105); if (write(ACTION_DUP_FD, "ok", 2) != 2) _exit(106); if (close(ACTION_DUP_FD) != 0) _exit(107); errno = 0; if (fcntl(CONTROL_WRITE_FD, F_GETFD) != -1 || errno != EBADF) _exit(108); char release = 0; if (read(CONTROL_READ_FD, &release, 1) != 1 || release != 'R') _exit(109); (void)close(CONTROL_READ_FD); _exit(23); } // Blocks one child until the parent supplies the exit code byte. static void run_wait_child(void) { if (getppid() <= 0 || getppid() == getpid()) _exit(110); char exitCode = 0; if (read(CONTROL_READ_FD, &exitCode, 1) != 1) _exit(111); (void)close(CONTROL_READ_FD); _exit((unsigned char)exitCode); } // Spawns one child that waits on CONTROL_READ_FD. static pid_t spawn_wait_child(const char* self, int writeFd) { make_control_pipe(writeFd); char* argv[] = { (char*)self, "--p5-wait", (char*)0 }; pid_t child = (pid_t)-1; int error = posix_spawn(&child, self, (const posix_spawn_file_actions_t*)0, (const posix_spawnattr_t*)0, argv, (char* const*)0); if (error != 0 || child <= 0) fail("wait child spawn failed"); if (close(CONTROL_READ_FD) != 0) fail("parent control read close failed"); return child; } // Exercises the P5 server-owned process construction and wait contract. int main(int argc, char** argv) { if (argc == 0) { pid_t parent = getppid(); if (parent < 0) _exit(145); if (parent == 0) { char self[512]; get_self_path(self, sizeof(self)); char* rootArgv[] = { self, "--p5-root", (char*)0 }; pid_t child = (pid_t)-1; int error = posix_spawn(&child, self, (const posix_spawn_file_actions_t*)0, (const posix_spawnattr_t*)0, rootArgv, environ); if (error != 0 || child <= 0) _exit(146); int childStatus = -1; if (waitpid(child, &childStatus, 0) != child || !WIFEXITED(childStatus)) _exit(147); _exit(WEXITSTATUS(childStatus)); } if (!environ || environ[0] != (char*)0) _exit(144); _exit(48); } if (argc >= 2 && strcmp(argv[1], "--p7-exec1") == 0) run_exec_stage1(argv[0]); if (argc >= 2 && strcmp(argv[1], "--p7-exec2") == 0) run_exec_stage2(argc, argv); if (argc >= 2 && strcmp(argv[1], "--p7-exec3") == 0) run_exec_stage3(argc, argv); if (argc >= 2 && strcmp(argv[1], "--p7-null") == 0) run_null_exec_stage(argv[0]); if (argc >= 2 && strcmp(argv[1], "--p7-vfork-exec") == 0) run_vfork_exec_stage(argc, argv); if (child_mode("--p5-full")) run_full_child(); if (child_mode("--p5-wait")) run_wait_child(); char self[512]; get_self_path(self, sizeof(self)); char cwd[512]; if (!getcwd(cwd, sizeof(cwd))) fail("parent getcwd failed"); const char cwdPrefix[] = "P5_CWD="; char cwdEnvironment[sizeof(cwdPrefix) + sizeof(cwd)]; size_t prefixLength = sizeof(cwdPrefix) - 1u; size_t cwdLength = strlen(cwd); if (prefixLength + cwdLength + 1u > sizeof(cwdEnvironment)) fail("cwd environment overflow"); memcpy(cwdEnvironment, cwdPrefix, prefixLength); memcpy(cwdEnvironment + prefixLength, cwd, cwdLength + 1u); const char* actionPath = "posix_process_spawn.tmp"; const char* collisionPath = "posix_process_collision.tmp"; (void)unlink(actionPath); (void)unlink(collisionPath); make_control_pipe(CONTROL_WRITE_FD); // Seed the dup2 target with an unrelated inherited description so the child // action must replace an already-occupied descriptor number. int collision = open(collisionPath, O_CREAT | O_TRUNC | O_RDWR, 0666); if (collision < 0) fail("spawn collision source open failed"); move_fd(collision, ACTION_DUP_FD); posix_spawn_file_actions_t actions; if (posix_spawn_file_actions_init(&actions) != 0 || posix_spawn_file_actions_addopen(&actions, ACTION_OPEN_FD, actionPath, O_CREAT | O_TRUNC | O_RDWR, 0666) != 0 || posix_spawn_file_actions_adddup2(&actions, ACTION_OPEN_FD, ACTION_DUP_FD) != 0 || posix_spawn_file_actions_addclose(&actions, ACTION_OPEN_FD) != 0) fail("spawn file actions setup failed"); posix_spawnattr_t attributes; if (posix_spawnattr_init(&attributes) != 0 || posix_spawnattr_setpgroup(&attributes, 0) != 0 || posix_spawnattr_setflags(&attributes, POSIX_SPAWN_SETPGROUP) != 0) fail("spawn attributes setup failed"); char* childArgv[] = { self, "--p5-full", (char*)0 }; char* childEnv[] = { "P5_ENV=server-owned", cwdEnvironment, (char*)0 }; pid_t child = (pid_t)-1; int spawnError = posix_spawn(&child, self, &actions, &attributes, childArgv, childEnv); if (spawnError != 0 || child <= 0) fail("full child spawn failed"); if (posix_spawn_file_actions_destroy(&actions) != 0 || posix_spawnattr_destroy(&attributes) != 0) fail("spawn setup destroy failed"); if (close(ACTION_DUP_FD) != 0) fail("parent collision descriptor close failed"); if (unlink(collisionPath) != 0) fail("parent collision source cleanup failed"); if (close(CONTROL_READ_FD) != 0) fail("full parent control read close failed"); int childStatus = -1; if (waitpid(child, &childStatus, WNOHANG) != 0) fail("WNOHANG did not report running child"); if (write(CONTROL_WRITE_FD, "R", 1) != 1 || close(CONTROL_WRITE_FD) != 0) fail("full child release failed"); if (waitpid(child, &childStatus, 0) != child || !WIFEXITED(childStatus) || WEXITSTATUS(childStatus) != 23) fail("exact child wait failed"); int result = open(actionPath, O_RDONLY); char actionBytes[3] = {0, 0, 0}; if (result < 0 || read(result, actionBytes, 2) != 2 || strcmp(actionBytes, "ok") != 0) fail("spawn file action result was wrong"); if (close(result) != 0 || unlink(actionPath) != 0) fail("spawn action cleanup failed"); errno = 0; if (waitpid(child, &childStatus, WNOHANG) != (pid_t)-1 || errno != ECHILD) fail("collected child remained waitable"); pid_t failedPid = 123; if (posix_spawn(&failedPid, "/system/p5_missing_image.exe", (const posix_spawn_file_actions_t*)0, (const posix_spawnattr_t*)0, (char* const*)0, (char* const*)0) == 0 || failedPid != (pid_t)-1) fail("failed construction returned a child"); pid_t first = spawn_wait_child(self, CONTROL_WRITE_A_FD); pid_t second = spawn_wait_child(self, CONTROL_WRITE_FD); char secondCode = 32; if (write(CONTROL_WRITE_FD, &secondCode, 1) != 1 || close(CONTROL_WRITE_FD) != 0) fail("second child release failed"); childStatus = -1; if (wait(&childStatus) != second || WEXITSTATUS(childStatus) != 32) fail("wait did not collect second child first"); char firstCode = 31; if (write(CONTROL_WRITE_A_FD, &firstCode, 1) != 1 || close(CONTROL_WRITE_A_FD) != 0) fail("first child release failed"); childStatus = -1; if (wait(&childStatus) != first || WEXITSTATUS(childStatus) != 31) fail("wait did not collect first child second"); errno = 0; if (wait((int*)0) != (pid_t)-1 || errno != ECHILD) fail("wait without children did not return ECHILD"); run_exec_parent(self); run_vfork_parent(self); print("posix_process_smoke: PASS\n"); return 0; }