+ struct sysctl_oid *removed_oidp = NULL; /* OID removed from tree */
+#if __x86_64__
+ struct sysctl_oid *old_oidp = NULL; /* OID compatibility copy */
+#else
+ struct sysctl_oid *const old_oidp = NULL;
+#endif
+
+ /* Get the write lock to modify the geometry */
+ lck_rw_lock_exclusive(sysctl_geometry_lock);
+
+ if (!(oidp->oid_kind & CTLFLAG_OID2)) {
+#if __x86_64__
+ /*
+ * We're using a copy so we can get the new fields in an
+ * old structure, so we have to iterate to compare the
+ * partial structure; when we find a match, we remove it
+ * normally and free the memory.
+ */
+ SLIST_FOREACH(old_oidp, oidp->oid_parent, oid_link) {
+ if (!memcmp(&oidp->oid_number, &old_oidp->oid_number, (offsetof(struct sysctl_oid, oid_descr) - offsetof(struct sysctl_oid, oid_number)))) {
+ break;
+ }
+ }
+ if (old_oidp != NULL) {
+ SLIST_REMOVE(old_oidp->oid_parent, old_oidp, sysctl_oid, oid_link);
+ removed_oidp = old_oidp;
+ }
+#else
+ panic("Old style sysctl without a version number isn't supported");
+#endif
+ } else {
+ /* It's a later version; handle the versions we know about */
+ switch (oidp->oid_version) {
+ case SYSCTL_OID_VERSION:
+ /* We can just remove the OID directly... */
+ SLIST_REMOVE(oidp->oid_parent, oidp, sysctl_oid, oid_link);
+ removed_oidp = oidp;
+ break;
+ default:
+ /* XXX: Can't happen; probably tree coruption.*/
+ break; /* rejects unknown version */
+ }
+ }
+
+#if defined(HAS_APPLE_PAC)
+ if (removed_oidp && removed_oidp->oid_handler && old_oidp == NULL) {
+ /*
+ * Revert address-discriminated signing performed by
+ * sysctl_register_oid() (in case this oid is registered again).
+ */
+ removed_oidp->oid_handler = ptrauth_auth_and_resign(removed_oidp->oid_handler,
+ ptrauth_key_function_pointer,
+ ptrauth_blend_discriminator(&removed_oidp->oid_handler,
+ os_hash_kernel_pointer(removed_oidp->oid_arg1)),
+ ptrauth_key_function_pointer,
+ ptrauth_function_pointer_type_discriminator(typeof(removed_oidp->oid_handler)));
+ }
+#endif /* defined(HAS_APPLE_PAC) */
+
+ /*
+ * We've removed it from the list at this point, but we don't want
+ * to return to the caller until all handler references have drained
+ * out. Doing things in this order prevent other people coming in
+ * and starting new operations against the OID node we want removed.
+ *
+ * Note: oidp could be NULL if it wasn't found.
+ */
+ while (removed_oidp && removed_oidp->oid_refcnt) {
+ lck_rw_sleep(sysctl_geometry_lock, LCK_SLEEP_EXCLUSIVE, &removed_oidp->oid_refcnt, THREAD_UNINT);
+ }
+
+ /* Release the write lock */
+ lck_rw_unlock_exclusive(sysctl_geometry_lock);
+
+ if (old_oidp != NULL) {
+#if __x86_64__
+ /* If it was allocated, free it after dropping the lock */
+ FREE(old_oidp, M_TEMP);
+#endif
+ }