/* * Copyright (c) 1998-2021 Apple Computer, Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * */ #define TEST_HEADERS 0 #if TEST_HEADERS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /* TEST_HEADERS */ #include #include #include #include #include "Tests.h" #if DEVELOPMENT || DEBUG #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static uint64_t gIOWorkLoopTestDeadline; static void TESAction(OSObject * owner, IOTimerEventSource * tes) { if (mach_absolute_time() < gIOWorkLoopTestDeadline) { tes->setTimeout(1, kMicrosecondScale); } } static int IOWorkLoopTest(int newValue) { IOReturn err; uint32_t idx; IOWorkLoop * wl; IOTimerEventSource * tes; IOInterruptEventSource * ies; wl = IOWorkLoop::workLoop(); assert(wl); tes = IOTimerEventSource::timerEventSource(kIOTimerEventSourceOptionsPriorityWorkLoop, wl, &TESAction); assert(tes); err = wl->addEventSource(tes); assert(kIOReturnSuccess == err); clock_interval_to_deadline(100, kMillisecondScale, &gIOWorkLoopTestDeadline); for (idx = 0; mach_absolute_time() < gIOWorkLoopTestDeadline; idx++) { tes->setTimeout(idx & 1023, kNanosecondScale); } tes->cancelTimeout(); wl->removeEventSource(tes); tes->release(); int value = 3; tes = IOTimerEventSource::timerEventSource(kIOTimerEventSourceOptionsDefault, wl, ^(IOTimerEventSource * tes){ kprintf("wl %p, value %d\n", wl, value); }); err = wl->addEventSource(tes); assert(kIOReturnSuccess == err); value = 2; tes->setTimeout(1, kNanosecondScale); IOSleep(1); wl->removeEventSource(tes); tes->release(); ies = IOInterruptEventSource::interruptEventSource(wl, NULL, 0, ^void (IOInterruptEventSource *sender, int count){ kprintf("ies block %p, %d\n", sender, count); }); assert(ies); kprintf("ies %p\n", ies); err = wl->addEventSource(ies); assert(kIOReturnSuccess == err); ies->interruptOccurred(NULL, NULL, 0); IOSleep(1); ies->interruptOccurred(NULL, NULL, 0); IOSleep(1); wl->removeEventSource(ies); ies->release(); wl->release(); return 0; } static int OSCollectionTest(int newValue) { OSArray * array = OSArray::withCapacity(8); array->setObject(kOSBooleanTrue); array->setObject(kOSBooleanFalse); array->setObject(kOSBooleanFalse); array->setObject(kOSBooleanTrue); array->setObject(kOSBooleanFalse); array->setObject(kOSBooleanTrue); __block unsigned int index; index = 0; array->iterateObjects(^bool (OSObject * obj) { kprintf("%d:%d ", index, (obj == kOSBooleanTrue) ? 1 : (obj == kOSBooleanFalse) ? 0 : 2); index++; return false; }); kprintf("\n"); array->release(); OSDictionary * dict = IOService::resourceMatching("hello"); assert(dict); index = 0; dict->iterateObjects(^bool (const OSSymbol * sym, OSObject * obj) { OSString * str = OSDynamicCast(OSString, obj); assert(str); kprintf("%d:%s=%s\n", index, sym->getCStringNoCopy(), str->getCStringNoCopy()); index++; return false; }); dict->release(); OSSerializer * serializer = OSSerializer::withBlock(^bool (OSSerialize * s){ return gIOBSDUnitKey->serialize(s); }); assert(serializer); IOService::getPlatform()->setProperty("OSSerializer_withBlock", serializer); serializer->release(); OSString * ab = OSString::withCString("abcdef", 2); assert(strcmp(ab->getCStringNoCopy(), "ab") == 0); OSString * defgh = OSString::withCString("defgh", 10); assert(strcmp(defgh->getCStringNoCopy(), "defgh") == 0); OSString * zyxwvut = OSString::withCString("zyxwvut", 7); assert(strcmp(zyxwvut->getCStringNoCopy(), "zyxwvut") == 0); OSSafeReleaseNULL(ab); OSSafeReleaseNULL(defgh); OSSafeReleaseNULL(zyxwvut); return 0; } static int OSAllocationTests(int) { OSAllocation ints(100, OSAllocateMemory); assert(ints); { int counter = 0; for (int& i : ints) { i = counter++; } } { int counter = 0; for (int& i : ints) { assert(i == counter); ++counter; } } OSAllocation arrayZero(100, OSAllocateMemoryZero); assert(arrayZero); for (const auto& i : arrayZero) { assert(i == 0); } // Make sure we can have two-level OSAllocations { OSAllocation > testArray(10, OSAllocateMemory); for (int i = 0; i < 10; i++) { testArray[i] = OSAllocation(10, OSAllocateMemory); for (int j = 0; j < 10; ++j) { testArray[i][j] = i + j; } } for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; ++j) { assert(testArray[i][j] == i + j); } } } return 0; } static int OSDataAllocationTests(int) { OSDataAllocation ints(100, OSAllocateMemory); assert(ints); { int counter = 0; for (int& i : ints) { i = counter++; } } { int counter = 0; for (const int& i : ints) { assert(i == counter); ++counter; } } OSDataAllocation arrayZero(100, OSAllocateMemoryZero); assert(arrayZero); for (const auto& i : arrayZero) { assert(i == 0); } return 0; } static int OSBoundedArrayTests(int) { OSBoundedArray ints = {0, 1, 2, 3, 4}; assert(ints.size() == 5); { int counter = 0; for (int& i : ints) { i = counter++; } } { int counter = 0; for (int& i : ints) { assert(i == counter); ++counter; } } return 0; } static int OSBoundedArrayRefTests(int) { OSBoundedArray storage = {0, 1, 2, 3, 4}; OSBoundedArrayRef ints(storage); assert(ints); { int counter = 0; for (int& i : ints) { i = counter++; } } { int counter = 0; for (int& i : ints) { assert(i == counter); ++counter; } } return 0; } class OSArraySubclass : public OSArray { OSDeclareDefaultStructors(OSArraySubclass); public: static OSArraySubclass * withCapacity(unsigned int inCapacity); virtual unsigned int iteratorSize() const APPLE_KEXT_OVERRIDE; }; OSDefineMetaClassAndStructors(OSArraySubclass, OSArray); OSArraySubclass * OSArraySubclass::withCapacity(unsigned int inCapacity) { OSArraySubclass * me = OSTypeAlloc(OSArraySubclass); if (me && !me->initWithCapacity(inCapacity)) { return nullptr; } return me; } unsigned int OSArraySubclass::iteratorSize() const { unsigned int result = 64; // Has to be larger than the OSArray iterator size to prevent out-of-bounds writes assert(result >= OSArray::iteratorSize()); return result; } class OSCISubclass : public OSCollectionIterator { OSDeclareDefaultStructors(OSCISubclass); public: static OSCISubclass * withCollection(const OSCollection * inColl); }; OSDefineMetaClassAndStructors(OSCISubclass, OSCollectionIterator); OSCISubclass * OSCISubclass::withCollection(const OSCollection * inColl) { OSCISubclass * me = OSTypeAlloc(OSCISubclass); if (me && !me->initWithCollection(inColl)) { return nullptr; } return me; } static int OSCollectionIteratorTests(int) { OSArray * array = OSArray::withCapacity(0); OSString * firstObj = OSString::withCString("test object"); OSString * secondObj = OSString::withCString("test object 2"); OSObject * current = NULL; OSCollectionIterator * osci = NULL; OSCISubclass * osciSubclass = NULL; size_t index = 0; array->setObject(firstObj); array->setObject(secondObj); // Test iteration over a normal OSArray osci = OSCollectionIterator::withCollection(array); assert(osci != NULL); index = 0; while ((current = osci->getNextObject()) != NULL) { if (index == 0) { assert(current == firstObj); } else if (index == 1) { assert(current == secondObj); } else { panic("index out of range"); } index++; } OSSafeReleaseNULL(osci); // Test iteration with a OSCollectionIterator subclass over a normal OSArray osciSubclass = OSCISubclass::withCollection(array); assert(osciSubclass != NULL); index = 0; while ((current = osciSubclass->getNextObject()) != NULL) { if (index == 0) { assert(current == firstObj); } else if (index == 1) { assert(current == secondObj); } else { panic("index out of range"); } index++; } OSSafeReleaseNULL(osciSubclass); OSSafeReleaseNULL(array); // Create the OSArray subclass OSArraySubclass * arraySubclass = OSArraySubclass::withCapacity(0); arraySubclass->setObject(firstObj); arraySubclass->setObject(secondObj); // Test iteration over a subclassed OSArray, with a large iterator size osci = OSCollectionIterator::withCollection(arraySubclass); assert(osci != NULL); index = 0; while ((current = osci->getNextObject()) != NULL) { if (index == 0) { assert(current == firstObj); } else if (index == 1) { assert(current == secondObj); } else { panic("index out of range"); } index++; } OSSafeReleaseNULL(osci); // Test iteration with a OSCollectionIterator subclass over a subclassed OSArray, // with a large iterator size. osciSubclass = OSCISubclass::withCollection(arraySubclass); assert(osciSubclass != NULL); index = 0; while ((current = osciSubclass->getNextObject()) != NULL) { if (index == 0) { assert(current == firstObj); } else if (index == 1) { assert(current == secondObj); } else { panic("index out of range"); } index++; } OSSafeReleaseNULL(osciSubclass); OSSafeReleaseNULL(arraySubclass); OSSafeReleaseNULL(firstObj); OSSafeReleaseNULL(secondObj); return 0; } static int OSBoundedPtrTests(int) { int array[5] = {55, 66, 77, 88, 99}; OSBoundedPtr begin(&array[0], &array[0], &array[5]); OSBoundedPtr end(&array[5], &array[0], &array[5]); { int counter = 0; for (OSBoundedPtr b = begin; b != end; ++b) { *b = counter++; } } { int counter = 0; for (OSBoundedPtr b = begin; b != end; ++b) { assert(*b == counter); ++counter; } } return 0; } static int IOSharedDataQueue_44636964(__unused int newValue) { IOSharedDataQueue* sd = IOSharedDataQueue::withCapacity(DATA_QUEUE_ENTRY_HEADER_SIZE + sizeof(UInt64)); UInt64 data = 0x11223344aa55aa55; UInt32 data2 = 0x44332211; UInt32 size = sizeof(UInt32); /* enqueue moves tail to end */ sd->enqueue(&data, sizeof(UInt64)); /* dequeue moves head to end */ sd->dequeue(&data, &size); /* Tail wraps around, head is still at end */ sd->enqueue(&data2, sizeof(UInt32)); /* something in the queue so peek() should return non-null */ assert(sd->peek() != NULL); sd->release(); return KERN_SUCCESS; } #if 0 #include class TestUserClient : public IOUserClient { OSDeclareDefaultStructors(TestUserClient); virtual void stop( IOService *provider) APPLE_KEXT_OVERRIDE; virtual bool finalize(IOOptionBits options) APPLE_KEXT_OVERRIDE; virtual IOReturn externalMethod( uint32_t selector, IOExternalMethodArguments * arguments, IOExternalMethodDispatch * dispatch, OSObject * target, void * reference ) APPLE_KEXT_OVERRIDE; }; void TestUserClient::stop( IOService *provider) { kprintf("TestUserClient::stop\n"); } bool TestUserClient::finalize(IOOptionBits options) { kprintf("TestUserClient::finalize\n"); return true; } IOReturn TestUserClient::externalMethod( uint32_t selector, IOExternalMethodArguments * arguments, IOExternalMethodDispatch * dispatch, OSObject * target, void * reference ) { getProvider()->terminate(); IOSleep(500); return 0; } OSDefineMetaClassAndStructors(TestUserClient, IOUserClient); #endif static int IOServiceTest(int newValue) { OSDictionary * matching; IONotifier * note; __block IOService * found; #if 0 found = new IOService; found->init(); found->setName("IOTestUserClientProvider"); found->attach(IOService::getPlatform()); found->setProperty("IOUserClientClass", "TestUserClient"); found->registerService(); #endif matching = IOService::serviceMatching("IOPlatformExpert"); assert(matching); found = nullptr; note = IOService::addMatchingNotification(gIOMatchedNotification, matching, 0, ^bool (IOService * newService, IONotifier * notifier) { kprintf("found %s, %d\n", newService->getName(), newService->getRetainCount()); found = newService; found->retain(); return true; } ); assert(note); assert(found); matching->release(); note->remove(); note = found->registerInterest(gIOBusyInterest, ^IOReturn (uint32_t messageType, IOService * provider, void * messageArgument, size_t argSize) { kprintf("%p messageType 0x%08x %p\n", provider, messageType, messageArgument); return kIOReturnSuccess; }); assert(note); IOSleep(1 * 1000); note->remove(); found->release(); return 0; } static void OSStaticPtrCastTests() { // const& overload { OSSharedPtr const dict = OSMakeShared(); OSSharedPtr collection = OSStaticPtrCast(dict); assert(collection == dict); } { OSSharedPtr const dict = nullptr; OSSharedPtr collection = OSStaticPtrCast(dict); assert(collection == nullptr); } // && overload { OSSharedPtr dict = OSMakeShared(); OSDictionary* oldDict = dict.get(); OSSharedPtr collection = OSStaticPtrCast(os::move(dict)); assert(collection.get() == oldDict); assert(dict == nullptr); } { OSSharedPtr dict = nullptr; OSSharedPtr collection = OSStaticPtrCast(os::move(dict)); assert(collection == nullptr); assert(dict == nullptr); } } static void OSConstPtrCastTests() { // const& overload { OSSharedPtr const dict = OSMakeShared(); OSSharedPtr dict2 = OSConstPtrCast(dict); assert(dict2 == dict); } { OSSharedPtr const dict = OSMakeShared(); OSSharedPtr dict2 = OSConstPtrCast(dict); assert(dict2 == dict); } { OSSharedPtr const dict = nullptr; OSSharedPtr dict2 = OSConstPtrCast(dict); assert(dict2 == nullptr); } { OSSharedPtr const dict = nullptr; OSSharedPtr dict2 = OSConstPtrCast(dict); assert(dict2 == nullptr); } // && overload { OSSharedPtr dict = OSMakeShared(); OSDictionary const* oldDict = dict.get(); OSSharedPtr dict2 = OSConstPtrCast(os::move(dict)); assert(dict == nullptr); assert(dict2 == oldDict); } { OSSharedPtr dict = nullptr; OSSharedPtr dict2 = OSConstPtrCast(os::move(dict)); assert(dict == nullptr); assert(dict2 == nullptr); } } static void OSDynamicPtrCastTests() { OSSharedPtr const dict = OSMakeShared(); { OSSharedPtr collection = OSDynamicPtrCast(dict); assert(collection != nullptr); } { OSSharedPtr array = OSDynamicPtrCast(dict); assert(array == nullptr); assert(dict != nullptr); } { OSTaggedSharedPtr taggedDict(dict.get(), OSRetain); OSTaggedSharedPtr collection = OSDynamicPtrCast(taggedDict); assert(collection != nullptr); } { OSTaggedSharedPtr taggedDict(dict.get(), OSRetain); OSTaggedSharedPtr array = OSDynamicPtrCast(taggedDict); assert(array == nullptr); assert(dict != nullptr); } { OSSharedPtr collection = OSDynamicPtrCast(dict); assert(collection.get() == OSDynamicCast(OSDictionary, dict.get())); OSSharedPtr newDict = OSDynamicPtrCast(os::move(collection)); assert(collection == nullptr); assert(newDict != nullptr); assert(newDict.get() == dict.get()); } } class IOTestUserNotification : public IOUserNotification { OSDeclareDefaultStructors(IOTestUserNotification); }; OSDefineMetaClassAndStructors(IOTestUserNotification, IOUserNotification) struct IOUserNotificationTestThreadArgs { IOTestUserNotification * userNotify; IOLock * lock; size_t * completed; size_t iterations; }; static bool IOUserNotificationMatchingHandler( void * target __unused, void * ref __unused, IOService * newService __unused, IONotifier * notifier __unused ) { return true; } static void IOUserNotificationTestThread(void * arg, wait_result_t result __unused) { IOUserNotificationTestThreadArgs * threadArgs = (IOUserNotificationTestThreadArgs *)arg; OSDictionary * dict = OSDictionary::withCapacity(0); OSString * rootPath = OSString::withCStringNoCopy(":/"); dict->setObject(gIOPathMatchKey, rootPath); for (size_t i = 0; i < threadArgs->iterations; i++) { if (i % 2 == 0) { IONotifier * notify = IOService::addMatchingNotification( gIOWillTerminateNotification, dict, &IOUserNotificationMatchingHandler, NULL ); threadArgs->userNotify->setNotification(notify); } else { threadArgs->userNotify->setNotification(NULL); } } threadArgs->userNotify->setNotification(NULL); OSSafeReleaseNULL(rootPath); OSSafeReleaseNULL(dict); IOLockLock(threadArgs->lock); *threadArgs->completed = *threadArgs->completed + 1; IOLockWakeup(threadArgs->lock, (event_t)threadArgs->completed, true); IOLockUnlock(threadArgs->lock); } static int IOUserNotificationTests(__unused int newValue) { constexpr size_t numThreads = 10; constexpr size_t numIterations = 500000; IOTestUserNotification * userNotify = OSTypeAlloc(IOTestUserNotification); IOLock * lock = IOLockAlloc(); size_t threadsCompleted; size_t i = 0; thread_t threads[numThreads]; kern_return_t kr; bool result; struct IOUserNotificationTestThreadArgs threadArgs = { .userNotify = userNotify, .lock = lock, .completed = &threadsCompleted, .iterations = numIterations, }; result = userNotify->init(); assert(result); for (i = 0; i < numThreads; i++) { kr = kernel_thread_start(&IOUserNotificationTestThread, (void *)&threadArgs, &threads[i]); assert(kr == KERN_SUCCESS); } IOLockLock(lock); while (threadsCompleted < numThreads) { IOLockSleep(lock, &threadsCompleted, THREAD_UNINT); IOLog("%s: Threads completed: %zu/%zu\n", __func__, threadsCompleted, numThreads); } IOLockUnlock(lock); for (i = 0; i < numThreads; i++) { thread_deallocate(threads[i]); } userNotify->setNotification(NULL); OSSafeReleaseNULL(userNotify); IOLockFree(lock); return KERN_SUCCESS; } static void IOServiceMatchingSharedPtrTests() { const OSSymbol * name = OSSymbol::withCString("name"); const OSSymbol * value = OSSymbol::withCString("value"); { OSSharedPtr table; OSSharedPtr result = IOService::serviceMatching("name", table); assert(result); assert(result->getRetainCount() == 1); table = result; assert(table->getRetainCount() == 2); OSSharedPtr result2 = IOService::serviceMatching("name", table); assert(result2); assert(result2 == table); assert(result2->getRetainCount() == 3); } { OSSharedPtr table; OSSharedPtr result = IOService::serviceMatching(name, table); assert(result); assert(result->getRetainCount() == 1); table = result; assert(table->getRetainCount() == 2); OSSharedPtr result2 = IOService::serviceMatching(name, table); assert(result2); assert(result2 == table); assert(result2->getRetainCount() == 3); } { OSSharedPtr table; OSSharedPtr result = IOService::nameMatching("name", table); assert(result); assert(result->getRetainCount() == 1); table = result; assert(table->getRetainCount() == 2); OSSharedPtr result2 = IOService::nameMatching("name", table); assert(result2); assert(result2 == table); assert(result2->getRetainCount() == 3); } { OSSharedPtr table; OSSharedPtr result = IOService::nameMatching(name, table); assert(result); assert(result->getRetainCount() == 1); table = result; assert(table->getRetainCount() == 2); OSSharedPtr result2 = IOService::nameMatching(name, table); assert(result2); assert(result2 == table); assert(result2->getRetainCount() == 3); } { OSSharedPtr table; OSSharedPtr result = IOService::resourceMatching("name", table); assert(result); assert(result->getRetainCount() == 1); table = result; assert(table->getRetainCount() == 2); OSSharedPtr result2 = IOService::resourceMatching("name", table); assert(result2); assert(result2 == table); assert(result2->getRetainCount() == 3); } { OSSharedPtr table; OSSharedPtr result = IOService::resourceMatching(name, table); assert(result); assert(result->getRetainCount() == 1); table = result; assert(table->getRetainCount() == 2); OSSharedPtr result2 = IOService::resourceMatching(name, table); assert(result2); assert(result2 == table); assert(result2->getRetainCount() == 3); } { OSSharedPtr table; OSSharedPtr result = IOService::propertyMatching(name, value, table); assert(result); assert(result->getRetainCount() == 1); table = result; assert(table->getRetainCount() == 2); OSSharedPtr result2 = IOService::propertyMatching(name, value, table); assert(result2); assert(result2 == table); assert(result2->getRetainCount() == 3); } { OSSharedPtr table; OSSharedPtr result = IOService::registryEntryIDMatching(12, table); assert(result); assert(result->getRetainCount() == 1); table = result; assert(table->getRetainCount() == 2); OSSharedPtr result2 = IOService::registryEntryIDMatching(12, table); assert(result2); assert(result2 == table); assert(result2->getRetainCount() == 3); } OSSafeReleaseNULL(name); OSSafeReleaseNULL(value); } static int OSSharedPtrTests(int) { OSDynamicPtrCastTests(); OSConstPtrCastTests(); OSStaticPtrCastTests(); IOServiceMatchingSharedPtrTests(); return 0; } static int IOServiceStateNotificationTests(int) { IOService * service = IOService::getSystemStateNotificationService(); OSString * str = OSString::withCString(kIOSystemStateClamshellKey); kern_return_t kr = service->StateNotificationItemCreate(str, NULL); assert(kIOReturnSuccess == kr); void (^sendClam)(OSBoolean * state) = ^void (OSBoolean * state) { OSDictionary * value; kern_return_t kr; value = OSDictionary::withCapacity(4); value->setObject("value", state); kr = IOService::getSystemStateNotificationService()->StateNotificationItemSet(str, value); assert(kIOReturnSuccess == kr); value->release(); }; sendClam(kOSBooleanTrue); IOSleep(100); sendClam(kOSBooleanFalse); str->release(); str = OSString::withCString("test"); OSArray * array = OSArray::withCapacity(4); array->setObject(str); IOStateNotificationListenerRef listenerRef; kr = service->stateNotificationListenerAdd(array, &listenerRef, ^kern_return_t () { IOLog("test handler\n"); kern_return_t kr; OSDictionary * dict; kr = service->StateNotificationItemCopy(str, &dict); if (kIOReturnSuccess == kr) { OSSerialize * s = OSSerialize::withCapacity(4096); dict->serialize(s); IOLog("test handler %s\n", s->text()); s->release(); } return kIOReturnSuccess; }); assert(kIOReturnSuccess == kr); IOEventSource * es = IOServiceStateNotificationEventSource::serviceStateNotificationEventSource( service, array, ^void () { IOLog("test es handler\n"); kern_return_t kr; OSDictionary * dict; kr = service->StateNotificationItemCopy(str, &dict); if (kIOReturnSuccess == kr) { OSSerialize * s = OSSerialize::withCapacity(4096); dict->serialize(s); IOLog("test es handler %s\n", s->text()); s->release(); } }); assert(es != nullptr); IOService::getPlatform()->getWorkLoop()->addEventSource(es); es->enable(); IOSleep(30 * 1000); IOService::getPlatform()->getWorkLoop()->removeEventSource(es); es->release(); return kIOReturnSuccess; } #endif /* DEVELOPMENT || DEBUG */ #ifndef __clang_analyzer__ // All the scary things that this function is doing, such as the intentional // overrelease of an OSData, are hidden from the static analyzer. static int sysctl_iokittest(__unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req) { int error; int newValue, changed; error = sysctl_io_number(req, 0, sizeof(int), &newValue, &changed); if (error) { return error; } #if DEVELOPMENT || DEBUG if (changed && (66 == newValue)) { IOReturn ret; IOWorkLoop * wl = IOWorkLoop::workLoop(); IOCommandGate * cg = IOCommandGate::commandGate(wl); ret = wl->addEventSource(cg); struct x { uint64_t h; uint64_t l; }; struct x y; y.h = 0x1111111122222222; y.l = 0x3333333344444444; kprintf("ret1 %d\n", ret); ret = cg->runActionBlock(^(){ printf("hello %d 0x%qx\n", wl->inGate(), y.h); return 99; }); kprintf("ret %d\n", ret); } if (changed && (999 == newValue)) { OSData * data = OSData::withCapacity(16); data->release(); data->release(); } if (changed && (newValue >= 6666) && (newValue <= 6669)) { OSIterator * iter; IOService * service; service = NULL; iter = IOService::getMatchingServices(IOService::nameMatching("XHC1")); if (iter && (service = (IOService *) iter->getNextObject())) { if (newValue == 6666) { IOLog("terminating 0x%qx\n", service->getRegistryEntryID()); service->terminate(); } else if (newValue == 6667) { IOLog("register 0x%qx\n", service->getRegistryEntryID()); service->registerService(); } } OSSafeReleaseNULL(iter); if (service) { return 0; } } if (changed && (555 == newValue)) { IOServiceStateNotificationTests(newValue); } if (changed && newValue) { error = IOWorkLoopTest(newValue); assert(KERN_SUCCESS == error); error = IOServiceTest(newValue); assert(KERN_SUCCESS == error); error = OSCollectionTest(newValue); assert(KERN_SUCCESS == error); error = OSCollectionIteratorTests(newValue); assert(KERN_SUCCESS == error); error = OSAllocationTests(newValue); assert(KERN_SUCCESS == error); error = OSDataAllocationTests(newValue); assert(KERN_SUCCESS == error); error = OSBoundedArrayTests(newValue); assert(KERN_SUCCESS == error); error = OSBoundedArrayRefTests(newValue); assert(KERN_SUCCESS == error); error = OSBoundedPtrTests(newValue); assert(KERN_SUCCESS == error); error = IOMemoryDescriptorTest(newValue); assert(KERN_SUCCESS == error); error = OSSharedPtrTests(newValue); assert(KERN_SUCCESS == error); error = IOSharedDataQueue_44636964(newValue); assert(KERN_SUCCESS == error); error = IOUserNotificationTests(newValue); assert(KERN_SUCCESS == error); } #endif /* DEVELOPMENT || DEBUG */ return error; } SYSCTL_PROC(_kern, OID_AUTO, iokittest, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_KERN | CTLFLAG_LOCKED, NULL, 0, sysctl_iokittest, "I", ""); #endif // __clang_analyzer__ #if DEVELOPMENT || DEBUG /* * A simple wrapper around an IOService. This terminates the IOService in free(). */ class TestIOServiceHandle : public OSObject { OSDeclareDefaultStructors(TestIOServiceHandle); public: static TestIOServiceHandle * withService(IOService * service); private: bool initWithService(IOService * service); virtual void free() APPLE_KEXT_OVERRIDE; IOService * fService; }; OSDefineMetaClassAndStructors(TestIOServiceHandle, OSObject); TestIOServiceHandle * TestIOServiceHandle::withService(IOService * service) { TestIOServiceHandle * handle = new TestIOServiceHandle; if (handle && !handle->initWithService(service)) { return NULL; } return handle; } bool TestIOServiceHandle::initWithService(IOService * service) { fService = service; fService->retain(); return true; } void TestIOServiceHandle::free() { if (fService) { fService->terminate(); OSSafeReleaseNULL(fService); } } /* * Set up test IOServices. See the available services in xnu/iokit/Tests/TestServices. * * xnu darwintests use this sysctl to make these test services available. A send right is pushed * to the task that called the sysctl, which when deallocated removes the service. This ensures * that the registry isn't polluted by misbehaving tests. * * Since this sysctl allows callers to instantiate arbitrary classes based on their class name, * this can be a security concern. Tests that call this sysctl need the * kIOServiceTestServiceManagementEntitlementKey entitlement. */ static int sysctl_iokit_test_service_setup(struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req) { char classname[128] = {0}; IOService * service; // must not release OSObject * obj = NULL; // must release IOService * provider = NULL; // must not release TestIOServiceHandle * handle = NULL; // must release mach_port_name_t name __unused; int error; if (!IOTaskHasEntitlement(current_task(), kIOServiceTestServiceManagementEntitlementKey)) { error = EPERM; goto finish; } error = sysctl_handle_string(oidp, classname, sizeof(classname), req); if (error != 0) { goto finish; } /* * All test services currently attach to IOResources. */ provider = IOService::getResourceService(); if (!provider) { IOLog("Failed to find IOResources\n"); error = ENOENT; goto finish; } obj = OSMetaClass::allocClassWithName(classname); if (!obj) { IOLog("Failed to alloc class %s\n", classname); error = ENOENT; goto finish; } service = OSDynamicCast(IOService, obj); if (!service) { IOLog("Instance of class %s is not an IOService\n", classname); error = EINVAL; goto finish; } if (!service->init()) { IOLog("Failed to initialize %s\n", classname); error = EINVAL; goto finish; } if (!service->attach(provider)) { IOLog("Failed to attach %s\n", classname); error = EINVAL; goto finish; } if (!service->start(provider)) { IOLog("Failed to start %s\n", classname); error = EINVAL; goto finish; } handle = TestIOServiceHandle::withService(service); if (!handle) { IOLog("Failed to create service handle\n"); error = ENOMEM; goto finish; } name = iokit_make_send_right(current_task(), handle, IKOT_IOKIT_OBJECT); error = 0; finish: OSSafeReleaseNULL(obj); OSSafeReleaseNULL(handle); return error; } SYSCTL_PROC(_kern, OID_AUTO, iokit_test_service_setup, CTLTYPE_STRING | CTLFLAG_WR | CTLFLAG_KERN | CTLFLAG_LOCKED, NULL, 0, sysctl_iokit_test_service_setup, "-", ""); #endif /* DEVELOPMENT || DEBUG */ static __unused void CastCompileTest(OSObject *obj) { OSDynamicCast(IOService, obj)->terminate(); OSRequiredCast(IOService, obj)->terminate(); }