1 #include <assert.h> 2 #include <stdio.h> 3 #include <pthread.h> 4 #include <signal.h> 5 #include <unistd.h> 6 #include <errno.h> 7 #include <string.h> 8 #include <sys/wait.h> 9 #include <sys/types.h> 10 #include <sys/time.h> 11 #include <sys/event.h> 12 #include <sys/ptrace.h> 13 #include <sys/proc.h> 14 #include <stdlib.h> 15 #include <System/sys/codesign.h> 16 #include <darwintest.h> 17 18 T_GLOBAL_META(T_META_NAMESPACE("xnu.note_exec"), 19 T_META_RADAR_COMPONENT_NAME("xnu"), 20 T_META_RADAR_COMPONENT_VERSION("spawn")); 21 22 static int kq; 23 static int pid; 24 25 static void 26 do_exec(void) 27 { 28 char echo_arg[50] = ""; 29 30 snprintf(echo_arg, sizeof(echo_arg), "Child[%d] says hello after exec", getpid()); 31 32 char * new_argv[] = { 33 "/bin/echo", 34 echo_arg, 35 NULL 36 }; 37 38 int ret = execv(new_argv[0], new_argv); 39 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "execv()"); 40 } 41 42 static void * 43 thread_wait_exec(void *arg __unused) 44 { 45 int ret; 46 struct kevent64_s kev; 47 int csret; 48 uint32_t status = 0; 49 50 while (1) { 51 ret = kevent64(kq, NULL, 0, &kev, 1, 0, NULL); 52 if (ret == -1) { 53 if (errno == EINTR) { 54 continue; 55 } 56 } 57 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kevent64()"); 58 break; 59 } 60 61 /* Try to get the csops of child before we print anything */ 62 csret = csops(pid, CS_OPS_STATUS, &status, sizeof(status)); 63 if (csret != 0) { 64 T_QUIET; T_LOG("Child exited before parent could call csops. The race didn't happen"); 65 return NULL; 66 } 67 68 T_QUIET; T_ASSERT_EQ(ret, 1, "kevent64 returned 1 event as expected"); 69 T_QUIET; T_ASSERT_EQ((int)kev.filter, EVFILT_PROC, "EVFILT_PROC event received"); 70 T_QUIET; T_ASSERT_EQ((int)kev.udata, pid, "EVFILT_PROC event received for child pid"); 71 T_QUIET; T_ASSERT_EQ((kev.fflags & NOTE_EXEC), NOTE_EXEC, "NOTE_EXEC event received"); 72 73 /* Check that the platform binary bit is set */ 74 T_EXPECT_BITS_SET(status, CS_PLATFORM_BINARY, "CS_PLATFORM_BINARY should be set on child"); 75 76 return NULL; 77 } 78 79 static void 80 run_test(void) 81 { 82 struct kevent64_s kev; 83 int ret; 84 int fd[2]; 85 86 ret = pipe(fd); 87 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "pipe()"); 88 close(fd[0]); 89 90 T_QUIET; T_LOG("Forking child"); 91 92 pid = fork(); 93 94 if (pid == 0) { 95 char buf[10]; 96 97 close(fd[1]); 98 ret = (int)read(fd[0], buf, sizeof(buf)); 99 close(fd[0]); 100 101 do_exec(); 102 exit(1); 103 } 104 105 T_QUIET; T_LOG("Setting up NOTE_EXEC Handler for child pid %d", pid); 106 kq = kqueue(); 107 T_QUIET; T_ASSERT_POSIX_SUCCESS(kq, "kqueue()"); 108 109 EV_SET64(&kev, pid, EVFILT_PROC, EV_ADD | EV_ENABLE, 110 NOTE_EXEC, 0, pid, 0, 0); 111 ret = kevent64(kq, &kev, 1, NULL, 0, 0, NULL); 112 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kevent64()"); 113 114 pthread_t thread; 115 ret = pthread_create(&thread, NULL, thread_wait_exec, NULL); 116 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "pthread_create()"); 117 118 T_QUIET; T_LOG("Signalling child to call exec"); 119 close(fd[1]); 120 121 T_QUIET; T_LOG("Waiting for child to exit"); 122 pid = waitpid(pid, NULL, 0); 123 T_QUIET; T_ASSERT_POSIX_SUCCESS(pid, "waitpid()"); 124 125 T_QUIET; T_LOG("Waiting for note exec thread to exit"); 126 ret = pthread_join(thread, NULL); 127 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "pthread_join()"); 128 129 close(kq); 130 } 131 132 T_DECL(test_note_exec, "test NOTE_EXEC race with setting csops") { 133 T_QUIET; T_LOG("Testing race for NOTE_EXEC with csops"); 134 135 for (int i = 0; i < 100; i++) { 136 T_QUIET; T_LOG("Running iteration %d", i); 137 run_test(); 138 } 139 T_END; 140 } 141