Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | #include <darwintest.h> #include <signal.h> #include <spawn.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h> #include <libproc.h> #include <sys/reason.h> #include <TargetConditionals.h> T_GLOBAL_META( T_META_NAMESPACE("xnu.spawn"), T_META_RADAR_COMPONENT_NAME("xnu"), T_META_RADAR_COMPONENT_VERSION("spawn"), T_META_ENABLED(TARGET_OS_OSX)); static void __run_cmd(const char *cmd_prefix, const char *filename, const char *error) { char cmd[PATH_MAX]; strlcpy(cmd, cmd_prefix, sizeof(cmd)); strlcat(cmd, filename, sizeof(cmd)); FILE *file = popen(cmd, "r"); T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(file, "%s (cmd = %s)", error, cmd); pclose(file); } static void __spawn_exec(const char *args[], short flags) { posix_spawnattr_t attr; int error; error = posix_spawnattr_init(&attr); T_QUIET; T_ASSERT_POSIX_SUCCESS(error, "spawn attributes initialized"); error = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETEXEC | flags); T_QUIET; T_ASSERT_POSIX_SUCCESS(error, "spawn attributes flags set"); posix_spawnp(NULL, args[0], NULL, &attr, args, NULL); } static void invalid_code_signature_helper() { char filename[PATH_MAX]; sprintf(filename, "/tmp/echo-test-%ld", random()); T_LOG("temporary file created: %s", filename); __run_cmd("cp /bin/echo ", filename, "create a temporary copy"); __run_cmd("codesign --force --sign - --team-identifier 0 ", filename, "codesign the temporary copy with an invalid team ID"); /* Exec into the modified binary */ const char* args[] = { filename, NULL }; __spawn_exec(args, 0); } static void bad_spawnattr_helper() { const char* args[] = { "/bin/echo", NULL}; int error; error = setsid(); T_QUIET; T_ASSERT_POSIX_SUCCESS(error, "set SID before exec"); __spawn_exec(args, POSIX_SPAWN_SETSID); } static void setup_child_and_wait_for_exit( void (*do_exec)(void), uint64_t expected_reason_namespace, uint64_t expected_reason_code) { pid_t child = fork(); if (child > 0) { int status, ret; struct proc_exitreasonbasicinfo exit_reason; sleep(1); ret = proc_pidinfo(child, PROC_PIDEXITREASONBASICINFO, 1, &exit_reason, PROC_PIDEXITREASONBASICINFOSIZE); T_QUIET; T_ASSERT_EQ(ret, PROC_PIDEXITREASONBASICINFOSIZE, "retrive basic exit reason info"); waitpid(child, &status, 0); T_QUIET; T_EXPECT_FALSE(WIFEXITED(status), "process did not exit normally"); T_QUIET; T_EXPECT_TRUE(WIFSIGNALED(status), "process was terminated because of a signal"); T_EXPECT_EQ(WTERMSIG(status), SIGKILL, "process was SIGKILLed"); T_EXPECT_EQ(exit_reason.beri_namespace, expected_reason_namespace, "killed with reason EXEC"); T_EXPECT_EQ(exit_reason.beri_code, expected_reason_code, "reason code is %d", expected_reason_code); } else { do_exec(); T_FAIL("Shouldn't reach here!"); } } T_DECL(spawn_exec_double_set_sid, "exec fails upon trying to set SID twice") { setup_child_and_wait_for_exit(bad_spawnattr_helper, OS_REASON_EXEC, EXEC_EXIT_REASON_BAD_PSATTR); } T_DECL(spawn_exec_invalid_cs, "exec fails due to invalid code signature") { #if defined(__arm64__) setup_child_and_wait_for_exit(invalid_code_signature_helper, OS_REASON_EXEC, EXEC_EXIT_REASON_SECURITY_POLICY); #else /* __arm64__ */ setup_child_and_wait_for_exit(invalid_code_signature_helper, OS_REASON_CODESIGNING, CODESIGNING_EXIT_REASON_TASKGATED_INVALID_SIG); #endif /* __arm64__ */ } |