xref: /xnu-12377.41.6/bsd/kern/kern_newsysctl.c (revision bbb1b6f9e71b8cdde6e5cd6f4841f207dee3d828)
1 /*
2  * Copyright (c) 2000-2019 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  *
28  *
29  * Copyright (c) 1982, 1986, 1989, 1993
30  *	The Regents of the University of California.  All rights reserved.
31  *
32  * This code is derived from software contributed to Berkeley by
33  * Mike Karels at Berkeley Software Design, Inc.
34  *
35  * Quite extensively rewritten by Poul-Henning Kamp of the FreeBSD
36  * project, to make these variables more userfriendly.
37  *
38  * Redistribution and use in source and binary forms, with or without
39  * modification, are permitted provided that the following conditions
40  * are met:
41  * 1. Redistributions of source code must retain the above copyright
42  *    notice, this list of conditions and the following disclaimer.
43  * 2. Redistributions in binary form must reproduce the above copyright
44  *    notice, this list of conditions and the following disclaimer in the
45  *    documentation and/or other materials provided with the distribution.
46  * 3. All advertising materials mentioning features or use of this software
47  *    must display the following acknowledgement:
48  *	This product includes software developed by the University of
49  *	California, Berkeley and its contributors.
50  * 4. Neither the name of the University nor the names of its contributors
51  *    may be used to endorse or promote products derived from this software
52  *    without specific prior written permission.
53  *
54  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
55  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
57  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
58  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
59  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
60  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
61  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
62  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
63  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64  * SUCH DAMAGE.
65  *
66  *	@(#)kern_sysctl.c	8.4 (Berkeley) 4/14/94
67  */
68 
69 
70 #include <kern/counter.h>
71 #include <sys/param.h>
72 #include <sys/buf.h>
73 #include <sys/kernel.h>
74 #include <sys/sysctl.h>
75 #include <sys/malloc.h>
76 #include <sys/proc_internal.h>
77 #include <sys/kauth.h>
78 #include <sys/systm.h>
79 #include <sys/sysproto.h>
80 #include <sys/variant_internal.h>
81 
82 #include <vm/vm_pageout_xnu.h>
83 
84 #include <os/atomic_private.h>
85 
86 #include <security/audit/audit.h>
87 #include <pexpert/pexpert.h>
88 
89 #include <IOKit/IOBSD.h>
90 
91 #if CONFIG_MACF
92 #include <security/mac_framework.h>
93 #endif
94 
95 #if defined(HAS_APPLE_PAC)
96 #include <os/hash.h>
97 #include <ptrauth.h>
98 #endif /* defined(HAS_APPLE_PAC) */
99 
100 #include <libkern/coreanalytics/coreanalytics.h>
101 
102 #if DEBUG || DEVELOPMENT
103 #include <os/system_event_log.h>
104 #endif /* DEBUG || DEVELOPMENT */
105 
106 static LCK_GRP_DECLARE(sysctl_lock_group, "sysctl");
107 static LCK_RW_DECLARE(sysctl_geometry_lock, &sysctl_lock_group);
108 static LCK_MTX_DECLARE(sysctl_unlocked_node_lock, &sysctl_lock_group);
109 
110 /*
111  * Conditionally allow dtrace to see these functions for debugging purposes.
112  */
113 #ifdef STATIC
114 #undef STATIC
115 #endif
116 #if 0
117 #define STATIC
118 #else
119 #define STATIC static
120 #endif
121 
122 /* forward declarations  of static functions */
123 STATIC void sysctl_sysctl_debug_dump_node(struct sysctl_oid_list *l, int i);
124 STATIC int sysctl_sysctl_debug(struct sysctl_oid *oidp, void *arg1,
125     int arg2, struct sysctl_req *req);
126 STATIC int sysctl_sysctl_name(struct sysctl_oid *oidp, void *arg1,
127     int arg2, struct sysctl_req *req);
128 STATIC int sysctl_sysctl_next_ls(struct sysctl_oid_list *lsp,
129     int *name, u_int namelen, int *next, int *len, int level,
130     struct sysctl_oid **oidpp);
131 STATIC int sysctl_old_kernel(struct sysctl_req *req, const void *p, size_t l);
132 STATIC int sysctl_new_kernel(struct sysctl_req *req, void *p, size_t l);
133 STATIC int name2oid(char *name, int *oid, size_t *len);
134 STATIC int sysctl_sysctl_name2oid(struct sysctl_oid *oidp, void *arg1, int arg2, struct sysctl_req *req);
135 STATIC int sysctl_sysctl_next(struct sysctl_oid *oidp, void *arg1, int arg2,
136     struct sysctl_req *req);
137 STATIC int sysctl_sysctl_oidfmt(struct sysctl_oid *oidp, void *arg1, int arg2, struct sysctl_req *req);
138 STATIC int sysctl_old_user(struct sysctl_req *req, const void *p, size_t l);
139 STATIC int sysctl_new_user(struct sysctl_req *req, void *p, size_t l);
140 
141 STATIC void sysctl_create_user_req(struct sysctl_req *req, struct proc *p, user_addr_t oldp,
142     size_t oldlen, user_addr_t newp, size_t newlen);
143 STATIC int sysctl_root(boolean_t from_kernel, boolean_t string_is_canonical, char *namestring, size_t namestringlen, int *name, size_t namelen, struct sysctl_req *req);
144 
145 int     kernel_sysctl(struct proc *p, int *name, size_t namelen, void *old, size_t *oldlenp, void *new, size_t newlen);
146 int     kernel_sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
147 int     userland_sysctl(boolean_t string_is_canonical,
148     char *namestring, size_t namestringlen,
149     int *name, u_int namelen, struct sysctl_req *req,
150     size_t *retval);
151 
152 SECURITY_READ_ONLY_LATE(struct sysctl_oid_list) sysctl__children; /* root list */
153 __SYSCTL_EXTENSION_NODE();
154 
155 /*
156  * Initialization of the MIB tree.
157  *
158  * Order by number in each list.
159  */
160 
161 static void
sysctl_register_oid_locked(struct sysctl_oid * new_oidp,struct sysctl_oid * oidp)162 sysctl_register_oid_locked(struct sysctl_oid *new_oidp,
163     struct sysctl_oid *oidp)
164 {
165 	struct sysctl_oid_list *parent = new_oidp->oid_parent;
166 	struct sysctl_oid_list *parent_rw = NULL;
167 	struct sysctl_oid *p, **prevp;
168 
169 	p = SLIST_FIRST(parent);
170 	if (p && p->oid_number == OID_MUTABLE_ANCHOR) {
171 		parent_rw = p->oid_arg1;
172 	}
173 
174 	if (oidp->oid_number == OID_AUTO) {
175 		int n = OID_AUTO_START;
176 
177 		/*
178 		 * If this oid has a number OID_AUTO, give it a number which
179 		 * is greater than any current oid.  Make sure it is at least
180 		 * OID_AUTO_START to leave space for pre-assigned oid numbers.
181 		 */
182 
183 		SLIST_FOREACH_PREVPTR(p, prevp, parent, oid_link) {
184 			if (p->oid_number >= n) {
185 				n = p->oid_number + 1;
186 			}
187 		}
188 
189 		if (parent_rw) {
190 			SLIST_FOREACH_PREVPTR(p, prevp, parent_rw, oid_link) {
191 				if (p->oid_number >= n) {
192 					n = p->oid_number + 1;
193 				}
194 			}
195 		}
196 
197 		/*
198 		 * Reflect the number in an allocated OID into the template
199 		 * of the caller for sysctl_unregister_oid() compares.
200 		 */
201 		oidp->oid_number = new_oidp->oid_number = n;
202 	} else {
203 		/*
204 		 * Insert the oid into the parent's list in order.
205 		 */
206 		SLIST_FOREACH_PREVPTR(p, prevp, parent, oid_link) {
207 			if (oidp->oid_number == p->oid_number) {
208 				panic("attempting to register a sysctl at previously registered slot : %d",
209 				    oidp->oid_number);
210 			} else if (oidp->oid_number < p->oid_number) {
211 				break;
212 			}
213 		}
214 
215 		if (parent_rw) {
216 			SLIST_FOREACH_PREVPTR(p, prevp, parent_rw, oid_link) {
217 				if (oidp->oid_number == p->oid_number) {
218 					panic("attempting to register a sysctl at previously registered slot : %d",
219 					    oidp->oid_number);
220 				} else if (oidp->oid_number < p->oid_number) {
221 					break;
222 				}
223 			}
224 		}
225 	}
226 
227 #if defined(HAS_APPLE_PAC)
228 	if (oidp->oid_handler) {
229 		/*
230 		 * Sign oid_handler address-discriminated upon installation to make it
231 		 * harder to replace with an arbitrary function pointer.  Blend with
232 		 * a hash of oid_arg1 for robustness against memory corruption.
233 		 */
234 		oidp->oid_handler = ptrauth_auth_and_resign(oidp->oid_handler,
235 		    ptrauth_key_function_pointer,
236 		    ptrauth_function_pointer_type_discriminator(typeof(oidp->oid_handler)),
237 		    ptrauth_key_function_pointer,
238 		    ptrauth_blend_discriminator(&oidp->oid_handler,
239 		    os_hash_kernel_pointer(oidp->oid_arg1)));
240 	}
241 #endif /* defined(HAS_APPLE_PAC) */
242 
243 	SLIST_NEXT(oidp, oid_link) = *prevp;
244 	*prevp = oidp;
245 }
246 
247 void
sysctl_register_oid(struct sysctl_oid * new_oidp)248 sysctl_register_oid(struct sysctl_oid *new_oidp)
249 {
250 	struct sysctl_oid *oidp;
251 
252 	if (new_oidp->oid_number < OID_AUTO) {
253 		panic("trying to register a node %p with an invalid oid_number: %d",
254 		    new_oidp, new_oidp->oid_number);
255 	}
256 	if (new_oidp->oid_kind & CTLFLAG_PERMANENT) {
257 		panic("Use sysctl_register_oid_early to register permanent nodes");
258 	}
259 
260 	/*
261 	 * The OID can be old-style (needs copy), new style without an earlier
262 	 * version (also needs copy), or new style with a matching version (no
263 	 * copy needed).  Later versions are rejected (presumably, the OID
264 	 * structure was changed for a necessary reason).
265 	 */
266 	if (!(new_oidp->oid_kind & CTLFLAG_OID2)) {
267 #if __x86_64__
268 		oidp = kalloc_type(struct sysctl_oid, Z_WAITOK | Z_ZERO | Z_NOFAIL);
269 		/*
270 		 * Copy the structure only through the oid_fmt field, which
271 		 * is the last field in a non-OID2 OID structure.
272 		 *
273 		 * Note:	We may want to set the oid_descr to the
274 		 *		oid_name (or "") at some future date.
275 		 */
276 		memcpy(oidp, new_oidp, offsetof(struct sysctl_oid, oid_descr));
277 #else
278 		panic("Old style sysctl without a version number isn't supported");
279 #endif
280 	} else {
281 		/* It's a later version; handle the versions we know about */
282 		switch (new_oidp->oid_version) {
283 		case SYSCTL_OID_VERSION:
284 			/* current version */
285 			oidp = new_oidp;
286 			break;
287 		default:
288 			return;                 /* rejects unknown version */
289 		}
290 	}
291 
292 	lck_rw_lock_exclusive(&sysctl_geometry_lock);
293 	sysctl_register_oid_locked(new_oidp, oidp);
294 	lck_rw_unlock_exclusive(&sysctl_geometry_lock);
295 }
296 
297 __startup_func
298 void
sysctl_register_oid_early(struct sysctl_oid * oidp)299 sysctl_register_oid_early(struct sysctl_oid *oidp)
300 {
301 	assert((oidp->oid_kind & CTLFLAG_OID2) &&
302 	    (oidp->oid_kind & CTLFLAG_PERMANENT) &&
303 	    oidp->oid_version == SYSCTL_OID_VERSION);
304 	assert(startup_phase < STARTUP_SUB_SYSCTL);
305 
306 	/*
307 	 * Clear the flag so that callers can use sysctl_register_oid_early
308 	 * again if they wish to register their node.
309 	 */
310 	if (oidp->oid_kind & CTLFLAG_NOAUTO) {
311 		oidp->oid_kind &= ~CTLFLAG_NOAUTO;
312 		return;
313 	}
314 
315 	sysctl_register_oid_locked(oidp, oidp);
316 }
317 
318 void
sysctl_unregister_oid(struct sysctl_oid * oidp)319 sysctl_unregister_oid(struct sysctl_oid *oidp)
320 {
321 	struct sysctl_oid *removed_oidp = NULL; /* OID removed from tree */
322 #if __x86_64__
323 	struct sysctl_oid *old_oidp = NULL;     /* OID compatibility copy */
324 #endif
325 	struct sysctl_oid_list *lsp;
326 
327 	/* Get the write lock to modify the geometry */
328 	lck_rw_lock_exclusive(&sysctl_geometry_lock);
329 
330 	lsp = oidp->oid_parent;
331 	if (SLIST_FIRST(lsp) && SLIST_FIRST(lsp)->oid_number == OID_MUTABLE_ANCHOR) {
332 		lsp = SLIST_FIRST(lsp)->oid_arg1;
333 	}
334 
335 	if (oidp->oid_kind & CTLFLAG_PERMANENT) {
336 		panic("Trying to unregister permanent sysctl %p", oidp);
337 	}
338 
339 	if (!(oidp->oid_kind & CTLFLAG_OID2)) {
340 #if __x86_64__
341 		/*
342 		 * We're using a copy so we can get the new fields in an
343 		 * old structure, so we have to iterate to compare the
344 		 * partial structure; when we find a match, we remove it
345 		 * normally and free the memory.
346 		 */
347 		SLIST_FOREACH(old_oidp, lsp, oid_link) {
348 			if (!memcmp(&oidp->oid_number, &old_oidp->oid_number, (offsetof(struct sysctl_oid, oid_descr) - offsetof(struct sysctl_oid, oid_number)))) {
349 				break;
350 			}
351 		}
352 		if (old_oidp != NULL) {
353 			SLIST_REMOVE(lsp, old_oidp, sysctl_oid, oid_link);
354 			removed_oidp = old_oidp;
355 		}
356 #else
357 		panic("Old style sysctl without a version number isn't supported");
358 #endif
359 	} else {
360 		/* It's a later version; handle the versions we know about */
361 		switch (oidp->oid_version) {
362 		case SYSCTL_OID_VERSION:
363 			/* We can just remove the OID directly... */
364 			SLIST_REMOVE(lsp, oidp, sysctl_oid, oid_link);
365 			removed_oidp = oidp;
366 			break;
367 		default:
368 			/* XXX: Can't happen; probably tree coruption.*/
369 			break;                  /* rejects unknown version */
370 		}
371 	}
372 
373 #if defined(HAS_APPLE_PAC)
374 	if (removed_oidp && removed_oidp->oid_handler) {
375 		/*
376 		 * Revert address-discriminated signing performed by
377 		 * sysctl_register_oid() (in case this oid is registered again).
378 		 */
379 		removed_oidp->oid_handler = ptrauth_auth_and_resign(removed_oidp->oid_handler,
380 		    ptrauth_key_function_pointer,
381 		    ptrauth_blend_discriminator(&removed_oidp->oid_handler,
382 		    os_hash_kernel_pointer(removed_oidp->oid_arg1)),
383 		    ptrauth_key_function_pointer,
384 		    ptrauth_function_pointer_type_discriminator(typeof(removed_oidp->oid_handler)));
385 	}
386 #endif /* defined(HAS_APPLE_PAC) */
387 
388 	/*
389 	 * We've removed it from the list at this point, but we don't want
390 	 * to return to the caller until all handler references have drained
391 	 * out.  Doing things in this order prevent other people coming in
392 	 * and starting new operations against the OID node we want removed.
393 	 *
394 	 * Note:	oidp could be NULL if it wasn't found.
395 	 */
396 	while (removed_oidp && removed_oidp->oid_refcnt) {
397 		lck_rw_sleep(&sysctl_geometry_lock, LCK_SLEEP_EXCLUSIVE,
398 		    &removed_oidp->oid_refcnt, THREAD_UNINT);
399 	}
400 
401 	/* Release the write lock */
402 	lck_rw_unlock_exclusive(&sysctl_geometry_lock);
403 
404 #if __x86_64__
405 	/* If it was allocated, free it after dropping the lock */
406 	kfree_type(struct sysctl_oid, old_oidp);
407 #endif
408 }
409 
410 /*
411  * Exported in BSDKernel.exports, kept for binary compatibility
412  */
413 #if defined(__x86_64__)
414 void
sysctl_register_fixed(void)415 sysctl_register_fixed(void)
416 {
417 }
418 #endif
419 
420 /*
421  * New handler interface
422  *   If the sysctl caller (user mode or kernel mode) is interested in the
423  *   value (req->oldptr != NULL), we copy the data (bigValue etc.) out,
424  *   if the caller wants to set the value (req->newptr), we copy
425  *   the data in (*pValue etc.).
426  */
427 
428 int
sysctl_io_number(struct sysctl_req * req,long long bigValue,size_t valueSize,void * pValue,int * changed)429 sysctl_io_number(struct sysctl_req *req, long long bigValue, size_t valueSize, void *pValue, int *changed)
430 {
431 	int             smallValue;
432 	int             error;
433 
434 	if (changed) {
435 		*changed = 0;
436 	}
437 
438 	/*
439 	 * Handle the various combinations of caller buffer size and
440 	 * data value size.  We are generous in the case where the
441 	 * caller has specified a 32-bit buffer but the value is 64-bit
442 	 * sized.
443 	 */
444 
445 	/* 32 bit value expected or 32 bit buffer offered */
446 	if (((valueSize == sizeof(int)) ||
447 	    ((req->oldlen == sizeof(int)) && (valueSize == sizeof(long long))))
448 	    && (req->oldptr)) {
449 		smallValue = (int)bigValue;
450 		if ((long long)smallValue != bigValue) {
451 			return ERANGE;
452 		}
453 		error = SYSCTL_OUT(req, &smallValue, sizeof(smallValue));
454 	} else {
455 		/* any other case is either size-equal or a bug */
456 		error = SYSCTL_OUT(req, &bigValue, valueSize);
457 	}
458 	/* error or nothing to set */
459 	if (error || !req->newptr) {
460 		return error;
461 	}
462 
463 	/* set request for constant */
464 	if (pValue == NULL) {
465 		return EPERM;
466 	}
467 
468 	/* set request needs to convert? */
469 	if ((req->newlen == sizeof(int)) && (valueSize == sizeof(long long))) {
470 		/* new value is 32 bits, upconvert to 64 bits */
471 		error = SYSCTL_IN(req, &smallValue, sizeof(smallValue));
472 		if (!error) {
473 			*(long long *)pValue = (long long)smallValue;
474 		}
475 	} else if ((req->newlen == sizeof(long long)) && (valueSize == sizeof(int))) {
476 		/* new value is 64 bits, downconvert to 32 bits and range check */
477 		error = SYSCTL_IN(req, &bigValue, sizeof(bigValue));
478 		if (!error) {
479 			smallValue = (int)bigValue;
480 			if ((long long)smallValue != bigValue) {
481 				return ERANGE;
482 			}
483 			*(int *)pValue = smallValue;
484 		}
485 	} else {
486 		/* sizes match, just copy in */
487 		error = SYSCTL_IN(req, pValue, valueSize);
488 	}
489 	if (!error && changed) {
490 		*changed = 1;
491 	}
492 	return error;
493 }
494 
495 int
sysctl_io_string(struct sysctl_req * req,char * pValue,size_t valueSize,int trunc,int * changed)496 sysctl_io_string(struct sysctl_req *req, char *pValue, size_t valueSize, int trunc, int *changed)
497 {
498 	int error;
499 	size_t len = strlen(pValue) + 1;
500 
501 	if (changed) {
502 		*changed = 0;
503 	}
504 
505 	if (trunc && req->oldptr && req->oldlen && (req->oldlen < len)) {
506 		/* If trunc != 0, if you give it a too small (but larger than
507 		 * 0 bytes) buffer, instead of returning ENOMEM, it truncates the
508 		 * returned string to the buffer size.  This preserves the semantics
509 		 * of some library routines implemented via sysctl, which truncate
510 		 * their returned data, rather than simply returning an error. The
511 		 * returned string is always nul (ascii '\0') terminated. */
512 		error = SYSCTL_OUT(req, pValue, req->oldlen - 1);
513 		if (!error) {
514 			char c = '\0';
515 			error = SYSCTL_OUT(req, &c, 1);
516 		}
517 	} else {
518 		/* Copy string out */
519 		error = SYSCTL_OUT(req, pValue, len);
520 	}
521 
522 	/* error or no new value */
523 	if (error || !req->newptr) {
524 		return error;
525 	}
526 
527 	/* attempt to set read-only value */
528 	if (valueSize == 0) {
529 		return EPERM;
530 	}
531 
532 	/* make sure there's room for the new string */
533 	if (req->newlen >= valueSize) {
534 		return EINVAL;
535 	}
536 
537 	/* copy the string in and force nul termination */
538 	error = SYSCTL_IN(req, pValue, req->newlen);
539 	pValue[req->newlen] = '\0';
540 
541 	if (!error && changed) {
542 		*changed = 1;
543 	}
544 	return error;
545 }
546 
547 int
sysctl_io_opaque(struct sysctl_req * req,void * pValue,size_t valueSize,int * changed)548 sysctl_io_opaque(struct sysctl_req *req, void *pValue, size_t valueSize, int *changed)
549 {
550 	int error;
551 
552 	if (changed) {
553 		*changed = 0;
554 	}
555 
556 	/* Copy blob out */
557 	error = SYSCTL_OUT(req, pValue, valueSize);
558 
559 	/* error or nothing to set */
560 	if (error || !req->newptr) {
561 		return error;
562 	}
563 
564 	error = SYSCTL_IN(req, pValue, valueSize);
565 
566 	if (!error && changed) {
567 		*changed = 1;
568 	}
569 	return error;
570 }
571 
572 /*
573  * SYSCTL_OID enumerators
574  *
575  * Because system OIDs are immutable, they are composed of 2 lists hanging from
576  * a first dummy OID_MUTABLE_ANCHOR node that has an immutable list hanging from
577  * its `oid_parent` field and a mutable list hanging from its oid_arg1 one.
578  *
579  * Those enumerators abstract away the implicit merging of those two lists in
580  * two possible order:
581  * - oid_number order (which will interleave both sorted lists)
582  * - system order which will list the immutable list first,
583  *   and the mutable list second.
584  */
585 struct sysctl_oid_iterator {
586 	struct sysctl_oid *a;
587 	struct sysctl_oid *b;
588 };
589 
590 static struct sysctl_oid_iterator
sysctl_oid_iterator_begin(struct sysctl_oid_list * l)591 sysctl_oid_iterator_begin(struct sysctl_oid_list *l)
592 {
593 	struct sysctl_oid_iterator it = { };
594 	struct sysctl_oid *a = SLIST_FIRST(l);
595 
596 	if (a == NULL) {
597 		return it;
598 	}
599 
600 	if (a->oid_number == OID_MUTABLE_ANCHOR) {
601 		it.a = SLIST_NEXT(a, oid_link);
602 		it.b = SLIST_FIRST((struct sysctl_oid_list *)a->oid_arg1);
603 	} else {
604 		it.a = a;
605 	}
606 	return it;
607 }
608 
609 static struct sysctl_oid *
sysctl_oid_iterator_next_num_order(struct sysctl_oid_iterator * it)610 sysctl_oid_iterator_next_num_order(struct sysctl_oid_iterator *it)
611 {
612 	struct sysctl_oid *a = it->a;
613 	struct sysctl_oid *b = it->b;
614 
615 	if (a == NULL && b == NULL) {
616 		return NULL;
617 	}
618 
619 	if (a == NULL) {
620 		it->b = SLIST_NEXT(b, oid_link);
621 		return b;
622 	}
623 
624 	if (b == NULL || a->oid_number <= b->oid_number) {
625 		it->a = SLIST_NEXT(a, oid_link);
626 		return a;
627 	}
628 
629 	it->b = SLIST_NEXT(b, oid_link);
630 	return b;
631 }
632 
633 #define SYSCTL_OID_FOREACH_NUM_ORDER(oidp, l) \
634 	for (struct sysctl_oid_iterator it = sysctl_oid_iterator_begin(l); \
635 	        ((oidp) = sysctl_oid_iterator_next_num_order(&it)); )
636 
637 static struct sysctl_oid *
sysctl_oid_iterator_next_system_order(struct sysctl_oid_iterator * it)638 sysctl_oid_iterator_next_system_order(struct sysctl_oid_iterator *it)
639 {
640 	struct sysctl_oid *a = it->a;
641 	struct sysctl_oid *b = it->b;
642 
643 	if (a) {
644 		it->a = SLIST_NEXT(a, oid_link);
645 		return a;
646 	}
647 
648 	if (b) {
649 		it->b = SLIST_NEXT(b, oid_link);
650 		return b;
651 	}
652 
653 	return NULL;
654 }
655 
656 #define SYSCTL_OID_FOREACH_SYS_ORDER(oidp, l) \
657 	for (struct sysctl_oid_iterator it = sysctl_oid_iterator_begin(l); \
658 	        ((oidp) = sysctl_oid_iterator_next_system_order(&it)); )
659 
660 /*
661  * "Staff-functions"
662  *
663  * These functions implement a presently undocumented interface
664  * used by the sysctl program to walk the tree, and get the type
665  * so it can print the value.
666  * This interface is under work and consideration, and should probably
667  * be killed with a big axe by the first person who can find the time.
668  * (be aware though, that the proper interface isn't as obvious as it
669  * may seem, there are various conflicting requirements.
670  *
671  * {0,0}	printf the entire MIB-tree.
672  * {0,1,...}	return the name of the "..." OID.
673  * {0,2,...}	return the next OID.
674  * {0,3}	return the OID of the name in "new"
675  * {0,4,...}	return the kind & format info for the "..." OID.
676  */
677 
678 /*
679  * sysctl_sysctl_debug_dump_node
680  *
681  * Description:	Dump debug information for a given sysctl_oid_list at the
682  *		given oid depth out to the kernel log, via printf
683  *
684  * Parameters:	l				sysctl_oid_list pointer
685  *		i				current node depth
686  *
687  * Returns:	(void)
688  *
689  * Implicit:	kernel log, modified
690  *
691  * Locks:	Assumes sysctl_geometry_lock is held prior to calling
692  *
693  * Notes:	This function may call itself recursively to resolve Node
694  *		values, which potentially have an inferioer sysctl_oid_list
695  *
696  *		This function is only callable indirectly via the function
697  *		sysctl_sysctl_debug()
698  *
699  * Bugs:	The node depth indentation does not work; this may be an
700  *		artifact of leading space removal by the log daemon itself
701  *		or some intermediate routine.
702  */
703 STATIC void
sysctl_sysctl_debug_dump_node(struct sysctl_oid_list * l,int i)704 sysctl_sysctl_debug_dump_node(struct sysctl_oid_list *l, int i)
705 {
706 	struct sysctl_oid *oidp;
707 	struct sysctl_oid_list *lp;
708 	const char *what;
709 
710 	SYSCTL_OID_FOREACH_SYS_ORDER(oidp, l) {
711 		switch (oidp->oid_kind & CTLTYPE) {
712 		case CTLTYPE_NODE:
713 			lp = oidp->oid_arg1;
714 			what = "Node   ";
715 			if (lp && SLIST_FIRST(lp) &&
716 			    SLIST_FIRST(lp)->oid_number == OID_MUTABLE_ANCHOR) {
717 				what = "NodeExt";
718 			} else {
719 			}
720 			break;
721 		case CTLTYPE_INT:
722 			what = "Int    ";
723 			break;
724 		case CTLTYPE_STRING:
725 			what = "String ";
726 			break;
727 		case CTLTYPE_QUAD:
728 			what = "Quad   ";
729 			break;
730 		case CTLTYPE_OPAQUE:
731 			what = "Opaque ";
732 			break;
733 		default:
734 			what = "Unknown";
735 			break;
736 		}
737 
738 		printf("%*s%-3d[%c%c%c%c%c] %s %s\n", i, "", oidp->oid_number,
739 		    oidp->oid_kind & CTLFLAG_LOCKED ? 'L':' ',
740 		    oidp->oid_kind & CTLFLAG_RD ? 'R':' ',
741 		    oidp->oid_kind & CTLFLAG_WR ? 'W':' ',
742 		    oidp->oid_kind & CTLFLAG_PERMANENT ? ' ':'*',
743 		    oidp->oid_handler ? 'h' : ' ',
744 		    what, oidp->oid_name);
745 
746 		if ((oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
747 			if (!oidp->oid_handler) {
748 				sysctl_sysctl_debug_dump_node(lp, i + 2);
749 			}
750 		}
751 	}
752 }
753 
754 /*
755  * sysctl_sysctl_debug
756  *
757  * Description:	This function implements the "sysctl.debug" portion of the
758  *		OID space for sysctl.
759  *
760  * OID:		0, 0
761  *
762  * Parameters:	__unused
763  *
764  * Returns:	ENOENT
765  *
766  * Implicit:	kernel log, modified
767  *
768  * Locks:	Acquires and then releases a read lock on the
769  *		sysctl_geometry_lock
770  */
771 STATIC int
sysctl_sysctl_debug(__unused struct sysctl_oid * oidp,__unused void * arg1,__unused int arg2,__unused struct sysctl_req * req)772 sysctl_sysctl_debug(__unused struct sysctl_oid *oidp, __unused void *arg1,
773     __unused int arg2, __unused struct sysctl_req *req)
774 {
775 	lck_rw_lock_shared(&sysctl_geometry_lock);
776 	sysctl_sysctl_debug_dump_node(&sysctl__children, 0);
777 	lck_rw_done(&sysctl_geometry_lock);
778 	return ENOENT;
779 }
780 
781 SYSCTL_PROC(_sysctl, 0, debug, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_LOCKED,
782     0, 0, sysctl_sysctl_debug, "-", "");
783 
784 /*
785  * sysctl_sysctl_name
786  *
787  * Description:	Convert an OID into a string name; this is used by the user
788  *		space sysctl() command line utility; this is done in a purely
789  *		advisory capacity (e.g. to provide node names for "sysctl -A"
790  *		output).
791  *
792  * OID:		0, 1
793  *
794  * Parameters:	oidp				__unused
795  *		arg1				A pointer to the OID name list
796  *						integer array, beginning at
797  *						adjusted option base 2
798  *		arg2				The number of elements which
799  *						remain in the name array
800  *
801  * Returns:	0				Success
802  *	SYSCTL_OUT:EPERM			Permission denied
803  *	SYSCTL_OUT:EFAULT			Bad user supplied buffer
804  *	SYSCTL_OUT:???				Return value from user function
805  *						for SYSCTL_PROC leaf node
806  *
807  * Implict:	Contents of user request buffer, modified
808  *
809  * Locks:	Acquires and then releases a read lock on the
810  *		sysctl_geometry_lock
811  *
812  * Notes:	SPI (System Programming Interface); this is subject to change
813  *		and may not be relied upon by third party applications; use
814  *		a subprocess to communicate with the "sysctl" command line
815  *		command instead, if you believe you need this functionality.
816  *		Preferrably, use sysctlbyname() instead.
817  *
818  *		Setting of the NULL termination of the output string is
819  *		delayed until after the geometry lock is dropped.  If there
820  *		are no Entries remaining in the OID name list when this
821  *		function is called, it will still write out the termination
822  *		byte.
823  *
824  *		This function differs from other sysctl functions in that
825  *		it can not take an output buffer length of 0 to determine the
826  *		space which will be required.  It is suggested that the buffer
827  *		length be PATH_MAX, and that authors of new sysctl's refrain
828  *		from exceeding this string length.
829  */
830 STATIC int
sysctl_sysctl_name(__unused struct sysctl_oid * oidp,void * arg1,int arg2,struct sysctl_req * req)831 sysctl_sysctl_name(__unused struct sysctl_oid *oidp, void *arg1, int arg2,
832     struct sysctl_req *req)
833 {
834 	int *name = (int *) arg1;
835 	u_int namelen = arg2;
836 	int error = 0;
837 	struct sysctl_oid *oid;
838 	struct sysctl_oid_list *lsp = &sysctl__children, *lsp2;
839 	char tempbuf[10] = {};
840 
841 	lck_rw_lock_shared(&sysctl_geometry_lock);
842 	while (namelen) {
843 		if (!lsp) {
844 			snprintf(tempbuf, sizeof(tempbuf), "%d", *name);
845 			if (req->oldidx) {
846 				error = SYSCTL_OUT(req, ".", 1);
847 			}
848 			if (!error) {
849 				error = SYSCTL_OUT(req, tempbuf, strlen(tempbuf));
850 			}
851 			if (error) {
852 				lck_rw_done(&sysctl_geometry_lock);
853 				return error;
854 			}
855 			namelen--;
856 			name++;
857 			continue;
858 		}
859 		lsp2 = 0;
860 		SYSCTL_OID_FOREACH_NUM_ORDER(oid, lsp) {
861 			if (oid->oid_number != *name) {
862 				continue;
863 			}
864 
865 			if (req->oldidx) {
866 				error = SYSCTL_OUT(req, ".", 1);
867 			}
868 			if (!error) {
869 				error = SYSCTL_OUT(req, oid->oid_name,
870 				    strlen(oid->oid_name));
871 			}
872 			if (error) {
873 				lck_rw_done(&sysctl_geometry_lock);
874 				return error;
875 			}
876 
877 			namelen--;
878 			name++;
879 
880 			if ((oid->oid_kind & CTLTYPE) != CTLTYPE_NODE) {
881 				break;
882 			}
883 
884 			if (oid->oid_handler) {
885 				break;
886 			}
887 
888 			lsp2 = (struct sysctl_oid_list *)oid->oid_arg1;
889 			break;
890 		}
891 		lsp = lsp2;
892 	}
893 	lck_rw_done(&sysctl_geometry_lock);
894 	return SYSCTL_OUT(req, "", 1);
895 }
896 
897 SYSCTL_NODE(_sysctl, 1, name, CTLFLAG_RD | CTLFLAG_LOCKED, sysctl_sysctl_name, "");
898 
899 /*
900  * sysctl_sysctl_next_ls
901  *
902  * Description:	For a given OID name value, return the next consecutive OID
903  *		name value within the geometry tree
904  *
905  * Parameters:	lsp				The OID list to look in
906  *		name				The OID name to start from
907  *		namelen				The length of the OID name
908  *		next				Pointer to new oid storage to
909  *						fill in
910  *		len				Pointer to receive new OID
911  *						length value of storage written
912  *		level				OID tree depth (used to compute
913  *						len value)
914  *		oidpp				Pointer to OID list entry
915  *						pointer; used to walk the list
916  *						forward across recursion
917  *
918  * Returns:	0				Returning a new entry
919  *		1				End of geometry list reached
920  *
921  * Implicit:	*next				Modified to contain the new OID
922  *		*len				Modified to contain new length
923  *
924  * Locks:	Assumes sysctl_geometry_lock is held prior to calling
925  *
926  * Notes:	This function will not return OID values that have special
927  *		handlers, since we can not tell wheter these handlers consume
928  *		elements from the OID space as parameters.  For this reason,
929  *		we STRONGLY discourage these types of handlers
930  */
931 STATIC int
sysctl_sysctl_next_ls(struct sysctl_oid_list * lsp,int * name,u_int namelen,int * next,int * len,int level,struct sysctl_oid ** oidpp)932 sysctl_sysctl_next_ls(struct sysctl_oid_list *lsp, int *name, u_int namelen,
933     int *next, int *len, int level, struct sysctl_oid **oidpp)
934 {
935 	struct sysctl_oid *oidp;
936 
937 	*len = level;
938 	SYSCTL_OID_FOREACH_NUM_ORDER(oidp, lsp) {
939 		*next = oidp->oid_number;
940 		*oidpp = oidp;
941 
942 		if (!namelen) {
943 			if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) {
944 				return 0;
945 			}
946 			if (oidp->oid_handler) {
947 				/* We really should call the handler here...*/
948 				return 0;
949 			}
950 			lsp = (struct sysctl_oid_list *)oidp->oid_arg1;
951 
952 			if (!SLIST_FIRST(lsp)) {
953 				/* This node had no children - skip it! */
954 				continue;
955 			}
956 
957 			if (!sysctl_sysctl_next_ls(lsp, 0, 0, next + 1,
958 			    len, level + 1, oidpp)) {
959 				return 0;
960 			}
961 			goto next;
962 		}
963 
964 		if (oidp->oid_number < *name) {
965 			continue;
966 		}
967 
968 		if (oidp->oid_number > *name) {
969 			if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) {
970 				return 0;
971 			}
972 			if (oidp->oid_handler) {
973 				return 0;
974 			}
975 			lsp = (struct sysctl_oid_list *)oidp->oid_arg1;
976 			if (!sysctl_sysctl_next_ls(lsp, name + 1, namelen - 1,
977 			    next + 1, len, level + 1, oidpp)) {
978 				return 0;
979 			}
980 			goto next;
981 		}
982 		if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) {
983 			continue;
984 		}
985 
986 		if (oidp->oid_handler) {
987 			continue;
988 		}
989 
990 		lsp = (struct sysctl_oid_list *)oidp->oid_arg1;
991 		if (!sysctl_sysctl_next_ls(lsp, name + 1, namelen - 1, next + 1,
992 		    len, level + 1, oidpp)) {
993 			return 0;
994 		}
995 next:
996 		/* We expect to be reducing namelen here, don't reset to 1 if this
997 		 * is actually an increase.
998 		 */
999 		if (namelen > 1) {
1000 			namelen = 1;
1001 		}
1002 		*len = level;
1003 	}
1004 	return 1;
1005 }
1006 
1007 /*
1008  * sysctl_sysctl_next
1009  *
1010  * Description:	This is an iterator function designed to iterate the oid tree
1011  *		and provide a list of OIDs for use by the user space "sysctl"
1012  *		command line tool
1013  *
1014  * OID:		0, 2
1015  *
1016  * Parameters:	oidp				__unused
1017  *		arg1				Pointer to start OID name
1018  *		arg2				Start OID name length
1019  *		req				Pointer to user request buffer
1020  *
1021  * Returns:	0				Success
1022  *		ENOENT				Reached end of OID space
1023  *	SYSCTL_OUT:EPERM			Permission denied
1024  *	SYSCTL_OUT:EFAULT			Bad user supplied buffer
1025  *	SYSCTL_OUT:???				Return value from user function
1026  *						for SYSCTL_PROC leaf node
1027  *
1028  * Implict:	Contents of user request buffer, modified
1029  *
1030  * Locks:	Acquires and then releases a read lock on the
1031  *		sysctl_geometry_lock
1032  *
1033  * Notes:	SPI (System Programming Interface); this is subject to change
1034  *		and may not be relied upon by third party applications; use
1035  *		a subprocess to communicate with the "sysctl" command line
1036  *		command instead, if you believe you need this functionality.
1037  *		Preferrably, use sysctlbyname() instead.
1038  *
1039  *		This function differs from other sysctl functions in that
1040  *		it can not take an output buffer length of 0 to determine the
1041  *		space which will be required.  It is suggested that the buffer
1042  *		length be PATH_MAX, and that authors of new sysctl's refrain
1043  *		from exceeding this string length.
1044  */
1045 STATIC int
sysctl_sysctl_next(__unused struct sysctl_oid * oidp,void * arg1,int arg2,struct sysctl_req * req)1046 sysctl_sysctl_next(__unused struct sysctl_oid *oidp, void *arg1, int arg2,
1047     struct sysctl_req *req)
1048 {
1049 	int *name = (int *) arg1;
1050 	u_int namelen = arg2;
1051 	int i, j, error;
1052 	struct sysctl_oid *oid;
1053 	struct sysctl_oid_list *lsp = &sysctl__children;
1054 	int newoid[CTL_MAXNAME] = {};
1055 
1056 	lck_rw_lock_shared(&sysctl_geometry_lock);
1057 	i = sysctl_sysctl_next_ls(lsp, name, namelen, newoid, &j, 1, &oid);
1058 	lck_rw_done(&sysctl_geometry_lock);
1059 	if (i) {
1060 		return ENOENT;
1061 	}
1062 	error = SYSCTL_OUT(req, newoid, j * sizeof(int));
1063 	return error;
1064 }
1065 
1066 SYSCTL_NODE(_sysctl, 2, next, CTLFLAG_RD | CTLFLAG_LOCKED, sysctl_sysctl_next, "");
1067 
1068 /*
1069  * name2oid
1070  *
1071  * Description:	Support function for use by sysctl_sysctl_name2oid(); looks
1072  *		up an OID name given a string name.
1073  *
1074  * Parameters:	name				NULL terminated string name
1075  *		oid				Pointer to receive OID name
1076  *		len				Pointer to receive OID length
1077  *						pointer value (see "Notes")
1078  *
1079  * Returns:	0				Success
1080  *		ENOENT				Entry not found
1081  *
1082  * Implicit:	*oid				Modified to contain OID value
1083  *		*len				Modified to contain OID length
1084  *
1085  * Locks:	Assumes sysctl_geometry_lock is held prior to calling
1086  */
1087 STATIC int
name2oid(char * name,int * oid,size_t * len)1088 name2oid(char *name, int *oid, size_t *len)
1089 {
1090 	struct sysctl_oid_iterator it;
1091 	struct sysctl_oid *oidp;
1092 	char *p;
1093 	char i;
1094 
1095 	if (!*name) {
1096 		return ENOENT;
1097 	}
1098 
1099 	p = name + strlen(name) - 1;
1100 	if (*p == '.') {
1101 		*p = '\0';
1102 	}
1103 
1104 	*len = 0;
1105 
1106 	for (p = name; *p && *p != '.'; p++) {
1107 		;
1108 	}
1109 	i = *p;
1110 	if (i == '.') {
1111 		*p = '\0';
1112 	}
1113 
1114 	it = sysctl_oid_iterator_begin(&sysctl__children);
1115 	oidp = sysctl_oid_iterator_next_system_order(&it);
1116 
1117 	while (oidp && *len < CTL_MAXNAME) {
1118 		if (strcmp(name, oidp->oid_name)) {
1119 			oidp = sysctl_oid_iterator_next_system_order(&it);
1120 			continue;
1121 		}
1122 		*oid++ = oidp->oid_number;
1123 		(*len)++;
1124 
1125 		if (i == '\0') {
1126 			return 0;
1127 		}
1128 
1129 		if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) {
1130 			break;
1131 		}
1132 
1133 		if (oidp->oid_handler) {
1134 			break;
1135 		}
1136 
1137 		it = sysctl_oid_iterator_begin(oidp->oid_arg1);
1138 		oidp = sysctl_oid_iterator_next_system_order(&it);
1139 
1140 		*p = i; /* restore */
1141 		name = p + 1;
1142 		for (p = name; *p && *p != '.'; p++) {
1143 			;
1144 		}
1145 		i = *p;
1146 		if (i == '.') {
1147 			*p = '\0';
1148 		}
1149 	}
1150 	return ENOENT;
1151 }
1152 
1153 /*
1154  * sysctl_sysctl_name2oid
1155  *
1156  * Description:	Translate a string name to an OID name value; this is used by
1157  *		the sysctlbyname() function as well as by the "sysctl" command
1158  *		line command.
1159  *
1160  * OID:		0, 3
1161  *
1162  * Parameters:	oidp				__unused
1163  *		arg1				__unused
1164  *		arg2				__unused
1165  *		req				Request structure
1166  *
1167  * Returns:	ENOENT				Input length too short
1168  *		ENAMETOOLONG			Input length too long
1169  *		ENOMEM				Could not allocate work area
1170  *	SYSCTL_IN/OUT:EPERM			Permission denied
1171  *	SYSCTL_IN/OUT:EFAULT			Bad user supplied buffer
1172  *	SYSCTL_IN/OUT:???			Return value from user function
1173  *	name2oid:ENOENT				Not found
1174  *
1175  * Implicit:	*req				Contents of request, modified
1176  *
1177  * Locks:	Acquires and then releases a read lock on the
1178  *		sysctl_geometry_lock
1179  *
1180  * Notes:	SPI (System Programming Interface); this is subject to change
1181  *		and may not be relied upon by third party applications; use
1182  *		a subprocess to communicate with the "sysctl" command line
1183  *		command instead, if you believe you need this functionality.
1184  *		Preferrably, use sysctlbyname() instead.
1185  *
1186  *		This function differs from other sysctl functions in that
1187  *		it can not take an output buffer length of 0 to determine the
1188  *		space which will be required.  It is suggested that the buffer
1189  *		length be PATH_MAX, and that authors of new sysctl's refrain
1190  *		from exceeding this string length.
1191  */
1192 STATIC int
sysctl_sysctl_name2oid(__unused struct sysctl_oid * oidp,__unused void * arg1,__unused int arg2,struct sysctl_req * req)1193 sysctl_sysctl_name2oid(__unused struct sysctl_oid *oidp, __unused void *arg1,
1194     __unused int arg2, struct sysctl_req *req)
1195 {
1196 	char *p;
1197 	int error, oid[CTL_MAXNAME] = {};
1198 	size_t len = 0;          /* set by name2oid() */
1199 
1200 	if (req->newlen < 1) {
1201 		return ENOENT;
1202 	}
1203 	if (req->newlen >= MAXPATHLEN) { /* XXX arbitrary, undocumented */
1204 		return ENAMETOOLONG;
1205 	}
1206 
1207 	p = (char *)kalloc_data(req->newlen + 1, Z_WAITOK);
1208 	if (!p) {
1209 		return ENOMEM;
1210 	}
1211 
1212 	error = SYSCTL_IN(req, p, req->newlen);
1213 	if (error) {
1214 		kfree_data(p, req->newlen + 1);
1215 		return error;
1216 	}
1217 
1218 	p[req->newlen] = '\0';
1219 
1220 	/*
1221 	 * Note:	We acquire and release the geometry lock here to
1222 	 *		avoid making name2oid needlessly complex.
1223 	 */
1224 	lck_rw_lock_shared(&sysctl_geometry_lock);
1225 	error = name2oid(p, oid, &len);
1226 	lck_rw_done(&sysctl_geometry_lock);
1227 
1228 	kfree_data(p, req->newlen + 1);
1229 
1230 	if (error) {
1231 		return error;
1232 	}
1233 
1234 	error = SYSCTL_OUT(req, oid, len * sizeof *oid);
1235 	return error;
1236 }
1237 
1238 SYSCTL_PROC(_sysctl, 3, name2oid, CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_KERN | CTLFLAG_LOCKED, 0, 0,
1239     sysctl_sysctl_name2oid, "I", "");
1240 
1241 /*
1242  * find_oid_by_name
1243  *
1244  * Description: Support function for use by sysctl_sysctl_oidfmt() and
1245  *		sysctl_sysctl_oiddescr()); looks up an OID given an
1246  *		OID name.
1247  *
1248  * Parameters:	name				A pointer to the OID name list
1249  *						integer array
1250  *		namelen				The length of the OID name
1251  *		oidp				Pointer to receive OID pointer
1252  *
1253  * Returns:	0				Success
1254  *		ENOENT				Entry not found
1255  *		EISDIR				Malformed request
1256  *
1257  * Implicit:	*oidp				Modified to contain pointer to
1258  *                                              the OID, or NULL if not found
1259  *
1260  * Locks:	Assumes sysctl_geometry_lock is held prior to calling
1261  */
1262 STATIC int
find_oid_by_name(int * name,u_int namelen,struct sysctl_oid ** oidp)1263 find_oid_by_name(int *name, u_int namelen, struct sysctl_oid **oidp)
1264 {
1265 	LCK_RW_ASSERT(&sysctl_geometry_lock, LCK_RW_ASSERT_SHARED);
1266 
1267 	struct sysctl_oid_iterator it = sysctl_oid_iterator_begin(&sysctl__children);
1268 	struct sysctl_oid *oid = sysctl_oid_iterator_next_system_order(&it);
1269 
1270 	u_int indx = 0;
1271 
1272 	*oidp = NULL;
1273 
1274 	while (oid && indx < CTL_MAXNAME) {
1275 		if (oid->oid_number == name[indx]) {
1276 			indx++;
1277 			if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
1278 				if (oid->oid_handler) {
1279 					goto found;
1280 				}
1281 				if (indx == namelen) {
1282 					goto found;
1283 				}
1284 				it = sysctl_oid_iterator_begin(oid->oid_arg1);
1285 				oid = sysctl_oid_iterator_next_system_order(&it);
1286 			} else {
1287 				if (indx != namelen) {
1288 					return EISDIR;
1289 				}
1290 				goto found;
1291 			}
1292 		} else {
1293 			oid = sysctl_oid_iterator_next_system_order(&it);
1294 		}
1295 	}
1296 
1297 	return ENOENT;
1298 
1299 found:
1300 	*oidp = oid;
1301 	return 0;
1302 }
1303 
1304 /*
1305  * sysctl_sysctl_oidfmt
1306  *
1307  * Description:	For a given OID name, determine the format of the data which
1308  *		is associated with it.  This is used by the "sysctl" command
1309  *		line command.
1310  *
1311  * OID:		0, 4
1312  *
1313  * Parameters:	oidp				__unused
1314  *		arg1				The OID name to look up
1315  *		arg2				The length of the OID name
1316  *		req				Pointer to user request buffer
1317  *
1318  * Returns:	0				Success
1319  *		EISDIR				Malformed request
1320  *		ENOENT				No such OID name
1321  *	SYSCTL_OUT:EPERM			Permission denied
1322  *	SYSCTL_OUT:EFAULT			Bad user supplied buffer
1323  *	SYSCTL_OUT:???				Return value from user function
1324  *
1325  * Implict:	Contents of user request buffer, modified
1326  *
1327  * Locks:	Acquires and then releases a read lock on the
1328  *		sysctl_geometry_lock
1329  *
1330  * Notes:	SPI (System Programming Interface); this is subject to change
1331  *		and may not be relied upon by third party applications; use
1332  *		a subprocess to communicate with the "sysctl" command line
1333  *		command instead, if you believe you need this functionality.
1334  *
1335  *		This function differs from other sysctl functions in that
1336  *		it can not take an output buffer length of 0 to determine the
1337  *		space which will be required.  It is suggested that the buffer
1338  *		length be PATH_MAX, and that authors of new sysctl's refrain
1339  *		from exceeding this string length.
1340  */
1341 STATIC int
sysctl_sysctl_oidfmt(__unused struct sysctl_oid * oidp,void * arg1,int arg2,struct sysctl_req * req)1342 sysctl_sysctl_oidfmt(__unused struct sysctl_oid *oidp, void *arg1, int arg2,
1343     struct sysctl_req *req)
1344 {
1345 	int *name = (int *) arg1;
1346 	int error;
1347 	u_int namelen = arg2;
1348 	struct sysctl_oid *oid;
1349 
1350 	lck_rw_lock_shared(&sysctl_geometry_lock);
1351 
1352 	error = find_oid_by_name(name, namelen, &oid);
1353 	if (error) {
1354 		goto err;
1355 	}
1356 
1357 	if (!oid->oid_fmt) {
1358 		error = ENOENT;
1359 		goto err;
1360 	}
1361 
1362 	error = SYSCTL_OUT(req,
1363 	    &oid->oid_kind, sizeof(oid->oid_kind));
1364 	if (!error) {
1365 		error = SYSCTL_OUT(req, oid->oid_fmt,
1366 		    strlen(oid->oid_fmt) + 1);
1367 	}
1368 
1369 err:
1370 	lck_rw_unlock_shared(&sysctl_geometry_lock);
1371 	return error;
1372 }
1373 
1374 SYSCTL_NODE(_sysctl, 4, oidfmt, CTLFLAG_RD | CTLFLAG_LOCKED, sysctl_sysctl_oidfmt, "");
1375 
1376 /*
1377  * sysctl_sysctl_oiddescr
1378  *
1379  * Description: For a given OID name, determine the description of the
1380  *		data which is associated with it.  This is used by the
1381  *		"sysctl" command line command.
1382  *
1383  * OID:		0, 5
1384  *
1385  * Parameters:	oidp				__unused
1386  *		arg1				The OID name to look up
1387  *		arg2				The length of the OID name
1388  *		req				Pointer to user request buffer
1389  *
1390  * Returns:	0				Success
1391  *		EISDIR				Malformed request
1392  *		ENOENT				No such OID name
1393  *	SYSCTL_OUT:EPERM			Permission denied
1394  *	SYSCTL_OUT:EFAULT			Bad user supplied buffer
1395  *	SYSCTL_OUT:???				Return value from user function
1396  *
1397  * Implict:	Contents of user request buffer, modified
1398  *
1399  * Locks:	Acquires and then releases a read lock on the
1400  *		sysctl_geometry_lock
1401  *
1402  * Notes:	SPI (System Programming Interface); this is subject to change
1403  *		and may not be relied upon by third party applications; use
1404  *		a subprocess to communicate with the "sysctl" command line
1405  *		command instead, if you believe you need this functionality.
1406  *
1407  *		This function differs from other sysctl functions in that
1408  *		it can not take an output buffer length of 0 to determine the
1409  *		space which will be required.  It is suggested that the buffer
1410  *		length be PATH_MAX, and that authors of new sysctl's refrain
1411  *		from exceeding this string length.
1412  */
1413 STATIC int
sysctl_sysctl_oiddescr(__unused struct sysctl_oid * oidp,void * arg1,int arg2,struct sysctl_req * req)1414 sysctl_sysctl_oiddescr(__unused struct sysctl_oid *oidp, void *arg1, int arg2,
1415     struct sysctl_req *req)
1416 {
1417 	int *name = (int *) arg1;
1418 	int error;
1419 	u_int namelen = arg2;
1420 	struct sysctl_oid *oid;
1421 
1422 	lck_rw_lock_shared(&sysctl_geometry_lock);
1423 
1424 	error = find_oid_by_name(name, namelen, &oid);
1425 	if (error) {
1426 		goto err;
1427 	}
1428 
1429 	if (!oid->oid_descr) {
1430 		error = ENOENT;
1431 		goto err;
1432 	}
1433 
1434 	error = SYSCTL_OUT(req, oid->oid_descr, strlen(oid->oid_descr) + 1);
1435 err:
1436 	lck_rw_unlock_shared(&sysctl_geometry_lock);
1437 	return error;
1438 }
1439 
1440 SYSCTL_NODE(_sysctl, 5, oiddescr, CTLFLAG_RD | CTLFLAG_LOCKED, sysctl_sysctl_oiddescr, "");
1441 
1442 
1443 /*
1444  * Default "handler" functions.
1445  */
1446 
1447 /*
1448  * Handle an int, signed or unsigned.
1449  * Two cases:
1450  *     a variable:  point arg1 at it.
1451  *     a constant:  pass it in arg2.
1452  */
1453 
1454 int
sysctl_handle_int(__unused struct sysctl_oid * oidp,void * arg1,int arg2,struct sysctl_req * req)1455 sysctl_handle_int(__unused struct sysctl_oid *oidp, void *arg1, int arg2,
1456     struct sysctl_req *req)
1457 {
1458 	return sysctl_io_number(req, arg1? *(int*)arg1: arg2, sizeof(int), arg1, NULL);
1459 }
1460 
1461 /*
1462  * Handle a long, signed or unsigned.  arg1 points to it.
1463  */
1464 
1465 int
sysctl_handle_long(__unused struct sysctl_oid * oidp,void * arg1,__unused int arg2,struct sysctl_req * req)1466 sysctl_handle_long(__unused struct sysctl_oid *oidp, void *arg1,
1467     __unused int arg2, struct sysctl_req *req)
1468 {
1469 	if (!arg1) {
1470 		return EINVAL;
1471 	}
1472 	return sysctl_io_number(req, *(long*)arg1, sizeof(long), arg1, NULL);
1473 }
1474 
1475 /*
1476  * Handle a quad, signed or unsigned.  arg1 points to it.
1477  */
1478 
1479 int
sysctl_handle_quad(__unused struct sysctl_oid * oidp,void * arg1,__unused int arg2,struct sysctl_req * req)1480 sysctl_handle_quad(__unused struct sysctl_oid *oidp, void *arg1,
1481     __unused int arg2, struct sysctl_req *req)
1482 {
1483 	if (!arg1) {
1484 		return EINVAL;
1485 	}
1486 	return sysctl_io_number(req, *(long long*)arg1, sizeof(long long), arg1, NULL);
1487 }
1488 
1489 /*
1490  * Expose an int value as a quad.
1491  *
1492  * This interface allows us to support interfaces defined
1493  * as using quad values while the implementation is still
1494  * using ints.
1495  */
1496 int
sysctl_handle_int2quad(__unused struct sysctl_oid * oidp,void * arg1,__unused int arg2,struct sysctl_req * req)1497 sysctl_handle_int2quad(__unused struct sysctl_oid *oidp, void *arg1,
1498     __unused int arg2, struct sysctl_req *req)
1499 {
1500 	int error = 0;
1501 	long long val;
1502 	int newval;
1503 
1504 	if (!arg1) {
1505 		return EINVAL;
1506 	}
1507 	val = (long long)*(int *)arg1;
1508 	error = SYSCTL_OUT(req, &val, sizeof(long long));
1509 
1510 	if (error || !req->newptr) {
1511 		return error;
1512 	}
1513 
1514 	error = SYSCTL_IN(req, &val, sizeof(long long));
1515 	if (!error) {
1516 		/*
1517 		 * Value must be representable; check by
1518 		 * casting and then casting back.
1519 		 */
1520 		newval = (int)val;
1521 		if ((long long)newval != val) {
1522 			error = ERANGE;
1523 		} else {
1524 			*(int *)arg1 = newval;
1525 		}
1526 	}
1527 	return error;
1528 }
1529 
1530 /*
1531  * Handle our generic '\0' terminated 'C' string.
1532  * Two cases:
1533  *      a variable string:  point arg1 at it, arg2 is max length.
1534  *      a constant string:  point arg1 at it, arg2 is zero.
1535  */
1536 
1537 int
sysctl_handle_string(__unused struct sysctl_oid * oidp,void * arg1,int arg2,struct sysctl_req * req)1538 sysctl_handle_string( __unused struct sysctl_oid *oidp, void *arg1, int arg2,
1539     struct sysctl_req *req)
1540 {
1541 	return sysctl_io_string(req, arg1, arg2, 0, NULL);
1542 }
1543 
1544 /*
1545  * Handle any kind of opaque data.
1546  * arg1 points to it, arg2 is the size.
1547  */
1548 
1549 int
sysctl_handle_opaque(__unused struct sysctl_oid * oidp,void * arg1,int arg2,struct sysctl_req * req)1550 sysctl_handle_opaque(__unused struct sysctl_oid *oidp, void *arg1, int arg2,
1551     struct sysctl_req *req)
1552 {
1553 	return sysctl_io_opaque(req, arg1, arg2, NULL);
1554 }
1555 
1556 /*
1557  * Transfer functions to/from kernel space.
1558  */
1559 STATIC int
sysctl_old_kernel(struct sysctl_req * req,const void * p,size_t l)1560 sysctl_old_kernel(struct sysctl_req *req, const void *p, size_t l)
1561 {
1562 	size_t i = 0;
1563 
1564 	if (req->oldptr) {
1565 		i = l;
1566 		if (i > req->oldlen - req->oldidx) {
1567 			i = req->oldlen - req->oldidx;
1568 		}
1569 		if (i > 0) {
1570 			bcopy((const void*)p, CAST_DOWN(char *, (req->oldptr + req->oldidx)), i);
1571 		}
1572 	}
1573 	req->oldidx += l;
1574 	if (req->oldptr && i != l) {
1575 		return ENOMEM;
1576 	}
1577 	return 0;
1578 }
1579 
1580 STATIC int
sysctl_new_kernel(struct sysctl_req * req,void * p,size_t l)1581 sysctl_new_kernel(struct sysctl_req *req, void *p, size_t l)
1582 {
1583 	if (!req->newptr) {
1584 		return 0;
1585 	}
1586 	if (req->newlen - req->newidx < l) {
1587 		return EINVAL;
1588 	}
1589 	bcopy(CAST_DOWN(char *, (req->newptr + req->newidx)), p, l);
1590 	req->newidx += l;
1591 	return 0;
1592 }
1593 
1594 int
kernel_sysctl(struct proc * p,int * name,size_t namelen,void * old,size_t * oldlenp,void * new,size_t newlen)1595 kernel_sysctl(struct proc *p, int *name, size_t namelen, void *old, size_t *oldlenp, void *new, size_t newlen)
1596 {
1597 	int error = 0;
1598 	struct sysctl_req req;
1599 
1600 	/*
1601 	 * Construct request.
1602 	 */
1603 	bzero(&req, sizeof req);
1604 	req.p = p;
1605 	if (oldlenp) {
1606 		req.oldlen = *oldlenp;
1607 	}
1608 	if (old) {
1609 		req.oldptr = CAST_USER_ADDR_T(old);
1610 	}
1611 	if (newlen) {
1612 		req.newlen = newlen;
1613 		req.newptr = CAST_USER_ADDR_T(new);
1614 	}
1615 	req.oldfunc = sysctl_old_kernel;
1616 	req.newfunc = sysctl_new_kernel;
1617 	req.lock = 1;
1618 
1619 	/* make the request */
1620 	error = sysctl_root(TRUE, FALSE, NULL, 0, name, namelen, &req);
1621 
1622 	if (error && error != ENOMEM) {
1623 		return error;
1624 	}
1625 
1626 	if (oldlenp) {
1627 		*oldlenp = req.oldidx;
1628 	}
1629 
1630 	return error;
1631 }
1632 
1633 /*
1634  * Transfer function to/from user space.
1635  */
1636 STATIC int
sysctl_old_user(struct sysctl_req * req,const void * p,size_t l)1637 sysctl_old_user(struct sysctl_req *req, const void *p, size_t l)
1638 {
1639 	int error = 0;
1640 	size_t i = 0;
1641 
1642 	if (req->oldptr) {
1643 		if (req->oldlen - req->oldidx < l) {
1644 			return ENOMEM;
1645 		}
1646 		i = l;
1647 		if (i > req->oldlen - req->oldidx) {
1648 			i = req->oldlen - req->oldidx;
1649 		}
1650 		if (i > 0) {
1651 			error = copyout((const void*)p, (req->oldptr + req->oldidx), i);
1652 		}
1653 	}
1654 	req->oldidx += l;
1655 	if (error) {
1656 		return error;
1657 	}
1658 	if (req->oldptr && i < l) {
1659 		return ENOMEM;
1660 	}
1661 	return 0;
1662 }
1663 
1664 STATIC int
sysctl_new_user(struct sysctl_req * req,void * p,size_t l)1665 sysctl_new_user(struct sysctl_req *req, void *p, size_t l)
1666 {
1667 	int error;
1668 
1669 	if (!req->newptr) {
1670 		return 0;
1671 	}
1672 	if (req->newlen - req->newidx < l) {
1673 		return EINVAL;
1674 	}
1675 	error = copyin((req->newptr + req->newidx), p, l);
1676 	req->newidx += l;
1677 	return error;
1678 }
1679 
1680 const char *trial_experiment_factors_entitlement = "com.apple.private.kernel.read-write-trial-experiment-factors";
1681 
1682 /*
1683  * Is the current task allowed to read/write trial experiment factors?
1684  * Requires either:
1685  *  - trial_experiment_factors_entitlement
1686  *  - root user (internal-diagnostics only)
1687  */
1688 STATIC bool
can_rw_trial_experiment_factors(struct sysctl_req * req)1689 can_rw_trial_experiment_factors(struct sysctl_req *req)
1690 {
1691 	if (IOTaskHasEntitlement(proc_task(req->p), trial_experiment_factors_entitlement)) {
1692 		return true;
1693 	}
1694 	if (os_variant_has_internal_diagnostics("com.apple.xnu")) {
1695 		return !proc_suser(req->p);
1696 	}
1697 	return false;
1698 }
1699 
1700 #define WRITE_LEGACY_EXPERIMENT_FACTORS_ENTITLEMENT "com.apple.private.write-kr-experiment-factors"
1701 /*
1702  * Is the current task allowed to write to experiment factors?
1703  * tasks with the WRITE_EXPERIMENT_FACTORS_ENTITLEMENT are always allowed to write these.
1704  * In the development / debug kernel we also allow root to write them.
1705  */
1706 STATIC bool
can_write_legacy_experiment_factors(__unused struct sysctl_req * req)1707 can_write_legacy_experiment_factors(__unused struct sysctl_req *req)
1708 {
1709 	if (IOCurrentTaskHasEntitlement(WRITE_LEGACY_EXPERIMENT_FACTORS_ENTITLEMENT)) {
1710 		return true;
1711 	}
1712 #if DEBUG || DEVELOPMENT
1713 	return !proc_suser(req->p);
1714 #else
1715 	return false;
1716 #endif /* DEBUG || DEVELOPMENT */
1717 }
1718 
1719 /*
1720  * Traverse our tree, and find the right node, execute whatever it points
1721  * at, and return the resulting error code.
1722  */
1723 
1724 int
sysctl_root(boolean_t from_kernel,boolean_t string_is_canonical,char * namestring,size_t namestringlen,int * name,size_t namelen,struct sysctl_req * req)1725 sysctl_root(boolean_t from_kernel, boolean_t string_is_canonical,
1726     char *namestring, size_t namestringlen,
1727     int *name, size_t namelen, struct sysctl_req *req)
1728 {
1729 	u_int indx;
1730 	int i;
1731 	struct sysctl_oid_iterator it;
1732 	struct sysctl_oid *oid;
1733 	sysctl_handler_t oid_handler = NULL;
1734 	int error;
1735 	boolean_t unlocked_node_found = FALSE;
1736 	boolean_t namestring_started = FALSE;
1737 
1738 	/* Get the read lock on the geometry */
1739 	lck_rw_lock_shared(&sysctl_geometry_lock);
1740 
1741 	if (string_is_canonical) {
1742 		/* namestring is actually canonical, name/namelen needs to be populated */
1743 		error = name2oid(namestring, name, &namelen);
1744 		if (error) {
1745 			goto err;
1746 		}
1747 	}
1748 
1749 	it = sysctl_oid_iterator_begin(&sysctl__children);
1750 	oid = sysctl_oid_iterator_next_system_order(&it);
1751 
1752 	indx = 0;
1753 	while (oid && indx < CTL_MAXNAME) {
1754 		if (oid->oid_number == name[indx]) {
1755 			if (!from_kernel && !string_is_canonical) {
1756 				if (namestring_started) {
1757 					if (strlcat(namestring, ".", namestringlen) >= namestringlen) {
1758 						error = ENAMETOOLONG;
1759 						goto err;
1760 					}
1761 				}
1762 
1763 				if (strlcat(namestring, oid->oid_name, namestringlen) >= namestringlen) {
1764 					error = ENAMETOOLONG;
1765 					goto err;
1766 				}
1767 				namestring_started = TRUE;
1768 			}
1769 
1770 			indx++;
1771 			if (!(oid->oid_kind & CTLFLAG_LOCKED)) {
1772 				unlocked_node_found = TRUE;
1773 			}
1774 			if (oid->oid_kind & CTLFLAG_NOLOCK) {
1775 				req->lock = 0;
1776 			}
1777 			/*
1778 			 * For SYSCTL_PROC() functions which are for sysctl's
1779 			 * which have parameters at the end of their OID
1780 			 * space, you need to OR CTLTYPE_NODE into their
1781 			 * access value.
1782 			 *
1783 			 * NOTE: For binary backward compatibility ONLY! Do
1784 			 * NOT add new sysctl's that do this!  Existing
1785 			 * sysctl's which do this will eventually have
1786 			 * compatibility code in user space, and this method
1787 			 * will become unsupported.
1788 			 */
1789 			if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
1790 				if (oid->oid_handler) {
1791 					goto found;
1792 				}
1793 				if (indx == namelen) {
1794 					error = ENOENT;
1795 					goto err;
1796 				}
1797 
1798 				it = sysctl_oid_iterator_begin(oid->oid_arg1);
1799 				oid = sysctl_oid_iterator_next_system_order(&it);
1800 			} else {
1801 				if (indx != namelen) {
1802 					error = EISDIR;
1803 					goto err;
1804 				}
1805 				goto found;
1806 			}
1807 		} else {
1808 			oid = sysctl_oid_iterator_next_system_order(&it);
1809 		}
1810 	}
1811 	error = ENOENT;
1812 	goto err;
1813 found:
1814 
1815 	/*
1816 	 * indx is the index of the first remaining OID name,
1817 	 * for sysctls that take them as arguments
1818 	 */
1819 	if (!from_kernel && !string_is_canonical && (indx < namelen)) {
1820 		char tempbuf[10];
1821 		u_int indx2;
1822 
1823 		for (indx2 = indx; indx2 < namelen; indx2++) {
1824 			snprintf(tempbuf, sizeof(tempbuf), "%d", name[indx2]);
1825 
1826 			if (namestring_started) {
1827 				if (strlcat(namestring, ".", namestringlen) >= namestringlen) {
1828 					error = ENAMETOOLONG;
1829 					goto err;
1830 				}
1831 			}
1832 
1833 			if (strlcat(namestring, tempbuf, namestringlen) >= namestringlen) {
1834 				error = ENAMETOOLONG;
1835 				goto err;
1836 			}
1837 			namestring_started = TRUE;
1838 		}
1839 	}
1840 
1841 	/* If writing isn't allowed */
1842 	if (req->newptr && (!(oid->oid_kind & CTLFLAG_WR) ||
1843 	    ((oid->oid_kind & CTLFLAG_SECURE) && securelevel > 0))) {
1844 		error = (EPERM);
1845 		goto err;
1846 	}
1847 
1848 	/*
1849 	 * If we're inside the kernel, the OID must be marked as kernel-valid.
1850 	 */
1851 	if (from_kernel && !(oid->oid_kind & CTLFLAG_KERN)) {
1852 		error = (EPERM);
1853 		goto err;
1854 	}
1855 
1856 	if (oid->oid_kind & CTLFLAG_EXPERIMENT && req->p) {
1857 		if (!can_rw_trial_experiment_factors(req)) {
1858 			error = (EPERM);
1859 			goto err;
1860 		}
1861 	}
1862 
1863 	if (req->newptr && req->p) {
1864 		if (oid->oid_kind & CTLFLAG_LEGACY_EXPERIMENT) {
1865 			/*
1866 			 * Experiment factors have different permissions since they need to be
1867 			 * writable by procs with WRITE_EXPERIMENT_FACTORS_ENTITLEMENT.
1868 			 */
1869 			if (!can_write_legacy_experiment_factors(req)) {
1870 				error = (EPERM);
1871 				goto err;
1872 			}
1873 		} else {
1874 			/*
1875 			 * This is where legacy enforcement of permissions occurs.  If the
1876 			 * flag does not say CTLFLAG_ANYBODY, then we prohibit anyone but
1877 			 * root from writing new values down.  If local enforcement happens
1878 			 * at the leaf node, then it needs to be set as CTLFLAG_ANYBODY.  In
1879 			 * addition, if the leaf node is set this way, then in order to do
1880 			 * specific enforcement, it has to be of type SYSCTL_PROC.
1881 			 */
1882 			if (!(oid->oid_kind & CTLFLAG_ANYBODY) &&
1883 			    (error = proc_suser(req->p))) {
1884 				goto err;
1885 			}
1886 		}
1887 	}
1888 
1889 	/*
1890 	 * sysctl_unregister_oid() may change the handler value, so grab it
1891 	 * under the lock.
1892 	 */
1893 	oid_handler = oid->oid_handler;
1894 	if (!oid_handler) {
1895 		error = EINVAL;
1896 		goto err;
1897 	}
1898 
1899 	/*
1900 	 * Reference the OID and drop the geometry lock; this prevents the
1901 	 * OID from being deleted out from under the handler call, but does
1902 	 * not prevent other calls into handlers or calls to manage the
1903 	 * geometry elsewhere from blocking...
1904 	 */
1905 	if ((oid->oid_kind & CTLFLAG_PERMANENT) == 0) {
1906 		OSAddAtomic(1, &oid->oid_refcnt);
1907 	}
1908 
1909 	lck_rw_done(&sysctl_geometry_lock);
1910 
1911 #if CONFIG_MACF
1912 	if (!from_kernel) {
1913 		error = mac_system_check_sysctlbyname(kauth_cred_get(),
1914 		    namestring,
1915 		    name,
1916 		    namelen,
1917 		    req->oldptr,
1918 		    req->oldlen,
1919 		    req->newptr,
1920 		    req->newlen);
1921 		if (error) {
1922 			goto dropref;
1923 		}
1924 	}
1925 #endif
1926 
1927 	/*
1928 	 * ...however, we still have to grab the mutex for those calls which
1929 	 * may be into code whose reentrancy is protected by it.
1930 	 */
1931 	if (unlocked_node_found) {
1932 		lck_mtx_lock(&sysctl_unlocked_node_lock);
1933 	}
1934 
1935 #if defined(HAS_APPLE_PAC)
1936 	/*
1937 	 * oid_handler is signed address-discriminated by sysctl_register_oid().
1938 	 */
1939 	oid_handler = ptrauth_auth_and_resign(oid_handler,
1940 	    ptrauth_key_function_pointer,
1941 	    ptrauth_blend_discriminator(&oid->oid_handler,
1942 	    os_hash_kernel_pointer(oid->oid_arg1)),
1943 	    ptrauth_key_function_pointer,
1944 	    ptrauth_function_pointer_type_discriminator(typeof(oid_handler)));
1945 #endif /* defined(HAS_APPLE_PAC) */
1946 
1947 	if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
1948 		i = oid_handler(oid, name + indx, (int)(namelen - indx), req);
1949 	} else {
1950 		i = oid_handler(oid, oid->oid_arg1, oid->oid_arg2, req);
1951 	}
1952 	error = i;
1953 
1954 	if (unlocked_node_found) {
1955 		lck_mtx_unlock(&sysctl_unlocked_node_lock);
1956 	}
1957 
1958 #if CONFIG_MACF
1959 	/* only used from another CONFIG_MACF block */
1960 dropref:
1961 #endif
1962 
1963 	/*
1964 	 * This is tricky... we re-grab the geometry lock in order to drop
1965 	 * the reference and wake on the address; since the geometry
1966 	 * lock is a reader/writer lock rather than a mutex, we have to
1967 	 * wake on all apparent 1->0 transitions.  This abuses the drop
1968 	 * after the reference decrement in order to wake any lck_rw_sleep()
1969 	 * in progress in sysctl_unregister_oid() that slept because of a
1970 	 * non-zero reference count.
1971 	 *
1972 	 * Note:	OSAddAtomic() is defined to return the previous value;
1973 	 *		we use this and the fact that the lock itself is a
1974 	 *		barrier to avoid waking every time through on "hot"
1975 	 *		OIDs.
1976 	 */
1977 	lck_rw_lock_shared(&sysctl_geometry_lock);
1978 
1979 	if ((oid->oid_kind & CTLFLAG_PERMANENT) == 0) {
1980 		if (OSAddAtomic(-1, &oid->oid_refcnt) == 1) {
1981 			wakeup(&oid->oid_refcnt);
1982 		}
1983 	}
1984 
1985 err:
1986 	lck_rw_done(&sysctl_geometry_lock);
1987 	return error;
1988 }
1989 
1990 void
sysctl_create_user_req(struct sysctl_req * req,struct proc * p,user_addr_t oldp,size_t oldlen,user_addr_t newp,size_t newlen)1991 sysctl_create_user_req(struct sysctl_req *req, struct proc *p, user_addr_t oldp,
1992     size_t oldlen, user_addr_t newp, size_t newlen)
1993 {
1994 	bzero(req, sizeof(*req));
1995 
1996 	req->p = p;
1997 
1998 	req->oldlen = oldlen;
1999 	req->oldptr = oldp;
2000 
2001 	if (newlen) {
2002 		req->newlen = newlen;
2003 		req->newptr = newp;
2004 	}
2005 
2006 	req->oldfunc = sysctl_old_user;
2007 	req->newfunc = sysctl_new_user;
2008 	req->lock = 1;
2009 
2010 	return;
2011 }
2012 
2013 int
sysctl(proc_t p,struct sysctl_args * uap,__unused int32_t * retval)2014 sysctl(proc_t p, struct sysctl_args *uap, __unused int32_t *retval)
2015 {
2016 	int error, new_error;
2017 	size_t oldlen = 0, newlen;
2018 	int name[CTL_MAXNAME];
2019 	struct sysctl_req req;
2020 	char *namestring;
2021 	size_t namestringlen = MAXPATHLEN;
2022 
2023 	/*
2024 	 * all top-level sysctl names are non-terminal
2025 	 */
2026 	if (uap->namelen > CTL_MAXNAME || uap->namelen < 2) {
2027 		return EINVAL;
2028 	}
2029 	error = copyin(uap->name, &name[0], uap->namelen * sizeof(int));
2030 	if (error) {
2031 		return error;
2032 	}
2033 
2034 	AUDIT_ARG(ctlname, name, uap->namelen);
2035 
2036 	if (uap->newlen > SIZE_T_MAX) {
2037 		return EINVAL;
2038 	}
2039 	newlen = (size_t)uap->newlen;
2040 
2041 	if (uap->oldlenp != USER_ADDR_NULL) {
2042 		uint64_t        oldlen64 = fuulong(uap->oldlenp);
2043 
2044 		/*
2045 		 * If more than 4G, clamp to 4G
2046 		 */
2047 		if (oldlen64 > SIZE_T_MAX) {
2048 			oldlen = SIZE_T_MAX;
2049 		} else {
2050 			oldlen = (size_t)oldlen64;
2051 		}
2052 	}
2053 
2054 	sysctl_create_user_req(&req, p, uap->old, oldlen, uap->new, newlen);
2055 
2056 	/* Guess that longest length for the passed-in MIB, if we can be more aggressive than MAXPATHLEN */
2057 	if (uap->namelen == 2) {
2058 		if (name[0] == CTL_KERN && name[1] < KERN_MAXID) {
2059 			namestringlen = 32; /* "kern.speculative_reads_disabled" */
2060 		} else if (name[0] == CTL_HW && name[1] < HW_MAXID) {
2061 			namestringlen = 32; /* "hw.cachelinesize_compat" */
2062 		}
2063 	}
2064 
2065 	namestring = (char *)kalloc_data(namestringlen, Z_WAITOK);
2066 	if (!namestring) {
2067 		oldlen = 0;
2068 		goto err;
2069 	}
2070 
2071 	error = userland_sysctl(FALSE, namestring, namestringlen, name, uap->namelen, &req, &oldlen);
2072 
2073 	kfree_data(namestring, namestringlen);
2074 
2075 	if ((error) && (error != ENOMEM)) {
2076 		return error;
2077 	}
2078 
2079 err:
2080 	if (uap->oldlenp != USER_ADDR_NULL) {
2081 		/*
2082 		 * Only overwrite the old error value on a new error
2083 		 */
2084 		new_error = suulong(uap->oldlenp, oldlen);
2085 
2086 		if (new_error) {
2087 			error = new_error;
2088 		}
2089 	}
2090 
2091 	return error;
2092 }
2093 
2094 // sysctlbyname is also exported as KPI to kexts
2095 // and the syscall name cannot conflict with it
2096 int
sys_sysctlbyname(proc_t p,struct sysctlbyname_args * uap,__unused int32_t * retval)2097 sys_sysctlbyname(proc_t p, struct sysctlbyname_args *uap, __unused int32_t *retval)
2098 {
2099 	int error, new_error;
2100 	size_t oldlen = 0, newlen;
2101 	char *name;
2102 	size_t namelen = 0;
2103 	struct sysctl_req req;
2104 	int oid[CTL_MAXNAME];
2105 
2106 	if (uap->namelen >= MAXPATHLEN) { /* XXX arbitrary, undocumented */
2107 		return ENAMETOOLONG;
2108 	}
2109 	namelen = (size_t)uap->namelen;
2110 
2111 	name = (char *)kalloc_data(namelen + 1, Z_WAITOK);
2112 	if (!name) {
2113 		return ENOMEM;
2114 	}
2115 
2116 	error = copyin(uap->name, name, namelen);
2117 	if (error) {
2118 		kfree_data(name, namelen + 1);
2119 		return error;
2120 	}
2121 	name[namelen] = '\0';
2122 
2123 	/* XXX
2124 	 * AUDIT_ARG(ctlname, name, uap->namelen);
2125 	 */
2126 
2127 	if (uap->newlen > SIZE_T_MAX) {
2128 		kfree_data(name, namelen + 1);
2129 		return EINVAL;
2130 	}
2131 	newlen = (size_t)uap->newlen;
2132 
2133 	if (uap->oldlenp != USER_ADDR_NULL) {
2134 		uint64_t        oldlen64 = fuulong(uap->oldlenp);
2135 
2136 		/*
2137 		 * If more than 4G, clamp to 4G
2138 		 */
2139 		if (oldlen64 > SIZE_T_MAX) {
2140 			oldlen = SIZE_T_MAX;
2141 		} else {
2142 			oldlen = (size_t)oldlen64;
2143 		}
2144 	}
2145 
2146 	sysctl_create_user_req(&req, p, uap->old, oldlen, uap->new, newlen);
2147 
2148 	error = userland_sysctl(TRUE, name, namelen + 1, oid, CTL_MAXNAME, &req, &oldlen);
2149 
2150 	kfree_data(name, namelen + 1);
2151 
2152 	if ((error) && (error != ENOMEM)) {
2153 		return error;
2154 	}
2155 
2156 	if (uap->oldlenp != USER_ADDR_NULL) {
2157 		/*
2158 		 * Only overwrite the old error value on a new error
2159 		 */
2160 		new_error = suulong(uap->oldlenp, oldlen);
2161 
2162 		if (new_error) {
2163 			error = new_error;
2164 		}
2165 	}
2166 
2167 	return error;
2168 }
2169 
2170 /*
2171  * This is used from various compatibility syscalls too.  That's why name
2172  * must be in kernel space.
2173  */
2174 int
userland_sysctl(boolean_t string_is_canonical,char * namestring,size_t namestringlen,int * name,u_int namelen,struct sysctl_req * req,size_t * retval)2175 userland_sysctl(boolean_t string_is_canonical,
2176     char *namestring, size_t namestringlen,
2177     int *name, u_int namelen, struct sysctl_req *req,
2178     size_t *retval)
2179 {
2180 	int error = 0;
2181 	struct sysctl_req req2;
2182 
2183 	do {
2184 		/* if EAGAIN, reset output cursor */
2185 		req2 = *req;
2186 		if (!string_is_canonical) {
2187 			namestring[0] = '\0';
2188 		}
2189 
2190 		error = sysctl_root(FALSE, string_is_canonical, namestring, namestringlen, name, namelen, &req2);
2191 	} while (error == EAGAIN);
2192 
2193 	if (error && error != ENOMEM) {
2194 		return error;
2195 	}
2196 
2197 	if (retval) {
2198 		if (req2.oldptr && req2.oldidx > req2.oldlen) {
2199 			*retval = req2.oldlen;
2200 		} else {
2201 			*retval = req2.oldidx;
2202 		}
2203 	}
2204 	return error;
2205 }
2206 
2207 /*
2208  * Kernel versions of the userland sysctl helper functions.
2209  *
2210  * These allow sysctl to be used in the same fashion in both
2211  * userland and the kernel.
2212  *
2213  * Note that some sysctl handlers use copyin/copyout, which
2214  * may not work correctly.
2215  *
2216  * The "sysctlbyname" KPI for use by kexts is aliased to this function.
2217  */
2218 
2219 int
kernel_sysctlbyname(const char * name,void * oldp,size_t * oldlenp,void * newp,size_t newlen)2220 kernel_sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen)
2221 {
2222 	int oid[CTL_MAXNAME];
2223 	int name2mib_oid[2];
2224 	int error;
2225 	size_t oidlen;
2226 
2227 	/* look up the OID with magic service node */
2228 	name2mib_oid[0] = 0;
2229 	name2mib_oid[1] = 3;
2230 
2231 	oidlen = sizeof(oid);
2232 	error = kernel_sysctl(current_proc(), name2mib_oid, 2, oid, &oidlen, __DECONST(void *, name), strlen(name));
2233 	oidlen /= sizeof(int);
2234 	if (oidlen > UINT_MAX) {
2235 		error = EDOM;
2236 	}
2237 
2238 	/* now use the OID */
2239 	if (error == 0) {
2240 		error = kernel_sysctl(current_proc(), oid, (u_int)oidlen, oldp, oldlenp, newp, newlen);
2241 	}
2242 	return error;
2243 }
2244 
2245 int
2246 scalable_counter_sysctl_handler SYSCTL_HANDLER_ARGS
2247 {
2248 #pragma unused(arg2, oidp)
2249 	scalable_counter_t counter = *(scalable_counter_t*) arg1;
2250 	uint64_t value = counter_load(&counter);
2251 	return SYSCTL_OUT(req, &value, sizeof(value));
2252 }
2253 
2254 SYSCTL_NODE(_kern, OID_AUTO, trial, CTLFLAG_RW | CTLFLAG_LOCKED, 0,
2255     "trial experiment factors");
2256 
2257 #define X(name, T) \
2258 int \
2259 experiment_factor_##name##_handler SYSCTL_HANDLER_ARGS \
2260 { \
2261 	int error, changed = 0; \
2262 	T *ptr; \
2263 	T new_value, current_value; \
2264 	struct experiment_spec *spec = (struct experiment_spec *) arg1; \
2265 	if (!arg1) { \
2266 	        return EINVAL; \
2267 	} \
2268 	ptr = (T *)(spec->ptr); \
2269 	current_value = *ptr; \
2270 	error = sysctl_io_number(req, current_value, sizeof(T), &new_value, &changed); \
2271 	if (error != 0) { \
2272 	        return error; \
2273 	} \
2274 	if (changed) { \
2275 	        if (new_value < (T) spec->min_value || new_value > (T) spec->max_value) { \
2276 	                return EINVAL; \
2277 	        } \
2278 	        if (os_atomic_cmpxchg(&spec->modified, false, true, acq_rel)) { \
2279 	                spec->original_value = current_value; \
2280 	        } \
2281 	        os_atomic_store_wide(ptr, new_value, relaxed); \
2282 	} \
2283 	return 0; \
2284 }
2285 
2286 experiment_factor_numeric_types
2287 #undef X
2288 
2289 #if DEBUG || DEVELOPMENT
2290 static int
2291 sysctl_test_handler SYSCTL_HANDLER_ARGS
2292 {
2293 	int error;
2294 	int64_t value, out = 0;
2295 
2296 	error = SYSCTL_IN(req, &value, sizeof(value));
2297 	/* Only run test when new value was provided to prevent just reading or
2298 	 * querying from triggering the test, but still allow for sysctl
2299 	 * presence tests via read requests with NULL oldptr */
2300 	if (error == 0 && req->newptr) {
2301 		/* call the test that was specified in SYSCTL_TEST_REGISTER */
2302 		error = ((int (*)(int64_t, int64_t *))(uintptr_t)arg1)(value, &out);
2303 	}
2304 	if (error == 0) {
2305 		error = SYSCTL_OUT(req, &out, sizeof(out));
2306 	}
2307 	return error;
2308 }
2309 
2310 void
sysctl_register_test_startup(struct sysctl_test_setup_spec * spec)2311 sysctl_register_test_startup(struct sysctl_test_setup_spec *spec)
2312 {
2313 	struct sysctl_oid *oid = zalloc_permanent_type(struct sysctl_oid);
2314 
2315 	*oid = (struct sysctl_oid){
2316 		.oid_parent     = &sysctl__debug_test_children,
2317 		.oid_number     = OID_AUTO,
2318 		.oid_kind       = CTLTYPE_QUAD | CTLFLAG_OID2 | CTLFLAG_WR |
2319 	    CTLFLAG_PERMANENT | CTLFLAG_LOCKED | CTLFLAG_MASKED
2320 #ifdef __BUILDING_XNU_LIB_UNITTEST__
2321 	    | CTLFLAG_KERN,     /* allow calls from unit-test which use kernel_sysctlbyname() */
2322 #else /* __BUILDING_XNU_LIB_UNITTEST__ */
2323 		,
2324 #endif /* __BUILDING_XNU_LIB_UNITTEST__ */
2325 		.oid_arg1       = (void *)(uintptr_t)spec->st_func,
2326 		.oid_name       = spec->st_name,
2327 		.oid_handler    = sysctl_test_handler,
2328 		.oid_fmt        = "Q",
2329 		.oid_version    = SYSCTL_OID_VERSION,
2330 		.oid_descr      = "",
2331 	};
2332 	sysctl_register_oid_early(oid);
2333 }
2334 
2335 
2336 extern void vm_analytics_tick(void *arg0, void *arg1);
2337 
2338 /* Manual trigger of vm_analytics_tick for testing on dev/debug kernel. */
2339 static int
2340 sysctl_vm_analytics_tick SYSCTL_HANDLER_ARGS
2341 {
2342 #pragma unused(arg1, arg2)
2343 	int error, val = 0;
2344 	error = sysctl_handle_int(oidp, &val, 0, req);
2345 	if (error || !req->newptr) {
2346 		return error;
2347 	}
2348 	vm_analytics_tick(NULL, NULL);
2349 	return 0;
2350 }
2351 
2352 SYSCTL_PROC(_vm, OID_AUTO, analytics_report, CTLTYPE_INT | CTLFLAG_WR | CTLFLAG_LOCKED | CTLFLAG_MASKED, 0, 0, &sysctl_vm_analytics_tick, "I", "");
2353 
2354 /* Manual trigger of record_system_event for testing on dev/debug kernel */
2355 static int
2356 sysctl_test_record_system_event SYSCTL_HANDLER_ARGS
2357 {
2358 #pragma unused(arg1, arg2)
2359 	int error, val = 0;
2360 	error = sysctl_handle_int(oidp, &val, 0, req);
2361 	if (error || !req->newptr) {
2362 		return error;
2363 	}
2364 	record_system_event(SYSTEM_EVENT_TYPE_INFO, SYSTEM_EVENT_SUBSYSTEM_TEST, "sysctl test", "this is a test %s", "message");
2365 	return 0;
2366 }
2367 
2368 SYSCTL_PROC(_kern, OID_AUTO, test_record_system_event, CTLTYPE_INT | CTLFLAG_WR | CTLFLAG_LOCKED | CTLFLAG_MASKED, 0, 0, &sysctl_test_record_system_event, "-", "");
2369 
2370 #endif /* DEBUG || DEVELOPMENT */
2371 
2372 
2373 CA_EVENT(ca_test_event,
2374     CA_INT, TestKey,
2375     CA_BOOL, TestBool,
2376     CA_STATIC_STRING(CA_UUID_LEN), TestString);
2377 
2378 /*
2379  * Manual testing of sending a CoreAnalytics event
2380  */
2381 static int
2382 sysctl_test_ca_event SYSCTL_HANDLER_ARGS
2383 {
2384 #pragma unused(arg1, arg2)
2385 	int error, val = 0;
2386 	/*
2387 	 * Only send on write
2388 	 */
2389 	error = sysctl_handle_int(oidp, &val, 0, req);
2390 	if (error || !req->newptr) {
2391 		return error;
2392 	}
2393 
2394 	ca_event_t event = CA_EVENT_ALLOCATE(ca_test_event);
2395 	CA_EVENT_TYPE(ca_test_event) * event_data = event->data;
2396 	event_data->TestKey = val;
2397 	event_data->TestBool = true;
2398 	uuid_string_t test_str = "sysctl_test_ca_event";
2399 	strlcpy(event_data->TestString, test_str, CA_UUID_LEN);
2400 	CA_EVENT_SEND(event);
2401 	return 0;
2402 }
2403 
2404 SYSCTL_PROC(_kern, OID_AUTO, test_ca_event, CTLTYPE_INT | CTLFLAG_WR | CTLFLAG_LOCKED | CTLFLAG_MASKED, 0, 0, &sysctl_test_ca_event, "I", "");
2405 
2406 
2407 #if DEVELOPMENT || DEBUG
2408 struct perf_compressor_data {
2409 	user_addr_t buffer;
2410 	size_t buffer_size;
2411 	uint64_t benchmark_time;
2412 	uint64_t bytes_processed;
2413 	uint64_t compressor_growth;
2414 };
2415 
2416 static int
2417 sysctl_perf_compressor SYSCTL_HANDLER_ARGS
2418 {
2419 	int error = EINVAL;
2420 	size_t len = sizeof(struct perf_compressor_data);
2421 	struct perf_compressor_data benchmark_data = {0};
2422 
2423 	if (req->oldptr == USER_ADDR_NULL || req->oldlen != len ||
2424 	    req->newptr == USER_ADDR_NULL || req->newlen != len) {
2425 		return EINVAL;
2426 	}
2427 
2428 	error = SYSCTL_IN(req, &benchmark_data, len);
2429 	if (error) {
2430 		return error;
2431 	}
2432 
2433 	kern_return_t ret = run_compressor_perf_test(benchmark_data.buffer, benchmark_data.buffer_size,
2434 	    &benchmark_data.benchmark_time, &benchmark_data.bytes_processed, &benchmark_data.compressor_growth);
2435 	switch (ret) {
2436 	case KERN_SUCCESS:
2437 		error = 0;
2438 		break;
2439 	case KERN_NOT_SUPPORTED:
2440 		error = ENOTSUP;
2441 		break;
2442 	case KERN_INVALID_ARGUMENT:
2443 		error = EINVAL;
2444 		break;
2445 	case KERN_RESOURCE_SHORTAGE:
2446 		error = EAGAIN;
2447 		break;
2448 	default:
2449 		error = ret;
2450 		break;
2451 	}
2452 	if (error != 0) {
2453 		return error;
2454 	}
2455 
2456 	return SYSCTL_OUT(req, &benchmark_data, len);
2457 }
2458 
2459 /*
2460  * Compressor & swap performance test
2461  */
2462 SYSCTL_PROC(_kern, OID_AUTO, perf_compressor, CTLFLAG_WR | CTLFLAG_MASKED | CTLTYPE_STRUCT,
2463     0, 0, sysctl_perf_compressor, "S", "Compressor & swap benchmark");
2464 #endif /* DEVELOPMENT || DEBUG */
2465 
2466 #if CONFIG_JETSAM
2467 extern uint32_t swapout_sleep_threshold;
2468 #if DEVELOPMENT || DEBUG
2469 SYSCTL_UINT(_vm, OID_AUTO, swapout_sleep_threshold, CTLFLAG_RW | CTLFLAG_LOCKED, &swapout_sleep_threshold, 0, "");
2470 #else /* DEVELOPMENT || DEBUG */
2471 SYSCTL_UINT(_vm, OID_AUTO, swapout_sleep_threshold, CTLFLAG_RD | CTLFLAG_LOCKED, &swapout_sleep_threshold, 0, "");
2472 #endif /* DEVELOPMENT || DEBUG */
2473 #endif /* CONFIG_JETSAM */
2474 
2475 #if DEBUG || DEVELOPMENT
2476 
2477 /* The following sysctl nodes set up a tree that our walking logic
2478  * previously stumbled on. This tree gets walked in a unit test.
2479  */
2480 SYSCTL_NODE(_debug_test, OID_AUTO, sysctl_node_test, CTLFLAG_RW | CTLFLAG_LOCKED,
2481     0, "rdar://138698424 parent node");
2482 
2483 SYSCTL_NODE(_debug_test_sysctl_node_test, OID_AUTO, l2, CTLFLAG_RW | CTLFLAG_LOCKED,
2484     0, "rdar://138698424 L2 node");
2485 
2486 SYSCTL_NODE(_debug_test_sysctl_node_test_l2, OID_AUTO, l3,
2487     CTLFLAG_RW | CTLFLAG_LOCKED, 0, "rdar://138698424 L3 node");
2488 
2489 SYSCTL_NODE(_debug_test_sysctl_node_test_l2_l3, OID_AUTO, l4,
2490     CTLFLAG_RW | CTLFLAG_LOCKED, 0, "rdar://138698424 L4 node");
2491 
2492 SYSCTL_OID(_debug_test_sysctl_node_test_l2, OID_AUTO, hanging_oid,
2493     CTLFLAG_RW | CTLFLAG_LOCKED, 0, 0, NULL, "", "rdar://138698424 L2 hanging OID");
2494 
2495 #endif /* DEBUG || DEVELOPMENT */
2496 
2497 static int
2498 sysctl_static_if_modified_keys SYSCTL_HANDLER_ARGS
2499 {
2500 	extern char __static_if_segment_start[] __SEGMENT_START_SYM(STATIC_IF_SEGMENT);
2501 
2502 	uint64_t addr;
2503 	int      err;
2504 
2505 	for (static_if_key_t key = static_if_modified_keys;
2506 	    key; key = key->sik_modified_next) {
2507 		if ((key->sik_enable_count >= 0) == key->sik_init_value) {
2508 			continue;
2509 		}
2510 
2511 		addr = (vm_offset_t)key->sik_entries_head - (vm_offset_t)__static_if_segment_start;
2512 		err = SYSCTL_OUT(req, &addr, sizeof(addr));
2513 		if (err) {
2514 			return err;
2515 		}
2516 	}
2517 
2518 	return 0;
2519 }
2520 
2521 SYSCTL_PROC(_kern, OID_AUTO, static_if_modified_keys,
2522     CTLFLAG_RD | CTLFLAG_LOCKED | CTLTYPE_OPAQUE,
2523     0, 0, sysctl_static_if_modified_keys, "-",
2524     "List of unslid addresses of modified keys");
2525 
2526 SYSCTL_UINT(_kern, OID_AUTO, static_if_abi, CTLFLAG_RD | CTLFLAG_LOCKED,
2527     &static_if_abi, 0, "static_if ABI");
2528