1 /* 2 * proc_info_list_kthreads 3 * 4 * list 64 bit thread ids of kernel_task 5 */ 6 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <assert.h> 10 #include <err.h> 11 12 #include <libproc.h> 13 #include <strings.h> 14 #include <darwintest.h> 15 #include <TargetConditionals.h> 16 17 T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true)); 18 19 #define MAX_TRIES 20 20 #define EXTRA_THREADS 15 21 22 #if TARGET_OS_OSX 23 T_DECL(proc_info_list_kthreads, 24 "Test to verify PROC_PIDLISTTHREADIDS returns kernel thread IDs for pid 0", 25 T_META_ASROOT(true), 26 T_META_CHECK_LEAKS(false), 27 T_META_TAG_VM_PREFERRED) 28 #else 29 T_DECL(proc_info_list_kthreads, 30 "Test to verify PROC_PIDLISTTHREADIDS returns kernel thread IDs for pid 0", 31 T_META_ASROOT(false), 32 T_META_CHECK_LEAKS(false), 33 T_META_TAG_VM_PREFERRED) 34 #endif /* TARGET_OS_OSX */ 35 { 36 int buf_used = 0; 37 38 int thread_count = 0; 39 uint64_t *thread_list = NULL; 40 41 /* 42 * To use PROC_PIDLISTTHREADIDS, we must pass a buffer of uint64_t's for each thread ID. 43 * However, there is a TOCTOU race between asking for the thread count 44 * and asking for the array of identifiers. 45 * 46 * Because the process could have allocated more threads since last we asked 47 * how many threads there are, we instead pass an extra slot in the array, 48 * and try again if it used that slot. 49 */ 50 51 int attempt = 1; 52 while (!thread_count && (attempt < MAX_TRIES)) { 53 struct proc_taskinfo ti; 54 55 buf_used = proc_pidinfo(0, PROC_PIDTASKINFO, 0, &ti, sizeof(ti)); 56 57 T_QUIET; T_WITH_ERRNO; T_ASSERT_GT(buf_used, 0, "proc_pidinfo(PROC_PIDTASKINFO) returned a value > 0"); 58 T_QUIET; T_ASSERT_EQ(buf_used, (int)sizeof(ti), "proc_pidinfo(PROC_PIDTASKINFO) returned size %d == %lu", buf_used, sizeof(ti)); 59 60 T_LOG("The kernel says it has %d threads", ti.pti_threadnum); 61 62 int expected_size = ti.pti_threadnum * (int)sizeof(uint64_t); 63 /* tack on five extra to detect newly allocated threads */ 64 int allocated_size = expected_size + EXTRA_THREADS * (int)sizeof(uint64_t); 65 uint64_t *thread_list_tmp = malloc((size_t)allocated_size); 66 T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(thread_list_tmp, "malloc(size = %d) failed", allocated_size); 67 68 buf_used = proc_pidinfo(0, PROC_PIDLISTTHREADIDS, 0, thread_list_tmp, (int)allocated_size); 69 T_LOG("proc_pidinfo(PROC_PIDLISTTHREADIDS) buf_used = %d, expected_size = %d", buf_used, expected_size); 70 71 if (buf_used == 0) { 72 T_WITH_ERRNO; T_ASSERT_FAIL("proc_pidinfo(PROC_PIDLISTTHREADIDS) failed"); 73 } 74 if (buf_used == expected_size) { 75 /* success, we found the expected number of threads */ 76 thread_list = thread_list_tmp; 77 thread_count = expected_size / (int)sizeof(uint64_t); 78 } else if (buf_used < expected_size) { 79 /* there were fewer threads than we expected, fix up the allocation */ 80 thread_list = realloc(thread_list_tmp, (size_t)buf_used); 81 thread_count = buf_used / (int)sizeof(uint64_t); 82 T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(thread_list, "realloc(size = %d) failed", buf_used); 83 } else if (buf_used > expected_size) { 84 if (buf_used < allocated_size) { 85 thread_list = realloc(thread_list_tmp, (size_t)buf_used); 86 thread_count = buf_used / (int)sizeof(uint64_t); 87 T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(thread_list, "realloc(size = %d) failed", buf_used); 88 } else { 89 /* 90 * it used all the extra slots, meaning there are more 91 * threads than we thought, try again! 92 */ 93 T_LOG("expected %d threads, but saw an extra thread: %d", 94 expected_size / (int)sizeof(uint64_t), buf_used / (int)sizeof(uint64_t)); 95 free(thread_list_tmp); 96 } 97 } 98 attempt++; 99 } 100 T_QUIET; T_ASSERT_LE(attempt, MAX_TRIES, "attempt <= MAX_TRIES"); 101 T_QUIET; T_ASSERT_NOTNULL(thread_list, "thread_list != NULL"); 102 T_QUIET; T_ASSERT_GT(thread_count, 0, "thread_count > 0"); 103 104 struct proc_threadinfo pthinfo_64; 105 for (int i = 0; i < thread_count; i++) { 106 bzero(&pthinfo_64, sizeof(struct proc_threadinfo)); 107 int retval = proc_pidinfo(0, PROC_PIDTHREADID64INFO, thread_list[i], 108 (void *)&pthinfo_64, (uint32_t)sizeof(pthinfo_64)); 109 T_QUIET; T_WITH_ERRNO; T_EXPECT_GT(retval, 0, "proc_pidinfo(PROC_PIDTASKINFO) returned %d", retval); 110 T_QUIET; T_EXPECT_EQ(retval, (int)sizeof(pthinfo_64), "proc_pidinfo(PROC_PIDTASKINFO) returned size %d == %lu", 111 retval, sizeof(pthinfo_64)); 112 } 113 } 114