1 #include <darwintest.h>
4 #include <os/assumes.h>
5 #include <os/overflow.h>
9 #include <sys/sysctl.h>
10 #include <System/sys/kdebug.h>
14 #include <sys/proc_info.h>
15 #include <sys/event.h>
19 T_GLOBAL_META(T_META_NAMESPACE("xnu.all"));
21 #pragma mark proc_list_uptrs
24 static uint64_t uptrs
[NUPTRS
] = {
25 0x1122334455667788ULL
,
26 0x99aabbccddeeff00ULL
,
27 0xaabbaaddccaaffeeULL
,
31 static const char *uptr_names
[NUPTRS
];
34 print_uptrs(int argc
, char * const *argv
)
36 for (int i
= 0; i
< argc
; i
++) {
38 unsigned long pid
= strtoul(argv
[i
], &end
, 0);
40 printf("error: pid '%lu' would overflow an integer\n", pid
);
43 printf("error: could not parse '%s' as a pid\n", argv
[i
]);
46 int uptrs_count
= proc_list_uptrs((int)pid
, NULL
, 0);
47 if (uptrs_count
== 0) {
48 printf("no uptrs for process %d\n", (int)pid
);
53 unsigned int uptrs_len
= (unsigned int)uptrs_count
+ 32;
55 uint64_t *uptrs_alloc
= malloc(sizeof(uint64_t) * uptrs_len
);
56 os_assert(uptrs_alloc
!= NULL
);
58 uptrs_count
= proc_list_uptrs((int)pid
, uptrs_alloc
,
59 (uint32_t)(sizeof(uint64_t) * uptrs_len
));
60 printf("process %d has %d uptrs:\n", (int)pid
, uptrs_count
);
61 if (uptrs_count
> (int)uptrs_len
) {
62 uptrs_count
= (int)uptrs_len
;
64 for (int j
= 0; j
< uptrs_count
; j
++) {
65 printf("%#17" PRIx64
"\n", uptrs_alloc
[j
]);
70 T_DECL(proc_list_uptrs
,
71 "the kernel should return any up-pointers it knows about",
72 T_META_ALL_VALID_ARCHS(YES
))
75 print_uptrs(argc
, argv
);
76 T_SKIP("command line invocation of tool, not test");
79 unsigned int cur_uptr
= 0;
82 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kq
, "kqueue");
85 * Should find uptrs on file-type knotes and generic knotes (two
86 * different search locations, internally).
88 struct kevent64_s events
[2];
89 memset(events
, 0, sizeof(events
));
91 uptr_names
[cur_uptr
] = "kqueue file-backed knote";
92 events
[0].filter
= EVFILT_WRITE
;
93 events
[0].ident
= STDOUT_FILENO
;
94 events
[0].flags
= EV_ADD
;
95 events
[0].udata
= uptrs
[cur_uptr
++];
97 uptr_names
[cur_uptr
] = "kqueue non-file-backed knote";
98 events
[1].filter
= EVFILT_USER
;
100 events
[1].flags
= EV_ADD
;
101 events
[1].udata
= uptrs
[cur_uptr
++];
103 int kev_err
= kevent64(kq
, events
, sizeof(events
) / sizeof(events
[0]), NULL
,
104 0, KEVENT_FLAG_IMMEDIATE
, NULL
);
105 T_ASSERT_POSIX_SUCCESS(kev_err
, "register events with kevent64");
108 * Should find uptrs both on a kevent_id kqueue and in a workloop
109 * kqueue's knote's udata field.
111 uptr_names
[cur_uptr
] = "dynamic kqueue non-file-backed knote";
112 struct kevent_qos_s events_id
[] = {{
113 .filter
= EVFILT_USER
,
116 .udata
= uptrs
[cur_uptr
++]
119 uptr_names
[cur_uptr
] = "dynamic kqueue ID";
120 kev_err
= kevent_id(uptrs
[cur_uptr
++], events_id
, 1, NULL
, 0, NULL
, NULL
,
121 KEVENT_FLAG_WORKLOOP
| KEVENT_FLAG_IMMEDIATE
);
122 T_ASSERT_POSIX_SUCCESS(kev_err
, "register event with kevent_id");
125 int uptrs_count
= proc_list_uptrs(getpid(), NULL
, 0);
126 T_QUIET
; T_ASSERT_POSIX_SUCCESS(uptrs_count
, "proc_list_uptrs");
127 T_QUIET
; T_EXPECT_EQ(uptrs_count
, NUPTRS
,
128 "should see correct number of up-pointers");
130 uint64_t uptrs_obs
[NUPTRS
] = { 0 };
131 uptrs_count
= proc_list_uptrs(getpid(), uptrs_obs
, sizeof(uptrs_obs
));
132 T_QUIET
; T_ASSERT_POSIX_SUCCESS(uptrs_count
, "proc_list_uptrs");
134 for (int i
= 0; i
< uptrs_count
; i
++) {
136 for (int j
= 0; j
< NUPTRS
; j
++) {
137 if (uptrs_obs
[i
] == uptrs
[j
]) {
142 T_FAIL("unexpected up-pointer found: %#" PRIx64
, uptrs_obs
[i
]);
145 T_PASS("found up-pointer for %s", uptr_names
[found
]);
150 #pragma mark dynamic kqueue info
152 #define EXPECTED_ID UINT64_C(0x1122334455667788)
153 #define EXPECTED_UDATA UINT64_C(0x99aabbccddeeff00)
155 #define KQ_WORKLOOP 0x80
159 setup_kevent_id(kqueue_id_t id
)
161 struct kevent_qos_s events_id
[] = {{
162 .filter
= EVFILT_USER
,
165 .udata
= EXPECTED_UDATA
168 int err
= kevent_id(id
, events_id
, 1, NULL
, 0, NULL
, NULL
,
169 KEVENT_FLAG_WORKLOOP
| KEVENT_FLAG_IMMEDIATE
);
170 T_ASSERT_POSIX_SUCCESS(err
, "register event with kevent_id");
174 list_kqids(pid_t pid
, int *nkqids_out
)
178 kqueue_id_t
*kqids
= NULL
;
182 if (os_mul_overflow(sizeof(kqueue_id_t
), kqids_len
, &kqids_size
)) {
183 T_QUIET
; T_ASSERT_GT(kqids_len
, PROC_PIDDYNKQUEUES_MAX
, NULL
);
184 kqids_len
= PROC_PIDDYNKQUEUES_MAX
;
188 kqids
= malloc(kqids_size
);
189 T_QUIET
; T_ASSERT_NOTNULL(kqids
, "malloc(%" PRIu32
")", kqids_size
);
192 nkqids
= proc_list_dynkqueueids(pid
, kqids
, kqids_size
);
193 if (nkqids
> kqids_len
&& kqids_len
< PROC_PIDDYNKQUEUES_MAX
) {
195 if (kqids_len
> PROC_PIDDYNKQUEUES_MAX
) {
196 kqids_len
= PROC_PIDDYNKQUEUES_MAX
;
203 *nkqids_out
= nkqids
;
207 T_DECL(list_dynamic_kqueues
,
208 "the kernel should list IDs of dynamic kqueues",
209 T_META_ALL_VALID_ARCHS(true))
214 setup_kevent_id(EXPECTED_ID
);
215 kqueue_id_t
*kqids
= list_kqids(getpid(), &nkqids
);
216 T_ASSERT_GE(nkqids
, 1, "at least one dynamic kqueue is listed");
217 for (int i
= 0; i
< nkqids
; i
++) {
218 if (kqids
[i
] == EXPECTED_ID
) {
220 T_PASS("found expected dynamic kqueue ID");
222 T_LOG("found another dynamic kqueue with ID %#" PRIx64
, kqids
[i
]);
227 T_FAIL("could not find dynamic ID of kqueue created");
233 T_DECL(dynamic_kqueue_basic_info
,
234 "the kernel should report valid basic dynamic kqueue info",
235 T_META_ALL_VALID_ARCHS(true))
237 struct kqueue_info kqinfo
;
240 setup_kevent_id(EXPECTED_ID
);
241 ret
= proc_piddynkqueueinfo(getpid(), PROC_PIDDYNKQUEUE_INFO
, EXPECTED_ID
,
242 &kqinfo
, sizeof(kqinfo
));
243 T_ASSERT_POSIX_SUCCESS(ret
,
244 "proc_piddynkqueueinfo(... PROC_PIDDYNKQUEUE_INFO ...)");
245 T_QUIET
; T_ASSERT_GE(ret
, (int)sizeof(kqinfo
),
246 "PROC_PIDDYNKQUEUE_INFO should return the right size");
248 T_EXPECT_NE(kqinfo
.kq_state
& KQ_WORKLOOP
, 0U,
249 "kqueue info should be for a workloop kqueue");
250 T_EXPECT_EQ(kqinfo
.kq_stat
.vst_ino
, EXPECTED_ID
,
251 "inode field should be the kqueue's ID");
254 T_DECL(dynamic_kqueue_extended_info
,
255 "the kernel should report valid extended dynamic kqueue info",
256 T_META_ALL_VALID_ARCHS(true))
258 struct kevent_extinfo kqextinfo
[1];
261 setup_kevent_id(EXPECTED_ID
);
262 ret
= proc_piddynkqueueinfo(getpid(), PROC_PIDDYNKQUEUE_EXTINFO
,
263 EXPECTED_ID
, kqextinfo
, sizeof(kqextinfo
));
264 T_ASSERT_POSIX_SUCCESS(ret
,
265 "proc_piddynkqueueinfo(... PROC_PIDDYNKQUEUE_EXTINFO ...)");
266 T_QUIET
; T_ASSERT_EQ(ret
, 1,
267 "PROC_PIDDYNKQUEUE_EXTINFO should return a single knote");
269 T_EXPECT_EQ(kqextinfo
[0].kqext_kev
.ident
, 1ULL,
270 "kevent identifier matches what was configured");
271 T_EXPECT_EQ(kqextinfo
[0].kqext_kev
.filter
, (short)EVFILT_USER
,
272 "kevent filter matches what was configured");
273 T_EXPECT_EQ(kqextinfo
[0].kqext_kev
.udata
, EXPECTED_UDATA
,
274 "kevent udata matches what was configured");
277 #pragma mark proc_listpids
279 T_DECL(list_kdebug_pids
,
280 "the kernel should report processes that are filtered by kdebug",
283 int mib
[4] = { CTL_KERN
, KERN_KDEBUG
};
288 size_t regsize
= sizeof(reg
);
290 mib
[2] = KERN_KDREMOVE
;
291 ret
= sysctl(mib
, 3, NULL
, NULL
, NULL
, 0);
292 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "KERN_KDREMOVE sysctl");
294 mib
[2] = KERN_KDSETBUF
; mib
[3] = 100000;
295 ret
= sysctl(mib
, 4, NULL
, NULL
, NULL
, 0);
296 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "KERN_KDSETBUF sysctl");
298 mib
[2] = KERN_KDSETUP
;
299 ret
= sysctl(mib
, 3, NULL
, NULL
, NULL
, 0);
300 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "KERN_KDSETUP sysctl");
302 npids
= proc_listpids(PROC_KDBG_ONLY
, 0, pids
, sizeof(pids
));
303 T_EXPECT_EQ(npids
, 0, "no processes should be filtered initially");
305 reg
.type
= KDBG_TYPENONE
;
306 reg
.value1
= getpid();
307 reg
.value2
= 1; /* set the pid in the filter */
308 mib
[2] = KERN_KDPIDTR
;
309 ret
= sysctl(mib
, 3, ®
, ®size
, NULL
, 0);
310 T_ASSERT_POSIX_SUCCESS(ret
,
311 "KERN_KDPIDTR sysctl to set a pid in the filter");
313 npids
= proc_listpids(PROC_KDBG_ONLY
, 0, pids
, sizeof(pids
));
315 T_EXPECT_EQ(npids
, 1, "a process should be filtered");
316 T_EXPECT_EQ(pids
[0], getpid(),
317 "process filtered should be the one that was set");
319 mib
[2] = KERN_KDREMOVE
;
320 ret
= sysctl(mib
, 3, NULL
, NULL
, NULL
, 0);
321 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "KERN_KDREMOVE sysctl");