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 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | #include <assert.h> #include <stdio.h> #include <pthread.h> #include <signal.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/wait.h> #include <sys/types.h> #include <sys/time.h> #include <sys/event.h> #include <sys/ptrace.h> #include <sys/proc.h> #include <stdlib.h> #include <System/sys/codesign.h> #include <darwintest.h> T_GLOBAL_META(T_META_NAMESPACE("xnu.note_exec"), T_META_RADAR_COMPONENT_NAME("xnu"), T_META_RADAR_COMPONENT_VERSION("spawn")); static int kq; static int pid; static void do_exec(void) { char echo_arg[50] = ""; snprintf(echo_arg, sizeof(echo_arg), "Child[%d] says hello after exec", getpid()); char * new_argv[] = { "/bin/echo", echo_arg, NULL }; int ret = execv(new_argv[0], new_argv); T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "execv()"); } static void * thread_wait_exec(void *arg __unused) { int ret; struct kevent64_s kev; int csret; uint32_t status = 0; while (1) { ret = kevent64(kq, NULL, 0, &kev, 1, 0, NULL); if (ret == -1) { if (errno == EINTR) { continue; } } T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kevent64()"); break; } /* Try to get the csops of child before we print anything */ csret = csops(pid, CS_OPS_STATUS, &status, sizeof(status)); if (csret != 0) { T_QUIET; T_LOG("Child exited before parent could call csops. The race didn't happen"); return NULL; } T_QUIET; T_ASSERT_EQ(ret, 1, "kevent64 returned 1 event as expected"); T_QUIET; T_ASSERT_EQ((int)kev.filter, EVFILT_PROC, "EVFILT_PROC event received"); T_QUIET; T_ASSERT_EQ((int)kev.udata, pid, "EVFILT_PROC event received for child pid"); T_QUIET; T_ASSERT_EQ((kev.fflags & NOTE_EXEC), NOTE_EXEC, "NOTE_EXEC event received"); /* Check that the platform binary bit is set */ T_EXPECT_BITS_SET(status, CS_PLATFORM_BINARY, "CS_PLATFORM_BINARY should be set on child"); return NULL; } static void run_test(void) { struct kevent64_s kev; int ret; int fd[2]; ret = pipe(fd); T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "pipe()"); close(fd[0]); T_QUIET; T_LOG("Forking child"); pid = fork(); if (pid == 0) { char buf[10]; close(fd[1]); ret = (int)read(fd[0], buf, sizeof(buf)); close(fd[0]); do_exec(); exit(1); } T_QUIET; T_LOG("Setting up NOTE_EXEC Handler for child pid %d", pid); kq = kqueue(); T_QUIET; T_ASSERT_POSIX_SUCCESS(kq, "kqueue()"); EV_SET64(&kev, pid, EVFILT_PROC, EV_ADD | EV_ENABLE, NOTE_EXEC, 0, pid, 0, 0); ret = kevent64(kq, &kev, 1, NULL, 0, 0, NULL); T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kevent64()"); pthread_t thread; ret = pthread_create(&thread, NULL, thread_wait_exec, NULL); T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "pthread_create()"); T_QUIET; T_LOG("Signalling child to call exec"); close(fd[1]); T_QUIET; T_LOG("Waiting for child to exit"); pid = waitpid(pid, NULL, 0); T_QUIET; T_ASSERT_POSIX_SUCCESS(pid, "waitpid()"); T_QUIET; T_LOG("Waiting for note exec thread to exit"); ret = pthread_join(thread, NULL); T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "pthread_join()"); close(kq); } T_DECL(test_note_exec, "test NOTE_EXEC race with setting csops") { T_QUIET; T_LOG("Testing race for NOTE_EXEC with csops"); for (int i = 0; i < 100; i++) { T_QUIET; T_LOG("Running iteration %d", i); run_test(); } T_END; } |