xref: /xnu-11215.41.3/tests/proc_info_list_kthreads.c (revision 33de042d024d46de5ff4e89f2471de6608e37fa4) !
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