/* * Copyright (c) 1998-2021 Apple 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@ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "IOServicePrivate.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "IOKitKernelInternal.h" /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ TUNABLE(SInt64, gIODKDebug, "dk", kIODKEnable); #if DEBUG || DEVELOPMENT TUNABLE(bool, disable_dext_crash_reboot, "disable_dext_crash_reboot", 0); #endif /* DEBUG || DEVELOPMENT */ extern bool restore_boot; static OSString * gIOSystemStateSleepDescriptionKey; static const OSSymbol * gIOSystemStateSleepDescriptionReasonKey; static const OSSymbol * gIOSystemStateSleepDescriptionHibernateStateKey; static OSString * gIOSystemStateWakeDescriptionKey; static const OSSymbol * gIOSystemStateWakeDescriptionWakeReasonKey; static const OSSymbol * gIOSystemStateWakeDescriptionContinuousTimeOffsetKey; static OSString * gIOSystemStateHaltDescriptionKey; static const OSSymbol * gIOSystemStateHaltDescriptionHaltStateKey; static OSString * gIOSystemStatePowerSourceDescriptionKey; static const OSSymbol * gIOSystemStatePowerSourceDescriptionACAttachedKey; extern bool gInUserspaceReboot; extern void iokit_clear_registered_ports(task_t task); static IORPCMessage * IORPCMessageFromMachReply(IORPCMessageMach * msg); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct IOPStrings; class OSUserMetaClass : public OSObject { OSDeclareDefaultStructors(OSUserMetaClass); public: const OSSymbol * name; const OSMetaClass * meta; OSUserMetaClass * superMeta; queue_chain_t link; OSClassDescription * description; IOPStrings * queueNames; uint32_t methodCount; uint64_t * methods; virtual void free() override; virtual kern_return_t Dispatch(const IORPC rpc) APPLE_KEXT_OVERRIDE; }; OSDefineMetaClassAndStructors(OSUserMetaClass, OSObject); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ class IOUserService : public IOService { friend class IOService; OSDeclareDefaultStructors(IOUserService) virtual bool start(IOService * provider) APPLE_KEXT_OVERRIDE; }; OSDefineMetaClassAndStructors(IOUserService, IOService) /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ class IOUserUserClient : public IOUserClient { OSDeclareDefaultStructors(IOUserUserClient); public: task_t fTask; OSDictionary * fWorkGroups; OSDictionary * fEventLinks; IOLock * fLock; IOReturn setTask(task_t task); IOReturn eventlinkConfigurationTrap(void * p1, void * p2, void * p3, void * p4, void * p5, void * p6); IOReturn workgroupConfigurationTrap(void * p1, void * p2, void * p3, void * p4, void * p5, void * p6); virtual bool init( OSDictionary * dictionary ) APPLE_KEXT_OVERRIDE; virtual void free() APPLE_KEXT_OVERRIDE; virtual void stop(IOService * provider) APPLE_KEXT_OVERRIDE; virtual IOReturn clientClose(void) APPLE_KEXT_OVERRIDE; virtual IOReturn setProperties(OSObject * properties) APPLE_KEXT_OVERRIDE; virtual IOReturn externalMethod(uint32_t selector, IOExternalMethodArguments * args, IOExternalMethodDispatch * dispatch, OSObject * target, void * reference) APPLE_KEXT_OVERRIDE; virtual IOReturn clientMemoryForType(UInt32 type, IOOptionBits * options, IOMemoryDescriptor ** memory) APPLE_KEXT_OVERRIDE; virtual IOExternalTrap * getTargetAndTrapForIndex( IOService **targetP, UInt32 index ) APPLE_KEXT_OVERRIDE; }; OSDefineMetaClassAndStructors(IOUserServerCheckInToken, OSObject); OSDefineMetaClassAndStructors(_IOUserServerCheckInCancellationHandler, OSObject); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ bool IOUserService::start(IOService * provider) { bool ok = true; IOReturn ret; ret = Start(provider); if (kIOReturnSuccess != ret) { return false; } return ok; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #undef super /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct IODispatchQueue_IVars { IOUserServer * userServer; IODispatchQueue * queue; queue_chain_t link; uint64_t tid; mach_port_t serverPort; }; struct OSAction_IVars { OSObject * target; uint64_t targetmsgid; uint64_t msgid; IOUserServer * userServer; OSActionAbortedHandler abortedHandler; OSString * typeName; void * reference; size_t referenceSize; bool aborted; }; struct IOWorkGroup_IVars { IOUserServer * userServer; OSString * name; IOUserUserClient * userClient; }; struct IOEventLink_IVars { IOUserServer * userServer; OSString * name; IOUserUserClient * userClient; }; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ kern_return_t IOService::GetRegistryEntryID_Impl( uint64_t * registryEntryID) { IOReturn ret = kIOReturnSuccess; *registryEntryID = getRegistryEntryID(); return ret; } kern_return_t IOService::SetName_Impl( const char * name) { IOReturn ret = kIOReturnSuccess; setName(name); return ret; } kern_return_t IOService::CopyName_Impl( OSString ** name) { const OSString * str = copyName(); *name = __DECONST(OSString *, str); return str ? kIOReturnSuccess : kIOReturnError; } kern_return_t IOService::Start_Impl( IOService * provider) { IOReturn ret = kIOReturnSuccess; return ret; } IOReturn IOService::UpdateReport_Impl(OSData *channels, uint32_t action, uint32_t *outElementCount, uint64_t offset, uint64_t capacity, IOMemoryDescriptor *buffer) { return kIOReturnUnsupported; } IOReturn IOService::ConfigureReport_Impl(OSData *channels, uint32_t action, uint32_t *outCount) { return kIOReturnUnsupported; } // adapt old signature of configureReport to the iig-friendly signature of ConfigureReport IOReturn IOService::_ConfigureReport(IOReportChannelList *channelList, IOReportConfigureAction action, void *result, void *destination) { if (action != kIOReportEnable && action != kIOReportGetDimensions && action != kIOReportDisable) { return kIOReturnUnsupported; } static_assert(sizeof(IOReportChannelList) == 8); static_assert(sizeof(IOReportChannel) == 16); unsigned int size_of_channels; bool overflow = os_mul_and_add_overflow(channelList->nchannels, sizeof(IOReportChannel), sizeof(IOReportChannelList), &size_of_channels); if (overflow) { return kIOReturnOverrun; } OSSharedPtr sp_channels(OSData::withBytesNoCopy(channelList, size_of_channels), libkern::no_retain); if (!sp_channels) { return kIOReturnNoMemory; } int *resultp = (int*) result; uint32_t count = 0; IOReturn r = ConfigureReport(sp_channels.get(), action, &count); int new_result; overflow = os_add_overflow(*resultp, count, &new_result); if (overflow) { return kIOReturnOverrun; } *resultp = new_result; return r; } // adapt old signature of updateReport to the iig-friendly signature of UpdateReport IOReturn IOService::_UpdateReport(IOReportChannelList *channelList, IOReportUpdateAction action, void *result, void *destination) { if (action != kIOReportCopyChannelData) { return kIOReturnUnsupported; } unsigned int size_of_channels; bool overflow = os_mul_and_add_overflow(channelList->nchannels, sizeof(IOReportChannel), sizeof(IOReportChannelList), &size_of_channels); if (overflow) { return kIOReturnOverrun; } OSSharedPtr sp_channels(OSData::withBytesNoCopy(channelList, size_of_channels), libkern::no_retain); if (!sp_channels) { return kIOReturnNoMemory; } int *resultp = (int*) result; uint32_t count = 0; auto buffer = (IOBufferMemoryDescriptor*) destination; uint64_t length = buffer->getLength(); buffer->setLength(buffer->getCapacity()); IOReturn r = UpdateReport(sp_channels.get(), action, &count, length, buffer->getCapacity() - length, buffer); int new_result; overflow = os_add_overflow(*resultp, count, &new_result); size_t new_length; overflow = overflow || os_mul_and_add_overflow(count, sizeof(IOReportElement), length, &new_length); if (overflow || new_length > buffer->getCapacity()) { buffer->setLength(length); return kIOReturnOverrun; } *resultp = new_result; buffer->setLength(new_length); return r; } IOReturn IOService::SetLegend_Impl(OSArray *legend, bool is_public) { bool ok = setProperty(kIOReportLegendKey, legend); ok = ok && setProperty(kIOReportLegendPublicKey, is_public); return ok ? kIOReturnSuccess : kIOReturnError; } kern_return_t IOService::RegisterService_Impl() { IOReturn ret = kIOReturnSuccess; bool started; IOUserServer *us = (typeof(us))thread_iokit_tls_get(0); if (reserved != NULL && reserved->uvars != NULL && reserved->uvars->userServer == us) { started = reserved->uvars->started; } else { // assume started started = true; } if (OSDynamicCast(IOUserServer, this) != NULL || started) { registerService(kIOServiceAsynchronous); } else { assert(reserved != NULL && reserved->uvars != NULL); reserved->uvars->deferredRegisterService = true; } return ret; } kern_return_t IOService::CopyDispatchQueue_Impl( const char * name, IODispatchQueue ** queue) { IODispatchQueue * result; IOService * service; IOReturn ret; uint32_t index; if (!reserved->uvars) { return kIOReturnError; } if (!reserved->uvars->queueArray) { // CopyDispatchQueue should not be called after the service has stopped return kIOReturnError; } ret = kIOReturnNotFound; index = -1U; if (!strcmp("Default", name)) { index = 0; } else if (reserved->uvars->userMeta && reserved->uvars->userMeta->queueNames) { index = reserved->uvars->userServer->stringArrayIndex(reserved->uvars->userMeta->queueNames, name); if (index != -1U) { index++; } } if (index == -1U) { if ((service = getProvider())) { ret = service->CopyDispatchQueue(name, queue); } } else { result = reserved->uvars->queueArray[index]; if (result) { result->retain(); *queue = result; ret = kIOReturnSuccess; } } return ret; } kern_return_t IOService::CreateDefaultDispatchQueue_Impl( IODispatchQueue ** queue) { return kIOReturnError; } kern_return_t IOService::CoreAnalyticsSendEvent_Impl( uint64_t options, OSString * eventName, OSDictionary * eventPayload) { kern_return_t ret; if (NULL == gIOCoreAnalyticsSendEventProc) { // perhaps save for later? return kIOReturnNotReady; } ret = (*gIOCoreAnalyticsSendEventProc)(options, eventName, eventPayload); return ret; } kern_return_t IOService::SetDispatchQueue_Impl( const char * name, IODispatchQueue * queue) { IOReturn ret = kIOReturnSuccess; uint32_t index; if (!reserved->uvars) { return kIOReturnError; } if (kIODKLogSetup & gIODKDebug) { DKLOG(DKS "::SetDispatchQueue(%s)\n", DKN(this), name); } queue->ivars->userServer = reserved->uvars->userServer; index = -1U; if (!strcmp("Default", name)) { index = 0; } else if (reserved->uvars->userMeta && reserved->uvars->userMeta->queueNames) { index = reserved->uvars->userServer->stringArrayIndex(reserved->uvars->userMeta->queueNames, name); if (index != -1U) { index++; } } if (index == -1U) { ret = kIOReturnBadArgument; } else { reserved->uvars->queueArray[index] = queue; queue->retain(); } return ret; } IOService * IOService::GetProvider() const { return getProvider(); } kern_return_t IOService::SetProperties_Impl( OSDictionary * properties) { IOUserServer * us; OSDictionary * dict; IOReturn ret; us = (typeof(us))thread_iokit_tls_get(0); dict = OSDynamicCast(OSDictionary, properties); if (NULL == us) { if (!dict) { return kIOReturnBadArgument; } bool ok __block = true; dict->iterateObjects(^bool (const OSSymbol * key, OSObject * value) { ok = setProperty(key, value); return !ok; }); ret = ok ? kIOReturnSuccess : kIOReturnNotWritable; return ret; } ret = setProperties(properties); if (kIOReturnUnsupported == ret) { if (dict && reserved->uvars && (reserved->uvars->userServer == us)) { ret = runPropertyActionBlock(^IOReturn (void) { OSDictionary * userProps; IOReturn ret; userProps = OSDynamicCast(OSDictionary, getProperty(gIOUserServicePropertiesKey)); if (userProps) { userProps = (typeof(userProps))userProps->copyCollection(); } else { userProps = OSDictionary::withCapacity(4); } if (!userProps) { ret = kIOReturnNoMemory; } else { bool ok = userProps->merge(dict); if (ok) { ok = setProperty(gIOUserServicePropertiesKey, userProps); } OSSafeReleaseNULL(userProps); ret = ok ? kIOReturnSuccess : kIOReturnNotWritable; } return ret; }); } } return ret; } kern_return_t IOService::RemoveProperty_Impl(OSString * propertyName) { IOUserServer * us = (IOUserServer *)thread_iokit_tls_get(0); IOReturn ret = kIOReturnUnsupported; if (NULL == propertyName) { return kIOReturnUnsupported; } if (NULL == us) { removeProperty(propertyName); return kIOReturnSuccess; } if (reserved && reserved->uvars && reserved->uvars->userServer == us) { ret = runPropertyActionBlock(^IOReturn (void) { OSDictionary * userProps; userProps = OSDynamicCast(OSDictionary, getProperty(gIOUserServicePropertiesKey)); if (userProps) { userProps = (OSDictionary *)userProps->copyCollection(); if (!userProps) { return kIOReturnNoMemory; } userProps->removeObject(propertyName); bool ok = setProperty(gIOUserServicePropertiesKey, userProps); OSSafeReleaseNULL(userProps); return ok ? kIOReturnSuccess : kIOReturnNotWritable; } else { return kIOReturnNotFound; } }); } return ret; } kern_return_t IOService::CopyProperties_Local( OSDictionary ** properties) { OSDictionary * props; OSDictionary * userProps; props = dictionaryWithProperties(); userProps = OSDynamicCast(OSDictionary, props->getObject(gIOUserServicePropertiesKey)); if (userProps) { props->merge(userProps); props->removeObject(gIOUserServicePropertiesKey); } *properties = props; return props ? kIOReturnSuccess : kIOReturnNoMemory; } kern_return_t IOService::CopyProperties_Impl( OSDictionary ** properties) { return CopyProperties_Local(properties); } kern_return_t IOService::RequireMaxBusStall_Impl( uint64_t u64ns) { IOReturn ret; UInt32 ns; if (os_convert_overflow(u64ns, &ns)) { return kIOReturnBadArgument; } ret = requireMaxBusStall(ns); return ret; } #if PRIVATE_WIFI_ONLY kern_return_t IOService::UserSetProperties_Impl( OSContainer * properties) { return kIOReturnUnsupported; } kern_return_t IOService::SendIOMessageServicePropertyChange_Impl(void) { return messageClients(kIOMessageServicePropertyChange); } #endif /* PRIVATE_WIFI_ONLY */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ kern_return_t IOMemoryDescriptor::_CopyState_Impl( _IOMDPrivateState * state) { IOReturn ret; state->length = _length; state->options = _flags; ret = kIOReturnSuccess; return ret; } kern_return_t IOMemoryDescriptor::GetLength(uint64_t * returnLength) { *returnLength = getLength(); return kIOReturnSuccess; } kern_return_t IOMemoryDescriptor::CreateMapping_Impl( uint64_t options, uint64_t address, uint64_t offset, uint64_t length, uint64_t alignment, IOMemoryMap ** map) { IOReturn ret; IOMemoryMap * resultMap; IOOptionBits koptions; mach_vm_address_t atAddress; ret = kIOReturnSuccess; koptions = 0; resultMap = NULL; if (kIOMemoryMapFixedAddress & options) { atAddress = address; koptions = 0; } else { switch (kIOMemoryMapGuardedMask & options) { default: case kIOMemoryMapGuardedDefault: koptions |= kIOMapGuardedSmall; break; case kIOMemoryMapGuardedNone: break; case kIOMemoryMapGuardedSmall: koptions |= kIOMapGuardedSmall; break; case kIOMemoryMapGuardedLarge: koptions |= kIOMapGuardedLarge; break; } atAddress = 0; koptions |= kIOMapAnywhere; } if ((kIOMemoryMapReadOnly & options) || (kIODirectionOut == getDirection())) { if (!reserved || (current_task() != reserved->creator)) { koptions |= kIOMapReadOnly; } } switch (0xFF00 & options) { case kIOMemoryMapCacheModeDefault: koptions |= kIOMapDefaultCache; break; case kIOMemoryMapCacheModeInhibit: koptions |= kIOMapInhibitCache; break; case kIOMemoryMapCacheModeCopyback: koptions |= kIOMapCopybackCache; break; case kIOMemoryMapCacheModeWriteThrough: koptions |= kIOMapWriteThruCache; break; case kIOMemoryMapCacheModeRealTime: koptions |= kIOMapRealTimeCache; break; default: ret = kIOReturnBadArgument; } if (kIOReturnSuccess == ret) { resultMap = createMappingInTask(current_task(), atAddress, koptions, offset, length); if (!resultMap) { ret = kIOReturnError; } } *map = resultMap; return ret; } kern_return_t IOMemoryDescriptor::CreateSubMemoryDescriptor_Impl( uint64_t memoryDescriptorCreateOptions, uint64_t offset, uint64_t length, IOMemoryDescriptor * ofDescriptor, IOMemoryDescriptor ** memory) { IOReturn ret; IOMemoryDescriptor * iomd; IOByteCount mdOffset; IOByteCount mdLength; IOByteCount mdEnd; if (!ofDescriptor) { return kIOReturnBadArgument; } if (memoryDescriptorCreateOptions & ~kIOMemoryDirectionOutIn) { return kIOReturnBadArgument; } if (os_convert_overflow(offset, &mdOffset)) { return kIOReturnBadArgument; } if (os_convert_overflow(length, &mdLength)) { return kIOReturnBadArgument; } if (os_add_overflow(mdOffset, mdLength, &mdEnd)) { return kIOReturnBadArgument; } if (mdEnd > ofDescriptor->getLength()) { return kIOReturnBadArgument; } iomd = IOSubMemoryDescriptor::withSubRange( ofDescriptor, mdOffset, mdLength, (IOOptionBits) memoryDescriptorCreateOptions); if (iomd) { ret = kIOReturnSuccess; *memory = iomd; } else { ret = kIOReturnNoMemory; *memory = NULL; } return ret; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ kern_return_t IOMemoryDescriptor::CreateWithMemoryDescriptors_Impl( uint64_t memoryDescriptorCreateOptions, uint32_t withDescriptorsCount, IOMemoryDescriptor ** const withDescriptors, IOMemoryDescriptor ** memory) { IOReturn ret; IOMemoryDescriptor * iomd; if (!withDescriptors) { return kIOReturnBadArgument; } if (!withDescriptorsCount) { return kIOReturnBadArgument; } if (memoryDescriptorCreateOptions & ~kIOMemoryDirectionOutIn) { return kIOReturnBadArgument; } for (unsigned int idx = 0; idx < withDescriptorsCount; idx++) { if (NULL == withDescriptors[idx]) { return kIOReturnBadArgument; } } iomd = IOMultiMemoryDescriptor::withDescriptors(withDescriptors, withDescriptorsCount, (IODirection) memoryDescriptorCreateOptions, false); if (iomd) { ret = kIOReturnSuccess; *memory = iomd; } else { ret = kIOReturnNoMemory; *memory = NULL; } return ret; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ kern_return_t IOUserClient::CreateMemoryDescriptorFromClient_Impl( uint64_t memoryDescriptorCreateOptions, uint32_t segmentsCount, const IOAddressSegment segments[32], IOMemoryDescriptor ** memory) { IOReturn ret; IOMemoryDescriptor * iomd; IOOptionBits mdOptions; IOUserUserClient * me; IOAddressRange * ranges; me = OSDynamicCast(IOUserUserClient, this); if (!me) { return kIOReturnBadArgument; } if (!me->fTask) { return kIOReturnNotReady; } mdOptions = kIOMemoryThreadSafe; if (kIOMemoryDirectionOut & memoryDescriptorCreateOptions) { mdOptions |= kIODirectionOut; } if (kIOMemoryDirectionIn & memoryDescriptorCreateOptions) { mdOptions |= kIODirectionIn; } if (!(kIOMemoryDisableCopyOnWrite & memoryDescriptorCreateOptions)) { mdOptions |= kIOMemoryMapCopyOnWrite; } static_assert(sizeof(IOAddressRange) == sizeof(IOAddressSegment)); ranges = __DECONST(IOAddressRange *, &segments[0]); iomd = IOMemoryDescriptor::withAddressRanges( ranges, segmentsCount, mdOptions, me->fTask); if (iomd) { ret = kIOReturnSuccess; *memory = iomd; } else { ret = kIOReturnNoMemory; *memory = NULL; } return ret; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ kern_return_t IOMemoryMap::_CopyState_Impl( _IOMemoryMapPrivateState * state) { IOReturn ret; state->offset = fOffset; state->length = getLength(); state->address = getAddress(); state->options = getMapOptions(); ret = kIOReturnSuccess; return ret; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ kern_return_t IOBufferMemoryDescriptor::Create_Impl( uint64_t options, uint64_t capacity, uint64_t alignment, IOBufferMemoryDescriptor ** memory) { IOReturn ret; IOOptionBits bmdOptions; IOBufferMemoryDescriptor * bmd; IOMemoryDescriptorReserved * reserved; if (options & ~((uint64_t) kIOMemoryDirectionOutIn)) { // no other options currently defined return kIOReturnBadArgument; } bmdOptions = (options & kIOMemoryDirectionOutIn) | kIOMemoryKernelUserShared | kIOMemoryThreadSafe; bmd = IOBufferMemoryDescriptor::inTaskWithOptions( kernel_task, bmdOptions, capacity, alignment); *memory = bmd; if (!bmd) { return kIOReturnNoMemory; } reserved = bmd->getKernelReserved(); reserved->creator = current_task(); task_reference(reserved->creator); ret = kIOReturnSuccess; return ret; } kern_return_t IOBufferMemoryDescriptor::SetLength_Impl( uint64_t length) { setLength(length); return kIOReturnSuccess; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ kern_return_t IODMACommand::Create_Impl( IOService * device, uint64_t options, const IODMACommandSpecification * specification, IODMACommand ** command) { IOReturn ret; IODMACommand * dma; IODMACommand::SegmentOptions segmentOptions; IOMapper * mapper; if (options & ~((uint64_t) kIODMACommandCreateNoOptions)) { // no other options currently defined return kIOReturnBadArgument; } if (os_convert_overflow(specification->maxAddressBits, &segmentOptions.fNumAddressBits)) { return kIOReturnBadArgument; } segmentOptions.fMaxSegmentSize = 0; segmentOptions.fMaxTransferSize = 0; segmentOptions.fAlignment = 1; segmentOptions.fAlignmentLength = 1; segmentOptions.fAlignmentInternalSegments = 1; segmentOptions.fStructSize = sizeof(segmentOptions); mapper = IOMapper::copyMapperForDevice(device); dma = IODMACommand::withSpecification( kIODMACommandOutputHost64, &segmentOptions, kIODMAMapOptionDextOwner | kIODMAMapOptionMapped, mapper, NULL); OSSafeReleaseNULL(mapper); *command = dma; if (!dma) { return kIOReturnNoMemory; } ret = kIOReturnSuccess; return ret; } #define fInternalState reserved kern_return_t IODMACommand::PrepareForDMA_Impl( uint64_t options, IOMemoryDescriptor * memory, uint64_t offset, uint64_t length, uint64_t * flags, uint32_t * segmentsCount, IOAddressSegment * segments) { IOReturn ret; uint64_t lflags, mdFlags; UInt32 numSegments; UInt64 genOffset; if (options & ~((uint64_t) kIODMACommandPrepareForDMANoOptions)) { // no other options currently defined return kIOReturnBadArgument; } if (memory == NULL) { return kIOReturnBadArgument; } assert(fInternalState->fDextLock); IOLockLock(fInternalState->fDextLock); // uses IOMD direction ret = memory->prepare(); if (kIOReturnSuccess != ret) { goto exit; } ret = setMemoryDescriptor(memory, false); if (kIOReturnSuccess != ret) { memory->complete(); goto exit; } ret = prepare(offset, length); if (kIOReturnSuccess != ret) { clearMemoryDescriptor(false); memory->complete(); goto exit; } static_assert(sizeof(IODMACommand::Segment64) == sizeof(IOAddressSegment)); numSegments = *segmentsCount; genOffset = 0; ret = genIOVMSegments(&genOffset, segments, &numSegments); if (kIOReturnSuccess != ret) { clearMemoryDescriptor(true); memory->complete(); goto exit; } mdFlags = fMemory->getFlags(); lflags = 0; if (kIODirectionOut & mdFlags) { lflags |= kIOMemoryDirectionOut; } if (kIODirectionIn & mdFlags) { lflags |= kIOMemoryDirectionIn; } *flags = lflags; *segmentsCount = numSegments; exit: IOLockUnlock(fInternalState->fDextLock); return ret; } kern_return_t IODMACommand::CompleteDMA_Impl( uint64_t options) { IOReturn ret, completeRet; IOMemoryDescriptor * md; if (options & ~((uint64_t) kIODMACommandCompleteDMANoOptions)) { // no other options currently defined return kIOReturnBadArgument; } assert(fInternalState->fDextLock); IOLockLock(fInternalState->fDextLock); if (!fInternalState->fPrepared) { ret = kIOReturnNotReady; goto exit; } md = __DECONST(IOMemoryDescriptor *, fMemory); if (md) { md->retain(); } ret = clearMemoryDescriptor(true); if (md) { completeRet = md->complete(); OSSafeReleaseNULL(md); if (kIOReturnSuccess == ret) { ret = completeRet; } } exit: IOLockUnlock(fInternalState->fDextLock); return ret; } kern_return_t IODMACommand::GetPreparation_Impl( uint64_t * offset, uint64_t * length, IOMemoryDescriptor ** memory) { IOReturn ret; IOMemoryDescriptor * md; if (!fActive) { return kIOReturnNotReady; } ret = getPreparedOffsetAndLength(offset, length); if (kIOReturnSuccess != ret) { return ret; } if (memory) { md = __DECONST(IOMemoryDescriptor *, fMemory); *memory = md; if (!md) { ret = kIOReturnNotReady; } else { md->retain(); } } return ret; } kern_return_t IODMACommand::PerformOperation_Impl( uint64_t options, uint64_t dmaOffset, uint64_t length, uint64_t dataOffset, IOMemoryDescriptor * data) { IOReturn ret; OSDataAllocation buffer; UInt64 copiedDMA; IOByteCount mdOffset, mdLength, copied; if (options & ~((uint64_t) (kIODMACommandPerformOperationOptionRead | kIODMACommandPerformOperationOptionWrite | kIODMACommandPerformOperationOptionZero))) { // no other options currently defined return kIOReturnBadArgument; } if (!fActive) { return kIOReturnNotReady; } if (os_convert_overflow(dataOffset, &mdOffset)) { return kIOReturnBadArgument; } if (os_convert_overflow(length, &mdLength)) { return kIOReturnBadArgument; } if (length > fMemory->getLength()) { return kIOReturnBadArgument; } buffer = OSDataAllocation(length, OSAllocateMemory); if (!buffer) { return kIOReturnNoMemory; } switch (options) { case kIODMACommandPerformOperationOptionZero: bzero(buffer.data(), length); copiedDMA = writeBytes(dmaOffset, buffer.data(), length); if (copiedDMA != length) { ret = kIOReturnUnderrun; break; } ret = kIOReturnSuccess; break; case kIODMACommandPerformOperationOptionRead: case kIODMACommandPerformOperationOptionWrite: if (!data) { ret = kIOReturnBadArgument; break; } if (length > data->getLength()) { ret = kIOReturnBadArgument; break; } if (kIODMACommandPerformOperationOptionWrite == options) { copied = data->readBytes(mdOffset, buffer.data(), mdLength); if (copied != mdLength) { ret = kIOReturnUnderrun; break; } copiedDMA = writeBytes(dmaOffset, buffer.data(), length); if (copiedDMA != length) { ret = kIOReturnUnderrun; break; } } else { /* kIODMACommandPerformOperationOptionRead */ copiedDMA = readBytes(dmaOffset, buffer.data(), length); if (copiedDMA != length) { ret = kIOReturnUnderrun; break; } copied = data->writeBytes(mdOffset, buffer.data(), mdLength); if (copied != mdLength) { ret = kIOReturnUnderrun; break; } } ret = kIOReturnSuccess; break; default: ret = kIOReturnBadArgument; break; } return ret; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static kern_return_t OSActionCreateWithTypeNameInternal(OSObject * target, uint64_t targetmsgid, uint64_t msgid, size_t referenceSize, OSString * typeName, bool fromKernel, OSAction ** action) { OSAction * inst = NULL; void * reference = NULL; // must release const OSSymbol *sym = NULL; // must release OSObject *obj = NULL; // must release const OSMetaClass *actionMetaClass = NULL; // do not release kern_return_t ret; if (fromKernel && typeName) { /* The action is being constructed in the kernel with a type name */ sym = OSSymbol::withString(typeName); actionMetaClass = OSMetaClass::getMetaClassWithName(sym); if (actionMetaClass && actionMetaClass->getSuperClass() == OSTypeID(OSAction)) { obj = actionMetaClass->alloc(); if (!obj) { ret = kIOReturnNoMemory; goto finish; } inst = OSDynamicCast(OSAction, obj); obj = NULL; // prevent release assert(inst); // obj is a subclass of OSAction so the dynamic cast should always work } else { DKLOG("Attempted to create action object with type \"%s\" which does not inherit from OSAction\n", typeName->getCStringNoCopy()); ret = kIOReturnBadArgument; goto finish; } } else { inst = OSTypeAlloc(OSAction); if (!inst) { ret = kIOReturnNoMemory; goto finish; } } if (referenceSize != 0) { reference = IONewZeroData(uint8_t, referenceSize); if (reference == NULL) { ret = kIOReturnNoMemory; goto finish; } } inst->ivars = IONewZero(OSAction_IVars, 1); if (!inst->ivars) { ret = kIOReturnNoMemory; goto finish; } if (target) { target->retain(); if (!fromKernel && !OSDynamicCast(IOService, target)) { IOUserServer * us; us = (typeof(us))thread_iokit_tls_get(0); inst->ivars->userServer = OSDynamicCast(IOUserServer, us); assert(inst->ivars->userServer); inst->ivars->userServer->retain(); } } inst->ivars->target = target; inst->ivars->targetmsgid = targetmsgid; inst->ivars->msgid = msgid; inst->ivars->reference = reference; inst->ivars->referenceSize = referenceSize; reference = NULL; // prevent release if (typeName) { typeName->retain(); } inst->ivars->typeName = typeName; *action = inst; inst = NULL; // prevent release ret = kIOReturnSuccess; finish: OSSafeReleaseNULL(obj); OSSafeReleaseNULL(sym); OSSafeReleaseNULL(inst); if (reference) { IODeleteData(reference, uint8_t, referenceSize); } return ret; } kern_return_t OSAction::Create(OSAction_Create_Args) { return OSAction::CreateWithTypeName(target, targetmsgid, msgid, referenceSize, NULL, action); } kern_return_t OSAction::CreateWithTypeName(OSAction_CreateWithTypeName_Args) { return OSActionCreateWithTypeNameInternal(target, targetmsgid, msgid, referenceSize, typeName, true, action); } kern_return_t OSAction::Create_Impl( OSObject * target, uint64_t targetmsgid, uint64_t msgid, size_t referenceSize, OSAction ** action) { return OSAction::CreateWithTypeName_Impl(target, targetmsgid, msgid, referenceSize, NULL, action); } kern_return_t OSAction::CreateWithTypeName_Impl( OSObject * target, uint64_t targetmsgid, uint64_t msgid, size_t referenceSize, OSString * typeName, OSAction ** action) { return OSActionCreateWithTypeNameInternal(target, targetmsgid, msgid, referenceSize, typeName, false, action); } void OSAction::free() { if (ivars) { if (ivars->abortedHandler) { Block_release(ivars->abortedHandler); ivars->abortedHandler = NULL; } OSSafeReleaseNULL(ivars->target); OSSafeReleaseNULL(ivars->typeName); OSSafeReleaseNULL(ivars->userServer); if (ivars->reference) { assert(ivars->referenceSize > 0); IODeleteData(ivars->reference, uint8_t, ivars->referenceSize); } IOSafeDeleteNULL(ivars, OSAction_IVars, 1); } return super::free(); } void * OSAction::GetReference() { assert(ivars && ivars->referenceSize && ivars->reference); return ivars->reference; } kern_return_t OSAction::SetAbortedHandler(OSActionAbortedHandler handler) { ivars->abortedHandler = Block_copy(handler); return kIOReturnSuccess; } void OSAction::Aborted_Impl(void) { if (!os_atomic_cmpxchg(&ivars->aborted, false, true, relaxed)) { // already aborted return; } if (ivars->abortedHandler) { ivars->abortedHandler(); } } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct IODispatchSource_IVars { queue_chain_t link; IODispatchSource * source; IOUserServer * server; IODispatchQueue_IVars * queue; bool enabled; }; bool IODispatchSource::init() { if (!super::init()) { return false; } ivars = IOMallocType(IODispatchSource_IVars); ivars->source = this; return true; } void IODispatchSource::free() { IOFreeType(ivars, IODispatchSource_IVars); super::free(); } kern_return_t IODispatchSource::SetEnable_Impl( bool enable) { return SetEnableWithCompletion(enable, NULL); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct IOInterruptDispatchSource_IVars { IOService * provider; uint32_t intIndex; uint32_t flags; int interruptType; IOSimpleLock * lock; thread_t waiter; uint64_t count; uint64_t time; OSAction * action; bool enable; bool canceled; }; static void IOInterruptDispatchSourceInterrupt(OSObject * target, void * refCon, IOService * nub, int source ) { IOInterruptDispatchSource_IVars * ivars = (typeof(ivars))refCon; IOInterruptState is; is = IOSimpleLockLockDisableInterrupt(ivars->lock); ivars->count++; ivars->time = (kIOInterruptSourceContinuousTime & ivars->flags) ? mach_continuous_time() : mach_absolute_time(); if (ivars->waiter) { thread_wakeup_thread((event_t) ivars, ivars->waiter); ivars->waiter = NULL; } if (kIOInterruptTypeLevel & ivars->interruptType) { ivars->provider->disableInterrupt(ivars->intIndex); } IOSimpleLockUnlockEnableInterrupt(ivars->lock, is); } kern_return_t IOInterruptDispatchSource::Create_Impl( IOService * provider, uint32_t indexAndFlags, IODispatchQueue * queue, IOInterruptDispatchSource ** source) { IOReturn ret; IOInterruptDispatchSource * inst; uint32_t index; uint32_t flags; index = indexAndFlags & kIOInterruptSourceIndexMask; flags = indexAndFlags & ~kIOInterruptSourceIndexMask; inst = OSTypeAlloc(IOInterruptDispatchSource); if (!inst->init()) { inst->free(); return kIOReturnNoMemory; } inst->ivars->lock = IOSimpleLockAlloc(); ret = provider->getInterruptType(index, &inst->ivars->interruptType); if (kIOReturnSuccess != ret) { OSSafeReleaseNULL(inst); return ret; } ret = provider->registerInterrupt(index, inst, IOInterruptDispatchSourceInterrupt, inst->ivars); if (kIOReturnSuccess == ret) { inst->ivars->intIndex = index; inst->ivars->flags = flags; inst->ivars->provider = provider; inst->ivars->provider->retain(); *source = inst; } return ret; } kern_return_t IOInterruptDispatchSource::GetInterruptType_Impl( IOService * provider, uint32_t index, uint64_t * interruptType) { IOReturn ret; int type; *interruptType = 0; ret = provider->getInterruptType(index, &type); if (kIOReturnSuccess == ret) { *interruptType = type; } return ret; } bool IOInterruptDispatchSource::init() { if (!super::init()) { return false; } ivars = IOMallocType(IOInterruptDispatchSource_IVars); return true; } void IOInterruptDispatchSource::free() { if (ivars && ivars->provider) { (void) ivars->provider->unregisterInterrupt(ivars->intIndex); ivars->provider->release(); } if (ivars && ivars->lock) { IOSimpleLockFree(ivars->lock); } IOFreeType(ivars, IOInterruptDispatchSource_IVars); super::free(); } kern_return_t IOInterruptDispatchSource::SetHandler_Impl( OSAction * action) { IOReturn ret; OSAction * oldAction; oldAction = (typeof(oldAction))ivars->action; if (oldAction && OSCompareAndSwapPtr(oldAction, NULL, &ivars->action)) { oldAction->release(); } action->retain(); ivars->action = action; ret = kIOReturnSuccess; return ret; } kern_return_t IOInterruptDispatchSource::SetEnableWithCompletion_Impl( bool enable, IODispatchSourceCancelHandler handler) { IOReturn ret; IOInterruptState is; if (enable == ivars->enable) { return kIOReturnSuccess; } if (ivars->canceled) { return kIOReturnUnsupported; } assert(ivars->provider != NULL); if (enable) { is = IOSimpleLockLockDisableInterrupt(ivars->lock); ivars->enable = enable; IOSimpleLockUnlockEnableInterrupt(ivars->lock, is); ret = ivars->provider->enableInterrupt(ivars->intIndex); } else { ret = ivars->provider->disableInterrupt(ivars->intIndex); is = IOSimpleLockLockDisableInterrupt(ivars->lock); ivars->enable = enable; IOSimpleLockUnlockEnableInterrupt(ivars->lock, is); } return ret; } kern_return_t IOInterruptDispatchSource::Cancel_Impl( IODispatchSourceCancelHandler handler) { IOInterruptState is; IOService * provider; is = IOSimpleLockLockDisableInterrupt(ivars->lock); ivars->canceled = true; if (ivars->waiter) { thread_wakeup_thread((event_t) ivars, ivars->waiter); ivars->waiter = NULL; } provider = ivars->provider; if (provider) { ivars->provider = NULL; } IOSimpleLockUnlockEnableInterrupt(ivars->lock, is); if (provider) { (void) provider->unregisterInterrupt(ivars->intIndex); provider->release(); } return kIOReturnSuccess; } kern_return_t IOInterruptDispatchSource::CheckForWork_Impl( const IORPC rpc, bool synchronous) { IOReturn ret = kIOReturnNotReady; IOInterruptState is; bool willWait; bool canceled; wait_result_t waitResult; uint64_t icount; uint64_t itime; thread_t self; self = current_thread(); icount = 0; do { willWait = false; is = IOSimpleLockLockDisableInterrupt(ivars->lock); canceled = ivars->canceled; if (!canceled) { if ((icount = ivars->count)) { itime = ivars->time; ivars->count = 0; waitResult = THREAD_AWAKENED; } else if (synchronous) { assert(NULL == ivars->waiter); ivars->waiter = self; waitResult = assert_wait((event_t) ivars, THREAD_INTERRUPTIBLE); } willWait = (synchronous && (waitResult == THREAD_WAITING)); if (willWait && (kIOInterruptTypeLevel & ivars->interruptType) && ivars->enable) { IOSimpleLockUnlockEnableInterrupt(ivars->lock, is); ivars->provider->enableInterrupt(ivars->intIndex); } else { IOSimpleLockUnlockEnableInterrupt(ivars->lock, is); } } else { IOSimpleLockUnlockEnableInterrupt(ivars->lock, is); } if (willWait) { waitResult = thread_block(THREAD_CONTINUE_NULL); if (THREAD_INTERRUPTED == waitResult) { is = IOSimpleLockLockDisableInterrupt(ivars->lock); ivars->waiter = NULL; IOSimpleLockUnlockEnableInterrupt(ivars->lock, is); canceled = true; break; } } } while (synchronous && !icount && !canceled); if (icount && ivars->action) { ret = InterruptOccurred(rpc, ivars->action, icount, itime); } return ret; } void IOInterruptDispatchSource::InterruptOccurred_Impl( OSAction * action, uint64_t count, uint64_t time) { } kern_return_t IOInterruptDispatchSource::GetLastInterrupt_Impl( uint64_t * pCount, uint64_t * pTime) { IOInterruptState is; uint64_t count, time; is = IOSimpleLockLockDisableInterrupt(ivars->lock); count = ivars->count; time = ivars->time; IOSimpleLockUnlockEnableInterrupt(ivars->lock, is); if (pCount) { *pCount = count; } if (pTime) { *pTime = time; } return kIOReturnSuccess; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ enum { kIOServiceNotificationTypeCount = kIOServiceNotificationTypeLast + 1, }; struct IOServiceNotificationDispatchSource_IVars { OSObject * serverName; OSAction * action; IOLock * lock; IONotifier * notifier; OSDictionary * interestNotifiers; OSBoundedArray pending; bool enable; }; kern_return_t IOServiceNotificationDispatchSource::Create_Impl( OSDictionary * matching, uint64_t options, IODispatchQueue * queue, IOServiceNotificationDispatchSource ** notification) { IOUserServer * us; IOReturn ret; IOServiceNotificationDispatchSource * inst; inst = OSTypeAlloc(IOServiceNotificationDispatchSource); if (!inst->init()) { OSSafeReleaseNULL(inst); return kIOReturnNoMemory; } us = (typeof(us))thread_iokit_tls_get(0); assert(OSDynamicCast(IOUserServer, us)); if (!us) { OSSafeReleaseNULL(inst); return kIOReturnError; } inst->ivars->serverName = us->copyProperty(gIOUserServerNameKey); if (!inst->ivars->serverName) { OSSafeReleaseNULL(inst); return kIOReturnNoMemory; } inst->ivars->lock = IOLockAlloc(); if (!inst->ivars->lock) { OSSafeReleaseNULL(inst); return kIOReturnNoMemory; } for (uint32_t idx = 0; idx < kIOServiceNotificationTypeCount; idx++) { inst->ivars->pending[idx] = OSArray::withCapacity(4); if (!inst->ivars->pending[idx]) { OSSafeReleaseNULL(inst); return kIOReturnNoMemory; } } inst->ivars->interestNotifiers = OSDictionary::withCapacity(4); if (!inst->ivars->interestNotifiers) { OSSafeReleaseNULL(inst); return kIOReturnNoMemory; } inst->ivars->notifier = IOService::addMatchingNotification(gIOMatchedNotification, matching, 0 /*priority*/, ^bool (IOService * newService, IONotifier * notifier) { bool notifyReady = false; IONotifier * interest; OSObject * serverName; bool okToUse; serverName = newService->copyProperty(gIOUserServerNameKey); okToUse = (serverName && inst->ivars->serverName->isEqualTo(serverName)); OSSafeReleaseNULL(serverName); if (!okToUse) { OSObject * prop; OSObject * str; if (!newService->reserved->uvars || !newService->reserved->uvars->userServer) { return false; } str = OSString::withCStringNoCopy(kIODriverKitAllowsPublishEntitlementsKey); if (!str) { return false; } okToUse = newService->reserved->uvars->userServer->checkEntitlements(str, NULL, NULL); if (!okToUse) { if (kIODKLogSetup & gIODKDebug) { DKLOG(DKS ": publisher entitlements check failed\n", DKN(newService)); } return false; } prop = newService->copyProperty(kIODriverKitPublishEntitlementsKey); if (!prop) { return false; } okToUse = us->checkEntitlements(prop, NULL, NULL); if (!okToUse) { if (kIODKLogSetup & gIODKDebug) { DKLOG(DKS ": subscriber entitlements check failed\n", DKN(newService)); } return false; } } IOLockLock(inst->ivars->lock); notifyReady = (0 == inst->ivars->pending[kIOServiceNotificationTypeMatched]->getCount()); inst->ivars->pending[kIOServiceNotificationTypeMatched]->setObject(newService); IOLockUnlock(inst->ivars->lock); interest = newService->registerInterest(gIOGeneralInterest, ^IOReturn (uint32_t messageType, IOService * provider, void * messageArgument, size_t argSize) { IONotifier * interest; bool notifyReady = false; switch (messageType) { case kIOMessageServiceIsTerminated: IOLockLock(inst->ivars->lock); notifyReady = (0 == inst->ivars->pending[kIOServiceNotificationTypeTerminated]->getCount()); inst->ivars->pending[kIOServiceNotificationTypeTerminated]->setObject(provider); if (inst->ivars->interestNotifiers != NULL) { interest = (typeof(interest))inst->ivars->interestNotifiers->getObject((const OSSymbol *) newService); assert(interest); interest->remove(); inst->ivars->interestNotifiers->removeObject((const OSSymbol *) newService); } IOLockUnlock(inst->ivars->lock); break; default: break; } if (notifyReady && inst->ivars->action) { inst->ServiceNotificationReady(inst->ivars->action); } return kIOReturnSuccess; }); if (interest) { IOLockLock(inst->ivars->lock); inst->ivars->interestNotifiers->setObject((const OSSymbol *) newService, interest); IOLockUnlock(inst->ivars->lock); } if (notifyReady) { if (inst->ivars->action) { inst->ServiceNotificationReady(inst->ivars->action); } } return false; }); if (!inst->ivars->notifier) { OSSafeReleaseNULL(inst); ret = kIOReturnError; } *notification = inst; ret = kIOReturnSuccess; return ret; } kern_return_t IOServiceNotificationDispatchSource::CopyNextNotification_Impl( uint64_t * type, IOService ** service, uint64_t * options) { IOService * next; uint32_t idx; IOLockLock(ivars->lock); for (idx = 0; idx < kIOServiceNotificationTypeCount; idx++) { next = (IOService *) ivars->pending[idx]->getObject(0); if (next) { next->retain(); ivars->pending[idx]->removeObject(0); break; } } IOLockUnlock(ivars->lock); if (idx == kIOServiceNotificationTypeCount) { idx = kIOServiceNotificationTypeNone; } *type = idx; *service = next; *options = 0; return kIOReturnSuccess; } bool IOServiceNotificationDispatchSource::init() { if (!super::init()) { return false; } ivars = IOMallocType(IOServiceNotificationDispatchSource_IVars); return true; } void IOServiceNotificationDispatchSource::free() { if (ivars) { if (ivars->notifier) { ivars->notifier->remove(); ivars->notifier = NULL; } if (ivars->interestNotifiers) { OSDictionary * savedInterestNotifiers = NULL; // the lock is always initialized first, so it should exist assert(ivars->lock); // Prevent additional changes to interestNotifiers IOLockLock(ivars->lock); savedInterestNotifiers = ivars->interestNotifiers; ivars->interestNotifiers = NULL; IOLockUnlock(ivars->lock); // Remove all interest notifiers savedInterestNotifiers->iterateObjects(^bool (const OSSymbol * key, OSObject * object) { IONotifier * interest = (typeof(interest))object; interest->remove(); return false; }); OSSafeReleaseNULL(savedInterestNotifiers); } for (uint32_t idx = 0; idx < kIOServiceNotificationTypeCount; idx++) { OSSafeReleaseNULL(ivars->pending[idx]); } if (ivars->lock) { IOLockFree(ivars->lock); ivars->lock = NULL; } OSSafeReleaseNULL(ivars->serverName); IOFreeType(ivars, IOServiceNotificationDispatchSource_IVars); } super::free(); } kern_return_t IOServiceNotificationDispatchSource::SetHandler_Impl( OSAction * action) { IOReturn ret; bool notifyReady; notifyReady = false; IOLockLock(ivars->lock); OSSafeReleaseNULL(ivars->action); action->retain(); ivars->action = action; if (action) { for (uint32_t idx = 0; idx < kIOServiceNotificationTypeCount; idx++) { notifyReady = (ivars->pending[idx]->getCount()); if (notifyReady) { break; } } } IOLockUnlock(ivars->lock); if (notifyReady) { ServiceNotificationReady(action); } ret = kIOReturnSuccess; return ret; } kern_return_t IOServiceNotificationDispatchSource::SetEnableWithCompletion_Impl( bool enable, IODispatchSourceCancelHandler handler) { if (enable == ivars->enable) { return kIOReturnSuccess; } IOLockLock(ivars->lock); ivars->enable = enable; IOLockUnlock(ivars->lock); return kIOReturnSuccess; } kern_return_t IOServiceNotificationDispatchSource::Cancel_Impl( IODispatchSourceCancelHandler handler) { return kIOReturnUnsupported; } kern_return_t IOServiceNotificationDispatchSource::CheckForWork_Impl( const IORPC rpc, bool synchronous) { return kIOReturnNotReady; } kern_return_t IOServiceNotificationDispatchSource::DeliverNotifications(IOServiceNotificationBlock block) { return kIOReturnUnsupported; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ OSDictionary * IOService::CreatePropertyMatchingDictionary(const char * key, OSObjectPtr value, OSDictionary * matching) { OSDictionary * result; const OSSymbol * keySym; keySym = OSSymbol::withCString(key); result = propertyMatching(keySym, (const OSObject *) value, matching); OSSafeReleaseNULL(keySym); return result; } OSDictionary * IOService::CreatePropertyMatchingDictionary(const char * key, const char * stringValue, OSDictionary * matching) { OSDictionary * result; OSString * value; value = OSString::withCString(stringValue); result = CreatePropertyMatchingDictionary(key, value, matching); OSSafeReleaseNULL(value); return result; } OSDictionary * IOService::CreateKernelClassMatchingDictionary(OSString * className, OSDictionary * matching) { if (!className) { return NULL; } if (!matching) { matching = OSDictionary::withCapacity(2); if (!matching) { return NULL; } } matching->setObject(kIOProviderClassKey, className); return matching; } OSDictionary * IOService::CreateKernelClassMatchingDictionary(const char * className, OSDictionary * matching) { OSDictionary * result; OSString * string; string = OSString::withCString(className); result = CreateKernelClassMatchingDictionary(string, matching); OSSafeReleaseNULL(string); return result; } OSDictionary * IOService::CreateUserClassMatchingDictionary(OSString * className, OSDictionary * matching) { return CreatePropertyMatchingDictionary(kIOUserClassKey, className, matching); } OSDictionary * IOService::CreateUserClassMatchingDictionary(const char * className, OSDictionary * matching) { return CreatePropertyMatchingDictionary(kIOUserClassKey, className, matching); } OSDictionary * IOService::CreateNameMatchingDictionary(OSString * serviceName, OSDictionary * matching) { if (!serviceName) { return NULL; } if (!matching) { matching = OSDictionary::withCapacity(2); if (!matching) { return NULL; } } matching->setObject(kIONameMatchKey, serviceName); return matching; } OSDictionary * IOService::CreateNameMatchingDictionary(const char * serviceName, OSDictionary * matching) { OSDictionary * result; OSString * string; string = OSString::withCString(serviceName); result = CreateNameMatchingDictionary(string, matching); OSSafeReleaseNULL(string); return result; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ kern_return_t IOUserServer::waitInterruptTrap(void * p1, void * p2, void * p3, void * p4, void * p5, void * p6) { IOReturn ret = kIOReturnBadArgument; IOInterruptState is; IOInterruptDispatchSource * interrupt; IOInterruptDispatchSource_IVars * ivars; IOInterruptDispatchSourcePayload payload; bool willWait; bool canceled; wait_result_t waitResult; thread_t self; OSObject * object; object = iokit_lookup_object_with_port_name((mach_port_name_t)(uintptr_t)p1, IKOT_UEXT_OBJECT, current_task()); if (!object) { return kIOReturnBadArgument; } if (!(interrupt = OSDynamicCast(IOInterruptDispatchSource, object))) { ret = kIOReturnBadArgument; } else { self = current_thread(); ivars = interrupt->ivars; payload.count = 0; do { willWait = false; is = IOSimpleLockLockDisableInterrupt(ivars->lock); canceled = ivars->canceled; if (!canceled) { if ((payload.count = ivars->count)) { payload.time = ivars->time; ivars->count = 0; waitResult = THREAD_AWAKENED; } else { assert(NULL == ivars->waiter); ivars->waiter = self; waitResult = assert_wait((event_t) ivars, THREAD_INTERRUPTIBLE); } willWait = (waitResult == THREAD_WAITING); if (willWait && (kIOInterruptTypeLevel & ivars->interruptType) && ivars->enable) { IOSimpleLockUnlockEnableInterrupt(ivars->lock, is); ivars->provider->enableInterrupt(ivars->intIndex); } else { IOSimpleLockUnlockEnableInterrupt(ivars->lock, is); } } else { IOSimpleLockUnlockEnableInterrupt(ivars->lock, is); } if (willWait) { waitResult = thread_block(THREAD_CONTINUE_NULL); if (THREAD_INTERRUPTED == waitResult) { is = IOSimpleLockLockDisableInterrupt(ivars->lock); ivars->waiter = NULL; IOSimpleLockUnlockEnableInterrupt(ivars->lock, is); canceled = true; break; } } } while (!payload.count && !canceled); ret = (payload.count ? kIOReturnSuccess : kIOReturnAborted); } if (kIOReturnSuccess == ret) { int copyerr = copyout(&payload, (user_addr_t) p2, sizeof(payload)); if (copyerr) { ret = kIOReturnVMError; } } object->release(); return ret; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ kern_return_t IOUserServer::Create_Impl( const char * name, uint64_t tag, uint64_t options, OSString * bundleID, IOUserServer ** server) { IOReturn ret; IOUserServer * us; const OSSymbol * sym; OSNumber * serverTag; io_name_t rname; OSKext * kext; us = (typeof(us))thread_iokit_tls_get(0); assert(OSDynamicCast(IOUserServer, us)); if (kIODKLogSetup & gIODKDebug) { DKLOG(DKS "::Create(" DKS ") %p\n", DKN(us), name, tag, us); } if (!us) { return kIOReturnError; } if (bundleID) { kext = OSKext::lookupKextWithIdentifier(bundleID->getCStringNoCopy()); if (kext) { us->setTaskLoadTag(kext); us->setDriverKitUUID(kext); us->setDriverKitStatistics(kext); OSKext::OSKextLogDriverKitInfoLoad(kext); OSSafeReleaseNULL(kext); } else { DKLOG(DKS "::Create(" DKS "): could not find OSKext for %s\n", DKN(us), name, tag, bundleID->getCStringNoCopy()); } us->fAllocationName = kern_allocation_name_allocate(bundleID->getCStringNoCopy(), 0); assert(us->fAllocationName); } sym = OSSymbol::withCString(name); serverTag = OSNumber::withNumber(tag, 64); us->setProperty(gIOUserServerNameKey, (OSObject *) sym); us->setProperty(gIOUserServerTagKey, serverTag); serverTag->release(); OSSafeReleaseNULL(sym); snprintf(rname, sizeof(rname), "IOUserServer(%s-0x%qx)", name, tag); us->setName(rname); us->retain(); *server = us; ret = kIOReturnSuccess; return ret; } kern_return_t IOUserServer::RegisterService_Impl() { kern_return_t ret = IOService::RegisterService_Impl(); return ret; } kern_return_t IOUserServer::Exit_Impl( const char * reason) { return kIOReturnUnsupported; } kern_return_t IOUserServer::LoadModule_Impl( const char * path) { return kIOReturnUnsupported; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ kern_return_t IODispatchQueue::Create_Impl( const char * name, uint64_t options, uint64_t priority, IODispatchQueue ** queue) { IODispatchQueue * result; IOUserServer * us; result = OSTypeAlloc(IODispatchQueue); if (!result) { return kIOReturnNoMemory; } if (!result->init()) { OSSafeReleaseNULL(result); return kIOReturnNoMemory; } *queue = result; if (!strcmp("Root", name)) { us = (typeof(us))thread_iokit_tls_get(0); assert(OSDynamicCast(IOUserServer, us)); us->setRootQueue(result); } if (kIODKLogSetup & gIODKDebug) { DKLOG("IODispatchQueue::Create %s %p\n", name, result); } return kIOReturnSuccess; } kern_return_t IODispatchQueue::SetPort_Impl( mach_port_t port) { if (MACH_PORT_NULL != ivars->serverPort) { return kIOReturnNotReady; } ivars->serverPort = ipc_port_copy_send_mqueue(port); if (ivars->serverPort == MACH_PORT_NULL) { return kIOReturnBadArgument; } return kIOReturnSuccess; } bool IODispatchQueue::init() { ivars = IOMallocType(IODispatchQueue_IVars); ivars->queue = this; return true; } void IODispatchQueue::free() { if (ivars && ivars->serverPort) { ipc_port_release_send(ivars->serverPort); ivars->serverPort = MACH_PORT_NULL; } IOFreeType(ivars, IODispatchQueue_IVars); super::free(); } bool IODispatchQueue::OnQueue() { return false; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ kern_return_t OSMetaClassBase::Dispatch(IORPC rpc) { return kIOReturnUnsupported; } kern_return_t OSMetaClassBase::Invoke(IORPC rpc) { IOReturn ret = kIOReturnUnsupported; OSMetaClassBase * object; OSAction * action; IOService * service; IOUserServer * us; IORPCMessage * message; assert(rpc.sendSize >= (sizeof(IORPCMessageMach) + sizeof(IORPCMessage))); message = rpc.kernelContent; if (!message) { return kIOReturnIPCError; } message->flags |= kIORPCMessageKernel; us = NULL; if (!(kIORPCMessageLocalHost & message->flags)) { us = OSDynamicCast(IOUserServer, this); if (!us) { IOEventLink * eventLink = NULL; IOWorkGroup * workgroup = NULL; if ((action = OSDynamicCast(OSAction, this))) { object = IOUserServer::target(action, message); } else { object = this; } if ((service = OSDynamicCast(IOService, object)) && service->reserved->uvars) { // xxx other classes us = service->reserved->uvars->userServer; } else if (action) { us = action->ivars->userServer; } else if ((eventLink = OSDynamicCast(IOEventLink, object))) { us = eventLink->ivars->userServer; } else if ((workgroup = OSDynamicCast(IOWorkGroup, object))) { us = workgroup->ivars->userServer; } } } if (us) { message->flags |= kIORPCMessageRemote; ret = us->rpc(rpc); if (kIOReturnSuccess != ret) { if (kIODKLogIPC & gIODKDebug) { DKLOG("OSMetaClassBase::Invoke user 0x%x\n", ret); } } } else { if (kIODKLogIPC & gIODKDebug) { DKLOG("OSMetaClassBase::Invoke kernel %s 0x%qx\n", getMetaClass()->getClassName(), message->msgid); } void * prior = thread_iokit_tls_get(0); thread_iokit_tls_set(0, NULL); ret = Dispatch(rpc); thread_iokit_tls_set(0, prior); } return ret; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct IOPStrings { uint32_t dataSize; uint32_t count; const char strings[0]; }; kern_return_t OSUserMetaClass::Dispatch(IORPC rpc) { if (meta) { return const_cast(meta)->Dispatch(rpc); } else { return kIOReturnUnsupported; } } void OSUserMetaClass::free() { if (queueNames) { IOFreeData(queueNames, sizeof(IOPStrings) + queueNames->dataSize * sizeof(char)); queueNames = NULL; } if (description) { IOFreeData(description, description->descriptionSize); description = NULL; } IODeleteData(methods, uint64_t, 2 * methodCount); if (meta) { meta->releaseMetaClass(); } if (name) { name->release(); } OSObject::free(); } /* * Sets the loadTag of the associated OSKext * in the dext task. * NOTE: different instances of the same OSKext * (so same BounleID but different tasks) * will have the same loadTag. */ void IOUserServer::setTaskLoadTag(OSKext *kext) { task_t owningTask; uint32_t loadTag, prev_taskloadTag; owningTask = this->fOwningTask; if (!owningTask) { printf("%s: fOwningTask not found\n", __FUNCTION__); return; } loadTag = kext->getLoadTag(); prev_taskloadTag = set_task_loadTag(owningTask, loadTag); if (prev_taskloadTag) { printf("%s: found the task loadTag already set to %u (set to %u)\n", __FUNCTION__, prev_taskloadTag, loadTag); } } /* * Sets the OSKext uuid as the uuid of the userspace * dext executable. */ void IOUserServer::setDriverKitUUID(OSKext *kext) { task_t task; proc_t p; uuid_t p_uuid, k_uuid; OSData *k_data_uuid; OSData *new_uuid; uuid_string_t uuid_string = ""; task = this->fOwningTask; if (!task) { printf("%s: fOwningTask not found\n", __FUNCTION__); return; } p = (proc_t)(get_bsdtask_info(task)); if (!p) { printf("%s: proc not found\n", __FUNCTION__); return; } proc_getexecutableuuid(p, p_uuid, sizeof(p_uuid)); k_data_uuid = kext->copyUUID(); if (k_data_uuid) { memcpy(&k_uuid, k_data_uuid->getBytesNoCopy(), sizeof(k_uuid)); OSSafeReleaseNULL(k_data_uuid); if (uuid_compare(k_uuid, p_uuid) != 0) { printf("%s: uuid not matching\n", __FUNCTION__); } return; } uuid_unparse(p_uuid, uuid_string); new_uuid = OSData::withValue(p_uuid); kext->setDriverKitUUID(new_uuid); } void IOUserServer::setDriverKitStatistics(OSKext *kext) { OSDextStatistics * statistics = kext->copyDextStatistics(); if (statistics == NULL) { panic("Kext %s was not a DriverKit OSKext", kext->getIdentifierCString()); } fStatistics = statistics; } IOReturn IOUserServer::setCheckInToken(IOUserServerCheckInToken *token) { IOReturn ret = kIOReturnError; if (token != NULL && fCheckInToken == NULL) { token->retain(); fCheckInToken = token; ret = fCheckInToken->complete(); iokit_clear_registered_ports(fOwningTask); } else { printf("%s: failed to set check in token. token=%p, fCheckInToken=%p\n", __FUNCTION__, token, fCheckInToken); } return ret; } bool IOUserServer::serviceMatchesCheckInToken(IOUserServerCheckInToken *token) { if (token != NULL) { bool result = token == fCheckInToken; return result; } else { printf("%s: null check in token\n", __FUNCTION__); return false; } } // entitlements - dict of entitlements to check // prop - string - if present return true // - array of strings - if any present return true // - array of arrays of strings - in each leaf array all must be present // - if any top level array succeeds return true // consumes one reference of prop bool IOUserServer::checkEntitlements( OSDictionary * entitlements, OSObject * prop, IOService * provider, IOService * dext) { OSDictionary * matching; if (!prop) { return true; } if (!entitlements) { OSSafeReleaseNULL(prop); return false; } matching = NULL; if (dext) { matching = dext->dictionaryWithProperties(); if (!matching) { OSSafeReleaseNULL(prop); return false; } } bool allPresent __block = false; prop->iterateObjects(^bool (OSObject * object) { allPresent = false; object->iterateObjects(^bool (OSObject * object) { OSString * string; OSObject * value; string = OSDynamicCast(OSString, object); value = entitlements->getObject(string); if (matching && value) { matching->setObject(string, value); } allPresent = (NULL != value); // early terminate if not found return !allPresent; }); // early terminate if found return allPresent; }); if (allPresent && matching && provider) { allPresent = provider->matchPropertyTable(matching); } OSSafeReleaseNULL(matching); OSSafeReleaseNULL(prop); return allPresent; } bool IOUserServer::checkEntitlements(OSObject * prop, IOService * provider, IOService * dext) { return checkEntitlements(fEntitlements, prop, provider, dext); } bool IOUserServer::checkEntitlements(IOService * provider, IOService * dext) { OSObject * prop; bool ok; if (!fOwningTask) { return false; } prop = provider->copyProperty(gIOServiceDEXTEntitlementsKey); ok = checkEntitlements(fEntitlements, prop, provider, dext); if (!ok) { DKLOG(DKS ": provider entitlements check failed\n", DKN(dext)); } if (ok) { prop = dext->copyProperty(gIOServiceDEXTEntitlementsKey); ok = checkEntitlements(fEntitlements, prop, NULL, NULL); if (!ok) { DKLOG(DKS ": family entitlements check failed\n", DKN(dext)); } } return ok; } IOReturn IOUserServer::exit(const char * reason) { DKLOG("%s::exit(%s)\n", getName(), reason); Exit(reason); return kIOReturnSuccess; } IOReturn IOUserServer::kill(const char * reason) { IOReturn ret = kIOReturnError; if (fOwningTask != NULL) { DKLOG("%s::kill(%s)\n", getName(), reason); task_bsdtask_kill(fOwningTask); ret = kIOReturnSuccess; } return ret; } OSObjectUserVars * IOUserServer::varsForObject(OSObject * obj) { IOService * service; if ((service = OSDynamicCast(IOService, obj))) { return service->reserved->uvars; } return NULL; } IOPStrings * IOUserServer::copyInStringArray(const char * string, uint32_t userSize) { IOPStrings * array; vm_size_t alloc; size_t len; const char * end; OSBoundedPtr cstr; if (userSize <= 1) { return NULL; } if (os_add_overflow(sizeof(IOPStrings), userSize, &alloc)) { assert(false); return NULL; } if (alloc > 16384) { assert(false); return NULL; } array = (typeof(array))IOMallocData(alloc); if (!array) { return NULL; } array->dataSize = userSize; bcopy(string, (void *) &array->strings[0], userSize); array->count = 0; end = &array->strings[array->dataSize]; cstr = OSBoundedPtr(&array->strings[0], &array->strings[0], end); while ((len = (unsigned char)cstr[0])) { cstr++; if ((cstr + len) >= end) { break; } cstr += len; array->count++; } if (len) { IOFreeData(array, alloc); array = NULL; } return array; } uint32_t IOUserServer::stringArrayIndex(IOPStrings * array, const char * look) { uint32_t idx; size_t len, llen; OSBoundedPtr cstr; const char * end; idx = 0; end = &array->strings[array->dataSize]; cstr = OSBoundedPtr(&array->strings[0], &array->strings[0], end); llen = strlen(look); while ((len = (unsigned char)cstr[0])) { cstr++; if ((cstr + len) >= end) { break; } if ((len == llen) && !strncmp(cstr.discard_bounds(), look, len)) { return idx; } cstr += len; idx++; } return -1U; } #define kIODispatchQueueStopped ((IODispatchQueue *) -1L) IODispatchQueue * IOUserServer::queueForObject(OSObject * obj, uint64_t msgid) { IODispatchQueue * queue; OSObjectUserVars * uvars; uint64_t option; uvars = varsForObject(obj); if (!uvars) { return NULL; } if (!uvars->queueArray) { if (uvars->stopped) { return kIODispatchQueueStopped; } return NULL; } queue = uvars->queueArray[0]; if (uvars->userMeta && uvars->userMeta->methods) { uint32_t idx, baseIdx; uint32_t lim; // bsearch for (baseIdx = 0, lim = uvars->userMeta->methodCount; lim; lim >>= 1) { idx = baseIdx + (lim >> 1); if (msgid == uvars->userMeta->methods[idx]) { option = uvars->userMeta->methods[uvars->userMeta->methodCount + idx]; option &= 0xFF; if (option < uvars->userMeta->queueNames->count) { queue = uvars->queueArray[option + 1]; } break; } else if (msgid > uvars->userMeta->methods[idx]) { // move right baseIdx += (lim >> 1) + 1; lim--; } // else move left } } return queue; } IOReturn IOUserServer::objectInstantiate(OSObject * obj, IORPC rpc, IORPCMessage * message) { IOReturn ret; OSString * str; OSObject * prop; IOService * service; OSAction * action; OSObject * target; uint32_t queueCount, queueAlloc; const char * resultClassName; uint64_t resultFlags; mach_msg_size_t replySize; uint32_t methodCount; const uint64_t * methods; IODispatchQueue * queue; OSUserMetaClass * userMeta; OSObjectUserVars * uvars; uint32_t idx; ipc_port_t sendPort; bool serviceInactive; OSObject_Instantiate_Rpl_Content * reply; IODispatchQueue ** unboundedQueueArray = NULL; queueCount = 0; methodCount = 0; methods = NULL; str = NULL; prop = NULL; userMeta = NULL; resultClassName = NULL; resultFlags = 0; ret = kIOReturnUnsupportedMode; service = OSDynamicCast(IOService, obj); action = OSDynamicCast(OSAction, obj); if (!service) { // xxx other classes hosted resultFlags |= kOSObjectRPCKernel; resultFlags |= kOSObjectRPCRemote; } else { serviceInactive = false; if (service->lockForArbitration()) { if (service->isInactive() && (service->__state[1] & kIOServiceStartState) == 0) { serviceInactive = true; } service->unlockForArbitration(); } if (serviceInactive) { DKLOG(DKS "::instantiate inactive\n", DKN(service)); return kIOReturnOffline; } prop = service->copyProperty(gIOUserClassKey); str = OSDynamicCast(OSString, prop); if (!service->reserved->uvars) { resultFlags |= kOSObjectRPCRemote; resultFlags |= kOSObjectRPCKernel; } else if (this != service->reserved->uvars->userServer) { // remote, use base class resultFlags |= kOSObjectRPCRemote; } if (service->reserved->uvars && service->reserved->uvars->userServer) { if (!str) { DKLOG("no IOUserClass defined for " DKS "\n", DKN(service)); OSSafeReleaseNULL(prop); return kIOReturnError; } IOLockLock(service->reserved->uvars->userServer->fLock); userMeta = (typeof(userMeta))service->reserved->uvars->userServer->fClasses->getObject(str); IOLockUnlock(service->reserved->uvars->userServer->fLock); } } if (!str && !userMeta) { const OSMetaClass * meta; meta = obj->getMetaClass(); IOLockLock(fLock); if (action) { str = action->ivars->typeName; if (str) { userMeta = (typeof(userMeta))fClasses->getObject(str); } } while (meta && !userMeta) { str = (OSString *) meta->getClassNameSymbol(); userMeta = (typeof(userMeta))fClasses->getObject(str); if (!userMeta) { meta = meta->getSuperClass(); } } IOLockUnlock(fLock); } if (str) { if (!userMeta) { IOLockLock(fLock); userMeta = (typeof(userMeta))fClasses->getObject(str); IOLockUnlock(fLock); } if (kIODKLogSetup & gIODKDebug) { DKLOG("userMeta %s %p\n", str->getCStringNoCopy(), userMeta); } if (userMeta) { if (kOSObjectRPCRemote & resultFlags) { if (!action) { /* Special case: For OSAction subclasses, do not use the superclass */ while (userMeta && !(kOSClassCanRemote & userMeta->description->flags)) { userMeta = userMeta->superMeta; } } if (userMeta) { resultClassName = userMeta->description->name; ret = kIOReturnSuccess; } } else { service->reserved->uvars->userMeta = userMeta; queueAlloc = 1; if (userMeta->queueNames) { queueAlloc += userMeta->queueNames->count; } unboundedQueueArray = IONewZero(IODispatchQueue *, queueAlloc); service->reserved->uvars->queueArray = OSBoundedArrayRef(unboundedQueueArray, queueAlloc); resultClassName = str->getCStringNoCopy(); ret = kIOReturnSuccess; } } else if (kIODKLogSetup & gIODKDebug) { DKLOG("userMeta %s was not found in fClasses\n", str->getCStringNoCopy()); IOLockLock(fLock); fClasses->iterateObjects(^bool (const OSSymbol * key, OSObject * val) { DKLOG(" fClasses[\"%s\"] => %p\n", key->getCStringNoCopy(), val); return false; }); IOLockUnlock(fLock); } } OSSafeReleaseNULL(prop); IORPCMessageMach * machReply = rpc.reply; replySize = sizeof(OSObject_Instantiate_Rpl); if ((kIOReturnSuccess == ret) && (kOSObjectRPCRemote & resultFlags)) { target = obj; if (action) { if (action->ivars->referenceSize) { resultFlags |= kOSObjectRPCKernel; } else { resultFlags &= ~kOSObjectRPCKernel; if (action->ivars->target) { target = action->ivars->target; queueCount = 1; queue = queueForObject(target, action->ivars->targetmsgid); if (!queue && action->ivars->userServer) { queue = action->ivars->userServer->fRootQueue; } idx = 0; sendPort = NULL; if (queue && (kIODispatchQueueStopped != queue)) { sendPort = ipc_port_copy_send_mqueue(queue->ivars->serverPort); } replySize = sizeof(OSObject_Instantiate_Rpl) + queueCount * sizeof(machReply->objects[0]) + 2 * methodCount * sizeof(reply->methods[0]); if (replySize > rpc.replySize) { assert(false); return kIOReturnIPCError; } machReply->objects[idx].type = MACH_MSG_PORT_DESCRIPTOR; machReply->objects[idx].disposition = MACH_MSG_TYPE_MOVE_SEND; machReply->objects[idx].name = sendPort; machReply->objects[idx].pad2 = 0; machReply->objects[idx].pad_end = 0; } } } else { uvars = varsForObject(target); if (uvars && uvars->userMeta) { queueCount = 1; if (uvars->userMeta->queueNames) { queueCount += uvars->userMeta->queueNames->count; } methods = &uvars->userMeta->methods[0]; methodCount = uvars->userMeta->methodCount; replySize = sizeof(OSObject_Instantiate_Rpl) + queueCount * sizeof(machReply->objects[0]) + 2 * methodCount * sizeof(reply->methods[0]); if (replySize > rpc.replySize) { assert(false); return kIOReturnIPCError; } for (idx = 0; idx < queueCount; idx++) { queue = uvars->queueArray[idx]; sendPort = NULL; if (queue) { sendPort = ipc_port_copy_send_mqueue(queue->ivars->serverPort); } machReply->objects[idx].type = MACH_MSG_PORT_DESCRIPTOR; machReply->objects[idx].disposition = MACH_MSG_TYPE_MOVE_SEND; machReply->objects[idx].name = sendPort; machReply->objects[idx].pad2 = 0; machReply->objects[idx].pad_end = 0; } } } } if (kIODKLogIPC & gIODKDebug) { DKLOG("instantiate object %s with user class %s\n", obj->getMetaClass()->getClassName(), str ? str->getCStringNoCopy() : "(null)"); } if (kIOReturnSuccess != ret) { DKLOG("%s: no user class found\n", str ? str->getCStringNoCopy() : obj->getMetaClass()->getClassName()); resultClassName = "unknown"; } machReply->msgh.msgh_id = kIORPCVersionCurrentReply; machReply->msgh.msgh_size = replySize; machReply->msgh_body.msgh_descriptor_count = queueCount; reply = (typeof(reply))IORPCMessageFromMachReply(machReply); if (!reply) { return kIOReturnIPCError; } if (methodCount) { bcopy(methods, &reply->methods[0], methodCount * 2 * sizeof(reply->methods[0])); } reply->__hdr.msgid = OSObject_Instantiate_ID; reply->__hdr.flags = kIORPCMessageOneway; reply->__hdr.objectRefs = 0; reply->__pad = 0; reply->flags = resultFlags; strlcpy(reply->classname, resultClassName, sizeof(reply->classname)); reply->__result = ret; ret = kIOReturnSuccess; return ret; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ IOReturn IOUserServer::kernelDispatch(OSObject * obj, IORPC rpc) { IOReturn ret; IORPCMessage * message; message = rpc.kernelContent; if (!message) { return kIOReturnIPCError; } if (OSObject_Instantiate_ID == message->msgid) { ret = objectInstantiate(obj, rpc, message); if (kIOReturnSuccess != ret) { DKLOG("%s: instantiate failed 0x%x\n", obj->getMetaClass()->getClassName(), ret); } } else { if (kIODKLogIPC & gIODKDebug) { DKLOG("%s::Dispatch kernel 0x%qx\n", obj->getMetaClass()->getClassName(), message->msgid); } ret = obj->Dispatch(rpc); if (kIODKLogIPC & gIODKDebug) { DKLOG("%s::Dispatch kernel 0x%qx result 0x%x\n", obj->getMetaClass()->getClassName(), message->msgid, ret); } } return ret; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ OSObject * IOUserServer::target(OSAction * action, IORPCMessage * message) { OSObject * object; if (message->msgid != action->ivars->msgid) { return action; } object = action->ivars->target; if (!object) { return action; } message->msgid = action->ivars->targetmsgid; message->objects[0] = (OSObjectRef) object; if (kIORPCMessageRemote & message->flags) { object->retain(); #ifndef __clang_analyzer__ // Hide the release of 'action' from the clang static analyzer to suppress // an overrelease diagnostic. The analyzer doesn't have a way to express the // non-standard contract of this method, which is that it releases 'action' when // the message flags have kIORPCMessageRemote set. action->release(); #endif } if (kIODKLogIPC & gIODKDebug) { DKLOG("TARGET %s msg 0x%qx from 0x%qx\n", object->getMetaClass()->getClassName(), message->msgid, action->ivars->msgid); } return object; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ kern_return_t uext_server(ipc_port_t receiver, ipc_kmsg_t requestkmsg, ipc_kmsg_t * pReply) { kern_return_t ret; OSObject * object; IOUserServer * server; object = IOUserServer::copyObjectForSendRight(receiver, IKOT_UEXT_OBJECT); server = OSDynamicCast(IOUserServer, object); if (!server) { OSSafeReleaseNULL(object); return KERN_INVALID_NAME; } IORPCMessage * message = (typeof(message))ikm_udata_from_header(requestkmsg); ret = server->server(requestkmsg, message, pReply); object->release(); return ret; } /* * Chosen to hit kalloc zones (as opposed to the VM). * doesn't include the trailer size which ipc_kmsg_alloc() will add */ #define MAX_UEXT_REPLY_SIZE 0x17c0 static_assert(MAX_UEXT_REPLY_SIZE + MAX_TRAILER_SIZE <= KALLOC_SAFE_ALLOC_SIZE); kern_return_t IOUserServer::server(ipc_kmsg_t requestkmsg, IORPCMessage * message, ipc_kmsg_t * pReply) { kern_return_t ret; mach_msg_size_t replyAlloc; ipc_kmsg_t replykmsg; IORPCMessageMach * msgin; IORPCMessageMach * msgout; IORPCMessage * reply; uint32_t replySize; OSObject * object; OSAction * action; bool oneway; uint64_t msgid; msgin = (typeof(msgin))ikm_header(requestkmsg); replyAlloc = 0; msgout = NULL; replykmsg = NULL; if (msgin->msgh.msgh_size < (sizeof(IORPCMessageMach) + sizeof(IORPCMessage))) { if (kIODKLogIPC & gIODKDebug) { DKLOG("UEXT notify %o\n", msgin->msgh.msgh_id); } return KERN_NOT_SUPPORTED; } if (!(MACH_MSGH_BITS_COMPLEX & msgin->msgh.msgh_bits)) { msgin->msgh_body.msgh_descriptor_count = 0; } if (!message) { return kIOReturnIPCError; } if (message->objectRefs == 0) { return kIOReturnIPCError; } ret = copyInObjects(msgin, message, msgin->msgh.msgh_size, true, false); if (kIOReturnSuccess != ret) { if (kIODKLogIPC & gIODKDebug) { DKLOG("UEXT copyin(0x%x) %x\n", ret, msgin->msgh.msgh_id); } // release objects and ports consumeObjects(msgin, message, msgin->msgh.msgh_size); copyInObjects(msgin, message, msgin->msgh.msgh_size, false, true); return KERN_NOT_SUPPORTED; } if (msgin->msgh_body.msgh_descriptor_count < 1) { return KERN_NOT_SUPPORTED; } object = (OSObject *) message->objects[0]; msgid = message->msgid; message->flags &= ~kIORPCMessageKernel; message->flags |= kIORPCMessageRemote; if ((action = OSDynamicCast(OSAction, object))) { object = target(action, message); msgid = message->msgid; } oneway = (0 != (kIORPCMessageOneway & message->flags)); assert(oneway || (MACH_PORT_NULL != msgin->msgh.msgh_local_port)); replyAlloc = oneway ? 0 : MAX_UEXT_REPLY_SIZE; if (replyAlloc) { /* * Same as: * ipc_kmsg_alloc(MAX_UEXT_REPLY_SIZE_MACH, MAX_UEXT_REPLY_SIZE_MESSAGE, * IPC_KMSG_ALLOC_KERNEL | IPC_KMSG_ALLOC_ZERO | IPC_KMSG_ALLOC_LINEAR | * IPC_KMSG_ALLOC_NOFAIL); */ replykmsg = ipc_kmsg_alloc_uext_reply(MAX_UEXT_REPLY_SIZE); msgout = (typeof(msgout))ikm_header(replykmsg); } IORPC rpc = { .message = msgin, .reply = msgout, .sendSize = msgin->msgh.msgh_size, .replySize = replyAlloc, .kernelContent = message }; if (object) { kern_allocation_name_t prior; bool setAllocationName; setAllocationName = (NULL != fAllocationName); if (setAllocationName) { prior = thread_set_allocation_name(fAllocationName); } thread_iokit_tls_set(0, this); ret = kernelDispatch(object, rpc); thread_iokit_tls_set(0, NULL); if (setAllocationName) { thread_set_allocation_name(prior); } } else { ret = kIOReturnBadArgument; } // release objects consumeObjects(msgin, message, msgin->msgh.msgh_size); // release ports copyInObjects(msgin, message, msgin->msgh.msgh_size, false, true); if (!oneway) { if (kIOReturnSuccess == ret) { replySize = msgout->msgh.msgh_size; reply = IORPCMessageFromMachReply(msgout); if (!reply) { ret = kIOReturnIPCError; } else { ret = copyOutObjects(msgout, reply, replySize, (kIORPCVersionCurrentReply == msgout->msgh.msgh_id) /* =>!InvokeReply */); } } if (kIOReturnSuccess != ret) { IORPCMessageErrorReturnContent * errorMsg; msgout->msgh_body.msgh_descriptor_count = 0; msgout->msgh.msgh_id = kIORPCVersionCurrentReply; errorMsg = (typeof(errorMsg))IORPCMessageFromMachReply(msgout); errorMsg->hdr.msgid = message->msgid; errorMsg->hdr.flags = kIORPCMessageOneway | kIORPCMessageError; errorMsg->hdr.objectRefs = 0; errorMsg->result = ret; errorMsg->pad = 0; replySize = sizeof(IORPCMessageErrorReturn); } msgout->msgh.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS_SET(MACH_MSGH_BITS_LOCAL(msgin->msgh.msgh_bits) /*remote*/, 0 /*local*/, 0, 0); msgout->msgh.msgh_remote_port = msgin->msgh.msgh_local_port; msgout->msgh.msgh_local_port = MACH_PORT_NULL; msgout->msgh.msgh_voucher_port = (mach_port_name_t) 0; msgout->msgh.msgh_reserved = 0; msgout->msgh.msgh_size = replySize; } *pReply = replykmsg; return KERN_SUCCESS; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static inline uint32_t MAX_OBJECT_COUNT(IORPCMessageMach *mach, size_t size, IORPCMessage *message __unused) { assert(mach->msgh.msgh_size == size); size_t used_size; size_t remaining_size; if (os_mul_and_add_overflow( mach->msgh_body.msgh_descriptor_count, sizeof(mach_msg_port_descriptor_t), sizeof(mach->msgh) + sizeof(mach->msgh_body) + offsetof(IORPCMessage, objects[0]), &used_size)) { return 0; } if (os_sub_overflow(size, used_size, &remaining_size)) { return 0; } return (uint32_t)(remaining_size / sizeof(OSObjectRef)); } #pragma pack(push, 4) struct UEXTTrapReply { uint64_t replySize; IORPCMessage replyMessage; }; #pragma pack(pop) kern_return_t IOUserServerUEXTTrap(OSObject * object, void * p1, void * p2, void * p3, void * p4, void * p5, void * p6) { const user_addr_t msg = (uintptr_t) p1; size_t inSize = (uintptr_t) p2; user_addr_t out = (uintptr_t) p3; size_t outSize = (uintptr_t) p4; mach_port_name_t objectName1 = (mach_port_name_t)(uintptr_t) p5; size_t totalSize; OSObject * objectArg1; IORPCMessageMach * mach; mach_msg_port_descriptor_t * descs; #pragma pack(4) struct { uint32_t pad; IORPCMessageMach mach; mach_msg_port_descriptor_t objects[2]; IOTrapMessageBuffer buffer; } buffer; #pragma pack() IOReturn ret; OSAction * action; int copyerr; IORPCMessage * message; IORPCMessage * reply; IORPC rpc; uint64_t refs; uint32_t maxObjectCount; size_t copySize; UEXTTrapReply * replyHdr; uintptr_t p; bzero(&buffer, sizeof(buffer)); p = (typeof(p)) & buffer.buffer[0]; if (os_add_overflow(inSize, outSize, &totalSize)) { return kIOReturnMessageTooLarge; } if (totalSize > sizeof(buffer.buffer)) { return kIOReturnMessageTooLarge; } if (inSize < sizeof(IORPCMessage)) { return kIOReturnIPCError; } copyerr = copyin(msg, &buffer.buffer[0], inSize); if (copyerr) { return kIOReturnVMError; } message = (typeof(message))p; refs = message->objectRefs; if ((refs > 2) || !refs) { return kIOReturnUnsupported; } if (!(kIORPCMessageSimpleReply & message->flags)) { return kIOReturnUnsupported; } message->flags &= ~(kIORPCMessageKernel | kIORPCMessageRemote); descs = (typeof(descs))(p - refs * sizeof(*descs)); mach = (typeof(mach))(p - refs * sizeof(*descs) - sizeof(*mach)); mach->msgh.msgh_id = kIORPCVersionCurrent; mach->msgh.msgh_size = (mach_msg_size_t) (sizeof(IORPCMessageMach) + refs * sizeof(*descs) + inSize); // totalSize was checked mach->msgh_body.msgh_descriptor_count = ((mach_msg_size_t) refs); rpc.message = mach; rpc.sendSize = mach->msgh.msgh_size; rpc.reply = (IORPCMessageMach *) (p + inSize); rpc.replySize = ((uint32_t) (sizeof(buffer.buffer) - inSize)); // inSize was checked rpc.kernelContent = message; message->objects[0] = 0; if ((action = OSDynamicCast(OSAction, object))) { maxObjectCount = MAX_OBJECT_COUNT(rpc.message, rpc.sendSize, message); if (refs > maxObjectCount) { return kIOReturnBadArgument; } if (refs < 2) { DKLOG("invalid refs count %qd in message id 0x%qx\n", refs, message->msgid); return kIOReturnBadArgument; } object = IOUserServer::target(action, message); message->objects[1] = (OSObjectRef) action; if (kIODKLogIPC & gIODKDebug) { DKLOG("%s::Dispatch(trap) kernel 0x%qx\n", object->getMetaClass()->getClassName(), message->msgid); } ret = object->Dispatch(rpc); } else { objectArg1 = NULL; if (refs > 1) { if (objectName1) { objectArg1 = iokit_lookup_uext_ref_current_task(objectName1); if (!objectArg1) { return kIOReturnIPCError; } } message->objects[1] = (OSObjectRef) objectArg1; } if (kIODKLogIPC & gIODKDebug) { DKLOG("%s::Dispatch(trap) kernel 0x%qx\n", object->getMetaClass()->getClassName(), message->msgid); } ret = object->Dispatch(rpc); if (kIODKLogIPC & gIODKDebug) { DKLOG("%s::Dispatch(trap) kernel 0x%qx 0x%x\n", object->getMetaClass()->getClassName(), message->msgid, ret); } OSSafeReleaseNULL(objectArg1); if (kIOReturnSuccess == ret) { if (rpc.reply->msgh_body.msgh_descriptor_count) { return kIOReturnIPCError; } reply = IORPCMessageFromMachReply(rpc.reply); if (!reply) { return kIOReturnIPCError; } copySize = rpc.reply->msgh.msgh_size - (((uintptr_t) reply) - ((uintptr_t) rpc.reply)) + sizeof(uint64_t); if (copySize > outSize) { return kIOReturnIPCError; } replyHdr = (UEXTTrapReply *) ((uintptr_t)reply - sizeof(uint64_t)); replyHdr->replySize = copySize; copyerr = copyout(replyHdr, out, copySize); if (copyerr) { return kIOReturnVMError; } } } return ret; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ IOReturn IOUserServer::rpc(IORPC rpc) { if (isInactive() && !fRootQueue) { return kIOReturnOffline; } IOReturn ret; IORPCMessage * message; IORPCMessageMach * mach; mach_msg_id_t machid; uint32_t sendSize, replySize; bool oneway; uint64_t msgid; IODispatchQueue * queue; IOService * service; ipc_port_t port; ipc_port_t sendPort; queue = NULL; port = NULL; sendPort = NULL; mach = rpc.message; sendSize = rpc.sendSize; replySize = rpc.replySize; assert(sendSize >= (sizeof(IORPCMessageMach) + sizeof(IORPCMessage))); message = rpc.kernelContent; if (!message) { return kIOReturnIPCError; } msgid = message->msgid; machid = (msgid >> 32); if (mach->msgh_body.msgh_descriptor_count < 1) { return kIOReturnNoMedia; } IOLockLock(gIOUserServerLock); if ((service = OSDynamicCast(IOService, (OSObject *) message->objects[0]))) { queue = queueForObject(service, msgid); } if (!queue) { queue = fRootQueue; } if (queue && (kIODispatchQueueStopped != queue)) { port = queue->ivars->serverPort; } if (port) { sendPort = ipc_port_copy_send_mqueue(port); } IOLockUnlock(gIOUserServerLock); if (!sendPort) { return kIOReturnNotReady; } oneway = (0 != (kIORPCMessageOneway & message->flags)); ret = copyOutObjects(mach, message, sendSize, false); mach->msgh.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, (oneway ? 0 : MACH_MSG_TYPE_MAKE_SEND_ONCE)); mach->msgh.msgh_remote_port = sendPort; mach->msgh.msgh_local_port = (oneway ? MACH_PORT_NULL : mig_get_reply_port()); mach->msgh.msgh_id = kIORPCVersionCurrent; mach->msgh.msgh_reserved = 0; boolean_t message_moved; if (oneway) { ret = kernel_mach_msg_send(&mach->msgh, sendSize, MACH_SEND_KERNEL_DEFAULT, 0, &message_moved); } else { assert(replySize >= (sizeof(IORPCMessageMach) + sizeof(IORPCMessage))); ret = kernel_mach_msg_rpc(&mach->msgh, sendSize, replySize, FALSE, &message_moved); } ipc_port_release_send(sendPort); if (MACH_MSG_SUCCESS != ret) { if (kIODKLogIPC & gIODKDebug) { DKLOG("mach_msg() failed 0x%x\n", ret); } if (!message_moved) { // release ports copyInObjects(mach, message, sendSize, false, true); } } if ((KERN_SUCCESS == ret) && !oneway) { if (kIORPCVersionCurrentReply != mach->msgh.msgh_id) { ret = (MACH_NOTIFY_SEND_ONCE == mach->msgh.msgh_id) ? MIG_SERVER_DIED : MIG_REPLY_MISMATCH; } else if ((replySize = mach->msgh.msgh_size) < (sizeof(IORPCMessageMach) + sizeof(IORPCMessage))) { // printf("BAD REPLY SIZE\n"); ret = MIG_BAD_ARGUMENTS; } else { if (!(MACH_MSGH_BITS_COMPLEX & mach->msgh.msgh_bits)) { mach->msgh_body.msgh_descriptor_count = 0; } message = IORPCMessageFromMachReply(mach); if (!message) { ret = kIOReturnIPCError; } else if (message->msgid != msgid) { // printf("BAD REPLY ID\n"); ret = MIG_BAD_ARGUMENTS; } else { bool isError = (0 != (kIORPCMessageError & message->flags)); ret = copyInObjects(mach, message, replySize, !isError, true); if (kIOReturnSuccess != ret) { if (kIODKLogIPC & gIODKDebug) { DKLOG("rpc copyin(0x%x) %x\n", ret, mach->msgh.msgh_id); } if (!isError) { consumeObjects(mach, message, replySize); copyInObjects(mach, message, replySize, false, true); } return KERN_NOT_SUPPORTED; } if (isError) { IORPCMessageErrorReturnContent * errorMsg = (typeof(errorMsg))message; ret = errorMsg->result; } } } } return ret; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static IORPCMessage * IORPCMessageFromMachReply(IORPCMessageMach * msg) { mach_msg_size_t idx, count; mach_msg_port_descriptor_t * desc; mach_msg_port_descriptor_t * maxDesc; size_t size, msgsize; bool upgrade; bool reply = true; msgsize = msg->msgh.msgh_size; count = msg->msgh_body.msgh_descriptor_count; desc = &msg->objects[0]; maxDesc = (typeof(maxDesc))(((uintptr_t) msg) + msgsize); upgrade = (msg->msgh.msgh_id != (reply ? kIORPCVersionCurrentReply : kIORPCVersionCurrent)); if (upgrade) { OSReportWithBacktrace("obsolete message"); return NULL; } for (idx = 0; idx < count; idx++) { if (desc >= maxDesc) { return NULL; } switch (desc->type) { case MACH_MSG_PORT_DESCRIPTOR: size = sizeof(mach_msg_port_descriptor_t); break; case MACH_MSG_OOL_DESCRIPTOR: size = sizeof(mach_msg_ool_descriptor_t); break; default: return NULL; } desc = (typeof(desc))(((uintptr_t) desc) + size); } return (IORPCMessage *)(uintptr_t) desc; } ipc_port_t IOUserServer::copySendRightForObject(OSObject * object, ipc_kobject_type_t type) { ipc_port_t port; ipc_port_t sendPort = NULL; ipc_kobject_t kobj; port = iokit_port_for_object(object, type, &kobj); if (port) { sendPort = ipc_kobject_make_send(port, kobj, type); iokit_release_port(port); } return sendPort; } OSObject * IOUserServer::copyObjectForSendRight(ipc_port_t port, ipc_kobject_type_t type) { OSObject * object; object = iokit_lookup_io_object(port, type); return object; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // Create a vm_map_copy_t or kalloc'ed data for memory // to be copied out. ipc will free after the copyout. static kern_return_t copyoutkdata(const void * data, vm_size_t len, void ** buf) { kern_return_t err; vm_map_copy_t copy; err = vm_map_copyin( kernel_map, CAST_USER_ADDR_T(data), len, false /* src_destroy */, ©); assert( err == KERN_SUCCESS ); if (err == KERN_SUCCESS) { *buf = (char *) copy; } return err; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ IOReturn IOUserServer::copyOutObjects(IORPCMessageMach * mach, IORPCMessage * message, size_t size, bool consume) { uint64_t refs; uint32_t idx, maxObjectCount; ipc_port_t port; OSObject * object; size_t descsize; mach_msg_port_descriptor_t * desc; mach_msg_ool_descriptor_t * ool; vm_map_copy_t copy; void * address; mach_msg_size_t length; kern_return_t kr; OSSerialize * s; refs = message->objectRefs; maxObjectCount = MAX_OBJECT_COUNT(mach, size, message); // assert(refs <= mach->msgh_body.msgh_descriptor_count); // assert(refs <= maxObjectCount); if (refs > mach->msgh_body.msgh_descriptor_count) { return kIOReturnBadArgument; } if (refs > maxObjectCount) { return kIOReturnBadArgument; } desc = &mach->objects[0]; for (idx = 0; idx < refs; idx++) { object = (OSObject *) message->objects[idx]; switch (desc->type) { case MACH_MSG_PORT_DESCRIPTOR: descsize = sizeof(mach_msg_port_descriptor_t); port = NULL; if (object) { #if DEVELOPMENT || DEBUG if (kIODKLogIPC & gIODKDebug) { IOMemoryDescriptor * iomd = OSDynamicCast(IOMemoryDescriptor, object); if (iomd != NULL && (iomd->getFlags() & kIOMemoryThreadSafe) == 0) { OSReportWithBacktrace("IOMemoryDescriptor %p was created without kIOMemoryThreadSafe flag", iomd); } } #endif /* DEVELOPMENT || DEBUG */ port = copySendRightForObject(object, IKOT_UEXT_OBJECT); if (!port) { break; } if (consume) { object->release(); } message->objects[idx] = 0; } // desc->type = MACH_MSG_PORT_DESCRIPTOR; desc->disposition = MACH_MSG_TYPE_MOVE_SEND; desc->name = port; desc->pad2 = 0; desc->pad_end = 0; break; case MACH_MSG_OOL_DESCRIPTOR: descsize = sizeof(mach_msg_ool_descriptor_t); length = 0; address = NULL; if (object) { s = OSSerialize::binaryWithCapacity(4096); assert(s); if (!s) { break; } s->setIndexed(true); if (!object->serialize(s)) { assert(false); descsize = -1UL; s->release(); break; } length = s->getLength(); kr = copyoutkdata(s->text(), length, &address); s->release(); if (KERN_SUCCESS != kr) { descsize = -1UL; address = NULL; length = 0; } if (consume) { object->release(); } message->objects[idx] = 0; } ool = (typeof(ool))desc; // ool->type = MACH_MSG_OOL_DESCRIPTOR; ool->deallocate = false; ool->copy = MACH_MSG_PHYSICAL_COPY; ool->size = length; ool->address = address; break; default: descsize = -1UL; break; } if (-1UL == descsize) { break; } desc = (typeof(desc))(((uintptr_t) desc) + descsize); } if (idx >= refs) { return kIOReturnSuccess; } desc = &mach->objects[0]; while (idx--) { switch (desc->type) { case MACH_MSG_PORT_DESCRIPTOR: descsize = sizeof(mach_msg_port_descriptor_t); port = desc->name; if (port) { ipc_port_release_send(port); } break; case MACH_MSG_OOL_DESCRIPTOR: descsize = sizeof(mach_msg_ool_descriptor_t); ool = (typeof(ool))desc; copy = (vm_map_copy_t) ool->address; if (copy) { vm_map_copy_discard(copy); } break; default: descsize = -1UL; break; } if (-1UL == descsize) { break; } desc = (typeof(desc))(((uintptr_t) desc) + descsize); } return kIOReturnBadArgument; } IOReturn IOUserServer::copyInObjects(IORPCMessageMach * mach, IORPCMessage * message, size_t size, bool copyObjects, bool consumePorts) { uint64_t refs; uint32_t idx, maxObjectCount; ipc_port_t port; OSObject * object; size_t descsize; mach_msg_port_descriptor_t * desc; mach_msg_ool_descriptor_t * ool; vm_map_address_t copyoutdata; kern_return_t kr; refs = message->objectRefs; maxObjectCount = MAX_OBJECT_COUNT(mach, size, message); // assert(refs <= mach->msgh_body.msgh_descriptor_count); // assert(refs <= maxObjectCount); if (refs > mach->msgh_body.msgh_descriptor_count) { return kIOReturnBadArgument; } if (refs > maxObjectCount) { return kIOReturnBadArgument; } for (idx = 0; idx < refs; idx++) { message->objects[idx] = (OSObjectRef) NULL; } desc = &mach->objects[0]; for (idx = 0; idx < mach->msgh_body.msgh_descriptor_count; idx++) { bool isObjectPort = idx < refs; switch (desc->type) { case MACH_MSG_PORT_DESCRIPTOR: descsize = sizeof(mach_msg_port_descriptor_t); object = NULL; port = desc->name; if (port) { if (isObjectPort && copyObjects) { object = copyObjectForSendRight(port, IKOT_UEXT_OBJECT); if (!object) { descsize = -1UL; break; } } if (consumePorts) { ipc_port_release_send(port); desc->name = MACH_PORT_NULL; } } break; case MACH_MSG_OOL_DESCRIPTOR: descsize = sizeof(mach_msg_ool_descriptor_t); ool = (typeof(ool))desc; object = NULL; if (isObjectPort && copyObjects && ool->size && ool->address) { kr = vm_map_copyout(kernel_map, ©outdata, (vm_map_copy_t) ool->address); if (KERN_SUCCESS == kr) { object = OSUnserializeXML((const char *) copyoutdata, ool->size); kr = vm_deallocate(kernel_map, copyoutdata, ool->size); assert(KERN_SUCCESS == kr); // vm_map_copyout() has consumed the vm_map_copy_t in the message ool->size = 0; ool->address = NULL; } if (!object) { descsize = -1UL; break; } } break; default: descsize = -1UL; break; } if (-1UL == descsize) { break; } if (isObjectPort && copyObjects) { message->objects[idx] = (OSObjectRef) object; } desc = (typeof(desc))(((uintptr_t) desc) + descsize); } if (idx >= refs) { return kIOReturnSuccess; } while (idx--) { object = (OSObject *) message->objects[idx]; OSSafeReleaseNULL(object); message->objects[idx] = 0; } return kIOReturnBadArgument; } IOReturn IOUserServer::consumeObjects(IORPCMessageMach *mach, IORPCMessage * message, size_t messageSize) { uint64_t refs, idx; OSObject * object; refs = message->objectRefs; uint32_t maxObjectCount = MAX_OBJECT_COUNT(mach, messageSize, message); if (refs > mach->msgh_body.msgh_descriptor_count) { return kIOReturnBadArgument; } if (refs > maxObjectCount) { return kIOReturnBadArgument; } for (idx = 0; idx < refs; idx++) { object = (OSObject *) message->objects[idx]; if (object) { object->release(); message->objects[idx] = 0; } } return kIOReturnSuccess; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static kern_return_t acknowledgeSetPowerState(IOService * service); bool IOUserServer::finalize(IOOptionBits options) { OSArray * services; if (kIODKLogSetup & gIODKDebug) { DKLOG("%s::finalize(%p)\n", getName(), this); } IOLockLock(gIOUserServerLock); OSSafeReleaseNULL(fRootQueue); IOLockUnlock(gIOUserServerLock); services = NULL; IOLockLock(fLock); if (fServices) { services = OSArray::withArray(fServices); } IOLockUnlock(fLock); IOOptionBits terminateFlags = kIOServiceTerminateNeedWillTerminate | kIOServiceTerminateWithRematch; if (fCheckInToken) { bool can_rematch = fCheckInToken->dextTerminate(); if (can_rematch) { terminateFlags |= kIOServiceTerminateWithRematchCurrentDext; } else { DKLOG("%s::finalize(%p) dext was replaced, do not rematch current dext\n", getName(), this); } } else { terminateFlags |= kIOServiceTerminateWithRematchCurrentDext; DKLOG("%s::finalize(%p) could not find fCheckInToken\n", getName(), this); } if (services) { services->iterateObjects(^bool (OSObject * obj) { int service __unused; // hide outer defn IOService * nextService; IOService * provider; bool started = false; nextService = (IOService *) obj; if (kIODKLogSetup & gIODKDebug) { DKLOG("%s::terminate(" DKS ")\n", getName(), DKN(nextService)); } if (nextService->reserved->uvars) { IOUserClient * nextUserClient = OSDynamicCast(IOUserClient, nextService); provider = nextService->getProvider(); if (nextUserClient) { nextUserClient->setTerminateDefer(provider, false); } (void)::acknowledgeSetPowerState(nextService); started = nextService->reserved->uvars->started; nextService->reserved->uvars->serverDied = true; serviceDidStop(nextService, provider); if (provider != NULL && (terminateFlags & kIOServiceTerminateWithRematchCurrentDext) == 0) { provider->resetRematchProperties(); } if (started) { nextService->terminate(terminateFlags); } } if (!started) { DKLOG("%s::terminate(" DKS ") server exit before start()\n", getName(), DKN(nextService)); serviceStop(nextService, NULL); } return false; }); services->release(); } return IOUserClient::finalize(options); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #undef super #define super IOUserClient2022 OSDefineMetaClassAndStructors(IOUserServer, IOUserClient2022) /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ IOUserClient * IOUserServer::withTask(task_t owningTask) { IOUserServer * inst; assert(owningTask == current_task()); if (!task_is_driver(owningTask)) { DKLOG("IOUserServer may only be created with driver tasks\n"); return NULL; } inst = new IOUserServer; if (inst && !inst->init()) { inst->release(); inst = NULL; return inst; } OS_ANALYZER_SUPPRESS("82033761") inst->PMinit(); inst->fOwningTask = current_task(); task_reference(inst->fOwningTask); inst->fEntitlements = IOUserClient::copyClientEntitlements(inst->fOwningTask); if (!(kIODKDisableEntitlementChecking & gIODKDebug)) { proc_t p; pid_t pid; const char * name; p = (proc_t)get_bsdtask_info(inst->fOwningTask); if (p) { name = proc_best_name(p); pid = proc_pid(p); } else { name = "unknown"; pid = 0; } if (inst->fEntitlements == NULL) { #if DEVELOPMENT || DEBUG panic("entitlements are missing for %s[%d]\n", name, pid); #else DKLOG("entitlements are missing for %s[%d]\n", name, pid); #endif /* DEVELOPMENT || DEBUG */ } const char * dextTeamID = csproc_get_teamid(p); if (dextTeamID != NULL) { inst->fTeamIdentifier = OSString::withCString(dextTeamID); DKLOG("%s[%d] has team identifier %s\n", name, pid, dextTeamID); } if (!IOCurrentTaskHasEntitlement(gIODriverKitEntitlementKey->getCStringNoCopy())) { IOLog(kIODriverKitEntitlementKey " entitlement check failed for %s[%d]\n", name, pid); inst->release(); inst = NULL; return inst; } } /* Mark the current task's space as eligible for uext object ports */ iokit_label_dext_task(inst->fOwningTask); inst->fLock = IOLockAlloc(); inst->fServices = OSArray::withCapacity(4); inst->fClasses = OSDictionary::withCapacity(16); inst->fClasses->setOptions(OSCollection::kSort, OSCollection::kSort); inst->fPlatformDriver = task_get_platform_binary(inst->fOwningTask); if (csproc_get_validation_category(current_proc(), &inst->fCSValidationCategory) != KERN_SUCCESS) { inst->fCSValidationCategory = CS_VALIDATION_CATEGORY_INVALID; } inst->fWorkLoop = IOWorkLoop::workLoop(); inst->setProperty(kIOUserClientDefaultLockingKey, kOSBooleanTrue); inst->setProperty(kIOUserClientDefaultLockingSetPropertiesKey, kOSBooleanTrue); inst->setProperty(kIOUserClientDefaultLockingSingleThreadExternalMethodKey, kOSBooleanTrue); //requirement for gIODriverKitEntitlementKey is enforced elsewhere conditionally inst->setProperty(kIOUserClientEntitlementsKey, kOSBooleanFalse); return inst; } static bool gIOUserServerLeakObjects = false; bool IOUserServer::shouldLeakObjects() { return gIOUserServerLeakObjects; } void IOUserServer::beginLeakingObjects() { gIOUserServerLeakObjects = true; } bool IOUserServer::isPlatformDriver() { return fPlatformDriver; } int IOUserServer::getCSValidationCategory() { return fCSValidationCategory; } struct IOUserServerRecordExitReasonContext { task_t task; os_reason_t reason; }; static bool IOUserServerRecordExitReasonMatch(const OSObject *obj, void * context) { IOUserServerRecordExitReasonContext * ctx = (IOUserServerRecordExitReasonContext *)context; IOUserServer * us = OSDynamicCast(IOUserServer, obj); if (us == NULL) { return false; } if (us->fOwningTask == ctx->task) { assert(us->fTaskCrashReason == OS_REASON_NULL); assert(ctx->reason != OS_REASON_NULL); os_reason_ref(ctx->reason); us->fTaskCrashReason = ctx->reason; return true; } return false; } extern "C" void IOUserServerRecordExitReason(task_t task, os_reason_t reason) { IOUserServerRecordExitReasonContext ctx { task, reason }; IOUserServer::gMetaClass.applyToInstances(IOUserServerRecordExitReasonMatch, &ctx); } IOReturn IOUserServer::clientClose(void) { OSArray * services; bool __block unexpectedExit = false; if (kIODKLogSetup & gIODKDebug) { DKLOG("%s::clientClose(%p)\n", getName(), this); } services = NULL; IOLockLock(fLock); if (fServices) { services = OSArray::withArray(fServices); } IOLockUnlock(fLock); // if this was a an expected exit, termination and stop should have detached at this // point, so send any provider still attached and not owned by this user server // the ClientCrashed() notification if (services) { services->iterateObjects(^bool (OSObject * obj) { int service __unused; // hide outer defn IOService * nextService; IOService * provider; nextService = (IOService *) obj; if (nextService->isInactive()) { return false; } if (nextService->reserved && nextService->reserved->uvars && nextService->reserved->uvars->started) { unexpectedExit = true; } provider = nextService->getProvider(); if (provider && (!provider->reserved->uvars || (provider->reserved->uvars->userServer != this))) { if (kIODKLogSetup & gIODKDebug) { DKLOG(DKS "::ClientCrashed(" DKS ")\n", DKN(provider), DKN(nextService)); } if (unexpectedExit) { provider->unregisterAllInterrupts(); } provider->ClientCrashed(nextService, 0); } return false; }); services->release(); } if (unexpectedExit && !gInUserspaceReboot && (fTaskCrashReason != OS_REASON_NULL && fTaskCrashReason->osr_namespace != OS_REASON_JETSAM && fTaskCrashReason->osr_namespace != OS_REASON_RUNNINGBOARD) && fStatistics != NULL) { OSDextCrashPolicy policy = fStatistics->recordCrash(); bool allowPanic; #if DEVELOPMENT || DEBUG allowPanic = !restore_boot && fPlatformDriver && fEntitlements->getObject(gIODriverKitTestDriverEntitlementKey) != kOSBooleanTrue && !disable_dext_crash_reboot; #else allowPanic = !restore_boot && fPlatformDriver; #endif /* DEVELOPMENT || DEBUG */ if (policy == kOSDextCrashPolicyReboot && allowPanic) { panic("Driver %s has crashed too many times\n", getName()); } } terminate(); return kIOReturnSuccess; } IOReturn IOUserServer::setProperties(OSObject * properties) { IOReturn kr = kIOReturnUnsupported; return kr; } void IOUserServer::stop(IOService * provider) { if (fOwningTask) { task_deallocate(fOwningTask); fOwningTask = TASK_NULL; } PMstop(); IOServicePH::serverRemove(this); OSSafeReleaseNULL(fRootQueue); if (fInterruptLock) { IOSimpleLockFree(fInterruptLock); } } IOWorkLoop * IOUserServer::getWorkLoop() const { return fWorkLoop; } void IOUserServer::free() { OSSafeReleaseNULL(fEntitlements); OSSafeReleaseNULL(fClasses); if (fOwningTask) { task_deallocate(fOwningTask); fOwningTask = TASK_NULL; } if (fLock) { IOLockFree(fLock); } OSSafeReleaseNULL(fServices); OSSafeReleaseNULL(fCheckInToken); OSSafeReleaseNULL(fStatistics); OSSafeReleaseNULL(fTeamIdentifier); if (fAllocationName) { kern_allocation_name_release(fAllocationName); fAllocationName = NULL; } if (fTaskCrashReason != OS_REASON_NULL) { os_reason_free(fTaskCrashReason); } OSSafeReleaseNULL(fWorkLoop); IOUserClient::free(); } IOReturn IOUserServer::registerClass(OSClassDescription * desc, uint32_t size, OSUserMetaClass ** pCls) { OSUserMetaClass * cls; const OSSymbol * sym; uint64_t * methodOptions; const char * queueNames; uint32_t methodOptionsEnd, queueNamesEnd; IOReturn ret = kIOReturnSuccess; if (size < sizeof(OSClassDescription)) { assert(false); return kIOReturnBadArgument; } if (kIODKLogSetup & gIODKDebug) { DKLOG("%s::registerClass %s, %d, %d\n", getName(), desc->name, desc->queueNamesSize, desc->methodNamesSize); } if (desc->descriptionSize != size) { assert(false); return kIOReturnBadArgument; } if (os_add_overflow(desc->queueNamesOffset, desc->queueNamesSize, &queueNamesEnd)) { assert(false); return kIOReturnBadArgument; } if (queueNamesEnd > size) { assert(false); return kIOReturnBadArgument; } if (os_add_overflow(desc->methodOptionsOffset, desc->methodOptionsSize, &methodOptionsEnd)) { assert(false); return kIOReturnBadArgument; } if (methodOptionsEnd > size) { assert(false); return kIOReturnBadArgument; } // overlaps? if ((desc->queueNamesOffset >= desc->methodOptionsOffset) && (desc->queueNamesOffset < methodOptionsEnd)) { assert(false); return kIOReturnBadArgument; } if ((queueNamesEnd >= desc->methodOptionsOffset) && (queueNamesEnd < methodOptionsEnd)) { assert(false); return kIOReturnBadArgument; } if (desc->methodOptionsSize & ((2 * sizeof(uint64_t)) - 1)) { assert(false); return kIOReturnBadArgument; } if (sizeof(desc->name) == strnlen(desc->name, sizeof(desc->name))) { assert(false); return kIOReturnBadArgument; } if (sizeof(desc->superName) == strnlen(desc->superName, sizeof(desc->superName))) { assert(false); return kIOReturnBadArgument; } cls = OSTypeAlloc(OSUserMetaClass); assert(cls); if (!cls) { return kIOReturnNoMemory; } cls->description = (typeof(cls->description))IOMallocData(size); assert(cls->description); if (!cls->description) { assert(false); cls->release(); return kIOReturnNoMemory; } bcopy(desc, cls->description, size); cls->methodCount = desc->methodOptionsSize / (2 * sizeof(uint64_t)); cls->methods = IONewData(uint64_t, 2 * cls->methodCount); if (!cls->methods) { assert(false); cls->release(); return kIOReturnNoMemory; } methodOptions = (typeof(methodOptions))(((uintptr_t) desc) + desc->methodOptionsOffset); bcopy(methodOptions, cls->methods, 2 * cls->methodCount * sizeof(uint64_t)); queueNames = (typeof(queueNames))(((uintptr_t) desc) + desc->queueNamesOffset); cls->queueNames = copyInStringArray(queueNames, desc->queueNamesSize); sym = OSSymbol::withCString(desc->name); assert(sym); if (!sym) { assert(false); cls->release(); return kIOReturnNoMemory; } cls->name = sym; cls->meta = OSMetaClass::copyMetaClassWithName(sym); IOLockLock(fLock); cls->superMeta = OSDynamicCast(OSUserMetaClass, fClasses->getObject(desc->superName)); if (fClasses->getObject(sym) != NULL) { /* class with this name exists */ ret = kIOReturnBadArgument; } else { if (fClasses->setObject(sym, cls)) { *pCls = cls; } else { /* could not add class to fClasses */ ret = kIOReturnNoMemory; } } IOLockUnlock(fLock); cls->release(); return ret; } IOReturn IOUserServer::registerClass(OSClassDescription * desc, uint32_t size, OSSharedPtr& pCls) { OSUserMetaClass* pClsRaw = NULL; IOReturn result = registerClass(desc, size, &pClsRaw); if (result == kIOReturnSuccess) { pCls.reset(pClsRaw, OSRetain); } return result; } IOReturn IOUserServer::setRootQueue(IODispatchQueue * queue) { assert(!fRootQueue); if (fRootQueue) { return kIOReturnStillOpen; } queue->retain(); fRootQueue = queue; return kIOReturnSuccess; } IOReturn IOUserServer::externalMethod(uint32_t selector, IOExternalMethodArgumentsOpaque * args) { static const IOExternalMethodDispatch2022 dispatchArray[] = { [kIOUserServerMethodRegisterClass] = { .function = &IOUserServer::externalMethodRegisterClass, .checkScalarInputCount = 0, .checkStructureInputSize = kIOUCVariableStructureSize, .checkScalarOutputCount = 2, .checkStructureOutputSize = 0, .allowAsync = false, .checkEntitlement = NULL, }, [kIOUserServerMethodStart] = { .function = &IOUserServer::externalMethodStart, .checkScalarInputCount = 1, .checkStructureInputSize = 0, .checkScalarOutputCount = 1, .checkStructureOutputSize = 0, .allowAsync = false, .checkEntitlement = NULL, }, }; return dispatchExternalMethod(selector, args, dispatchArray, sizeof(dispatchArray) / sizeof(dispatchArray[0]), this, NULL); } IOReturn IOUserServer::externalMethodRegisterClass(OSObject * target, void * reference, IOExternalMethodArguments * args) { IOReturn ret = kIOReturnBadArgument; mach_port_name_t portname; IOUserServer * me = (typeof(me))target; OSUserMetaClass * cls; if (!args->structureInputSize) { return kIOReturnBadArgument; } ret = me->registerClass((OSClassDescription *) args->structureInput, args->structureInputSize, &cls); if (kIOReturnSuccess == ret) { portname = iokit_make_send_right(me->fOwningTask, cls, IKOT_UEXT_OBJECT); assert(portname); args->scalarOutput[0] = portname; args->scalarOutput[1] = kOSObjectRPCRemote; } return ret; } IOReturn IOUserServer::externalMethodStart(OSObject * target, void * reference, IOExternalMethodArguments * args) { mach_port_name_t portname = 0; IOReturn ret = kIOReturnSuccess; IOUserServer * me = (typeof(me))target; if (!(kIODKDisableCheckInTokenVerification & gIODKDebug)) { mach_port_name_t checkInPortName = ((typeof(checkInPortName))args->scalarInput[0]); OSObject * obj = iokit_lookup_object_with_port_name(checkInPortName, IKOT_IOKIT_IDENT, me->fOwningTask); IOUserServerCheckInToken * retrievedToken = OSDynamicCast(IOUserServerCheckInToken, obj); if (retrievedToken != NULL) { ret = me->setCheckInToken(retrievedToken); } else { ret = kIOReturnBadArgument; } OSSafeReleaseNULL(obj); } if (ret == kIOReturnSuccess) { portname = iokit_make_send_right(me->fOwningTask, me, IKOT_UEXT_OBJECT); assert(portname); } args->scalarOutput[0] = portname; return ret; } IOExternalTrap * IOUserServer::getTargetAndTrapForIndex( IOService **targetP, UInt32 index ) { static const OSBoundedArray trapTemplate = {{ { NULL, (IOTrap) & IOUserServer::waitInterruptTrap}, }}; if (index >= trapTemplate.size()) { return NULL; } *targetP = this; return (IOExternalTrap *)&trapTemplate[index]; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ IOReturn IOUserServer::serviceAttach(IOService * service, IOService * provider) { IOReturn ret; OSObjectUserVars * vars; OSObject * prop; OSString * str; OSSymbol const* bundleID; char execPath[1024]; vars = IOMallocType(OSObjectUserVars); service->reserved->uvars = vars; vars->userServer = this; vars->userServer->retain(); vars->uvarsLock = IOLockAlloc(); IOLockLock(fLock); if (-1U == fServices->getNextIndexOfObject(service, 0)) { fServices->setObject(service); // Add to IOAssociatedServices OSObject * serviceArrayObj = copyProperty(gIOAssociatedServicesKey); OSArray * serviceArray = OSDynamicCast(OSArray, serviceArrayObj); if (!serviceArray) { serviceArray = OSArray::withCapacity(0); } else { serviceArray = OSDynamicCast(OSArray, serviceArray->copyCollection()); assert(serviceArray != NULL); } OSNumber * registryEntryNumber = OSNumber::withNumber(service->getRegistryEntryID(), 64); serviceArray->setObject(registryEntryNumber); setProperty(gIOAssociatedServicesKey, serviceArray); OSSafeReleaseNULL(registryEntryNumber); OSSafeReleaseNULL(serviceArray); OSSafeReleaseNULL(serviceArrayObj); // populate kIOUserClassesKey OSUserMetaClass * userMeta; OSArray * classesArray; const OSString * str2; classesArray = OSArray::withCapacity(4); prop = service->copyProperty(gIOUserClassKey); str2 = OSDynamicCast(OSString, prop); userMeta = (typeof(userMeta))service->reserved->uvars->userServer->fClasses->getObject(str2); while (str2 && userMeta) { classesArray->setObject(str2); userMeta = userMeta->superMeta; if (userMeta) { str2 = userMeta->name; } } service->setProperty(gIOUserClassesKey, classesArray); OSSafeReleaseNULL(classesArray); OSSafeReleaseNULL(prop); } IOLockUnlock(fLock); prop = service->copyProperty(gIOUserClassKey); str = OSDynamicCast(OSString, prop); if (str) { service->setName(str); } OSSafeReleaseNULL(prop); prop = service->copyProperty(gIOModuleIdentifierKey); bundleID = OSDynamicCast(OSSymbol, prop); if (bundleID) { execPath[0] = 0; bool ok = OSKext::copyUserExecutablePath(bundleID, execPath, sizeof(execPath)); if (ok) { ret = LoadModule(execPath); if (kIODKLogSetup & gIODKDebug) { DKLOG("%s::LoadModule 0x%x %s\n", getName(), ret, execPath); } } } OSSafeReleaseNULL(prop); ret = kIOReturnSuccess; return ret; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ IOReturn IOUserServer::serviceNewUserClient(IOService * service, task_t owningTask, void * securityID, uint32_t type, OSDictionary * properties, IOUserClient ** handler) { IOReturn ret; IOUserClient * uc; IOUserUserClient * userUC; OSDictionary * entitlements; OSObject * prop; OSObject * bundleID; bool ok = false; entitlements = IOUserClient::copyClientEntitlements(owningTask); if (!entitlements) { entitlements = OSDictionary::withCapacity(8); } if (entitlements) { if (kIOReturnSuccess == clientHasPrivilege((void *) owningTask, kIOClientPrivilegeAdministrator)) { entitlements->setObject(kIODriverKitUserClientEntitlementAdministratorKey, kOSBooleanTrue); } OSString * creatorName = IOCopyLogNameForPID(proc_selfpid()); if (creatorName) { entitlements->setObject(kIOUserClientCreatorKey, creatorName); OSSafeReleaseNULL(creatorName); } } *handler = NULL; ret = service->_NewUserClient(type, entitlements, &uc); if (kIOReturnSuccess != ret) { OSSafeReleaseNULL(entitlements); return ret; } userUC = OSDynamicCast(IOUserUserClient, uc); if (!userUC) { if (uc) { uc->terminate(kIOServiceTerminateNeedWillTerminate); uc->setTerminateDefer(service, false); OSSafeReleaseNULL(uc); } OSSafeReleaseNULL(entitlements); return kIOReturnUnsupported; } userUC->setTask(owningTask); if (!(kIODKDisableEntitlementChecking & gIODKDebug)) { do { bool checkiOS3pEntitlements; // check if client has com.apple.private.driverkit.driver-access and the required entitlements match the driver's entitlements if (entitlements && (prop = entitlements->getObject(gIODriverKitRequiredEntitlementsKey))) { prop->retain(); ok = checkEntitlements(fEntitlements, prop, NULL, NULL); if (ok) { break; } else { DKLOG(DKS ":UC failed required entitlement check\n", DKN(userUC)); } } #if XNU_TARGET_OS_IOS checkiOS3pEntitlements = !fPlatformDriver; if (checkiOS3pEntitlements && fTeamIdentifier == NULL) { DKLOG("warning: " DKS " does not have a team identifier\n", DKN(this)); } #else checkiOS3pEntitlements = false; #endif if (checkiOS3pEntitlements) { // App must have com.apple.developer.driverkit.communicates-with-drivers ok = entitlements && entitlements->getObject(gIODriverKitUserClientEntitlementCommunicatesWithDriversKey) == kOSBooleanTrue; if (ok) { // check team ID const char * clientTeamID = csproc_get_teamid(current_proc()); bool sameTeam = fTeamIdentifier != NULL && clientTeamID != NULL && strncmp(fTeamIdentifier->getCStringNoCopy(), clientTeamID, CS_MAX_TEAMID_LEN) == 0; if (sameTeam) { ok = true; } else { // different team IDs, dext must have com.apple.developer.driverkit.allow-third-party-userclients ok = fEntitlements && fEntitlements->getObject(gIODriverKitUserClientEntitlementAllowThirdPartyUserClientsKey) == kOSBooleanTrue; } if (!ok) { DKLOG(DKS ":UC failed team ID check. client team=%s, driver team=%s\n", DKN(userUC), clientTeamID ? clientTeamID : "(null)", fTeamIdentifier ? fTeamIdentifier->getCStringNoCopy() : "(null)"); } } else { DKLOG(DKS ":UC entitlement check failed, app does not have %s entitlement\n", DKN(userUC), gIODriverKitUserClientEntitlementCommunicatesWithDriversKey->getCStringNoCopy()); } // When checking iOS 3rd party entitlements, do not fall through to other entitlement checks break; } // first party dexts and third party macOS dexts // check if driver has com.apple.developer.driverkit.allow-any-userclient-access if (fEntitlements && fEntitlements->getObject(gIODriverKitUserClientEntitlementAllowAnyKey)) { ok = true; break; } // check if client has com.apple.developer.driverkit.userclient-access and its value matches the bundle ID of the service bundleID = service->copyProperty(gIOModuleIdentifierKey); ok = (entitlements && bundleID && (prop = entitlements->getObject(gIODriverKitUserClientEntitlementsKey))); if (ok) { bool found __block = false; ok = prop->iterateObjects(^bool (OSObject * object) { found = object->isEqualTo(bundleID); return found; }); ok = found; } else { OSString * bundleIDStr = OSDynamicCast(OSString, bundleID); DKLOG(DKS ":UC failed userclient-access check, needed bundle ID %s\n", DKN(userUC), bundleIDStr ? bundleIDStr->getCStringNoCopy() : "(null)"); } OSSafeReleaseNULL(bundleID); } while (false); if (ok) { prop = userUC->copyProperty(gIOServiceDEXTEntitlementsKey); ok = checkEntitlements(entitlements, prop, NULL, NULL); } if (!ok) { DKLOG(DKS ":UC entitlements check failed\n", DKN(userUC)); uc->terminate(kIOServiceTerminateNeedWillTerminate); uc->setTerminateDefer(service, false); OSSafeReleaseNULL(uc); OSSafeReleaseNULL(entitlements); return kIOReturnNotPermitted; } } OSSafeReleaseNULL(entitlements); *handler = userUC; return ret; } IOReturn IOUserServer::serviceNewUserClient(IOService * service, task_t owningTask, void * securityID, uint32_t type, OSDictionary * properties, OSSharedPtr& handler) { IOUserClient* handlerRaw = NULL; IOReturn result = serviceNewUserClient(service, owningTask, securityID, type, properties, &handlerRaw); handler.reset(handlerRaw, OSNoRetain); return result; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static IOPMPowerState sPowerStates[] = { { .version = kIOPMPowerStateVersion1, .capabilityFlags = 0, .outputPowerCharacter = 0, .inputPowerRequirement = 0}, { .version = kIOPMPowerStateVersion1, .capabilityFlags = kIOPMLowPower, .outputPowerCharacter = kIOPMLowPower, .inputPowerRequirement = kIOPMLowPower}, { .version = kIOPMPowerStateVersion1, .capabilityFlags = kIOPMPowerOn, .outputPowerCharacter = kIOPMPowerOn, .inputPowerRequirement = kIOPMPowerOn}, }; enum { kUserServerMaxPowerState = 2 }; IOReturn IOUserServer::serviceJoinPMTree(IOService * service) { IOReturn ret; IOService * pmProvider; bool joinTree; if (service->reserved->uvars->userServerPM) { return kIOReturnSuccess; } if (!fRootNotifier) { ret = registerPowerDriver(this, sPowerStates, sizeof(sPowerStates) / sizeof(sPowerStates[0])); assert(kIOReturnSuccess == ret); IOServicePH::serverAdd(this); fRootNotifier = true; } joinTree = false; if (!(kIODKDisablePM & gIODKDebug) && !service->pm_vars) { kern_return_t kr; OSDictionary * props; kr = service->CopyProperties_Local(&props); if (kIOReturnSuccess == kr) { if (props->getObject(kIOPMResetPowerStateOnWakeKey) == kOSBooleanTrue) { service->setProperty(kIOPMResetPowerStateOnWakeKey, kOSBooleanTrue); } OSSafeReleaseNULL(props); } service->PMinit(); ret = service->registerPowerDriver(this, sPowerStates, sizeof(sPowerStates) / sizeof(sPowerStates[0])); assert(kIOReturnSuccess == ret); joinTree = true; } pmProvider = service; while (pmProvider && !pmProvider->inPlane(gIOPowerPlane)) { pmProvider = pmProvider->getProvider(); } if (!pmProvider) { pmProvider = getPMRootDomain(); } if (pmProvider) { IOService * entry; OSObject * prop; OSObject * nextProp; OSString * str; entry = pmProvider; prop = NULL; do { nextProp = entry->copyProperty("non-removable"); if (nextProp) { OSSafeReleaseNULL(prop); prop = nextProp; } entry = entry->getProvider(); } while (entry); if (prop) { str = OSDynamicCast(OSString, prop); if (str && str->isEqualTo("yes")) { pmProvider = NULL; } prop->release(); } } if (!(kIODKDisablePM & gIODKDebug) && pmProvider) { IOLockLock(fLock); service->reserved->uvars->powerState = true; IOLockUnlock(fLock); if (joinTree) { pmProvider->joinPMtree(service); service->reserved->uvars->userServerPM = true; service->reserved->uvars->resetPowerOnWake = service->propertyExists(kIOPMResetPowerStateOnWakeKey); } } service->registerInterestedDriver(this); return kIOReturnSuccess; } IOReturn IOUserServer::setPowerState(unsigned long state, IOService * service) { if (kIODKLogPM & gIODKDebug) { DKLOG(DKS "::setPowerState(%ld) %d\n", DKN(service), state, fSystemPowerAck); } return kIOPMAckImplied; } IOReturn IOUserServer::serviceSetPowerState(IOService * controllingDriver, IOService * service, IOPMPowerFlags flags, unsigned long state) { IOReturn ret; bool sendIt = false; IOLockLock(fLock); if (service->reserved->uvars) { if (!fSystemOff && !(kIODKDisablePM & gIODKDebug)) { OSDictionary * wakeDescription; OSObject * prop; char wakeReasonString[128]; wakeDescription = OSDictionary::withCapacity(4); if (wakeDescription) { wakeReasonString[0] = 0; getPMRootDomain()->copyWakeReasonString(wakeReasonString, sizeof(wakeReasonString)); if (wakeReasonString[0]) { prop = OSString::withCString(&wakeReasonString[0]); wakeDescription->setObject(gIOSystemStateWakeDescriptionWakeReasonKey, prop); OSSafeReleaseNULL(prop); } #if defined(__arm__) || defined(__arm64__) prop = OSNumber::withNumber(ml_get_conttime_offset(), sizeof(uint64_t) * CHAR_BIT); wakeDescription->setObject(gIOSystemStateWakeDescriptionContinuousTimeOffsetKey, prop); OSSafeReleaseNULL(prop); #endif /* defined(__arm__) || defined(__arm64__) */ getSystemStateNotificationService()->StateNotificationItemSet(gIOSystemStateWakeDescriptionKey, wakeDescription); OSSafeReleaseNULL(wakeDescription); } service->reserved->uvars->willPower = true; service->reserved->uvars->willPowerState = state; service->reserved->uvars->controllingDriver = controllingDriver; sendIt = true; } else { service->reserved->uvars->willPower = false; } } IOLockUnlock(fLock); if (sendIt) { if (kIODKLogPM & gIODKDebug) { DKLOG(DKS "::serviceSetPowerState(%ld) %d\n", DKN(service), state, fSystemPowerAck); } ret = service->SetPowerState((uint32_t) flags); if (kIOReturnSuccess == ret) { return 20 * 1000 * 1000; } else { IOLockLock(fLock); service->reserved->uvars->willPower = false; IOLockUnlock(fLock); } } return kIOPMAckImplied; } IOReturn IOUserServer::powerStateWillChangeTo(IOPMPowerFlags flags, unsigned long state, IOService * service) { return kIOPMAckImplied; } IOReturn IOUserServer::powerStateDidChangeTo(IOPMPowerFlags flags, unsigned long state, IOService * service) { unsigned int idx; bool pmAck; pmAck = false; IOLockLock(fLock); idx = fServices->getNextIndexOfObject(service, 0); if (-1U == idx) { IOLockUnlock(fLock); return kIOPMAckImplied; } service->reserved->uvars->powerState = (0 != state); bool allPowerStates __block = service->reserved->uvars->powerState; if (!allPowerStates) { // any service on? fServices->iterateObjects(^bool (OSObject * obj) { int service __unused; // hide outer defn IOService * nextService; nextService = (IOService *) obj; allPowerStates = nextService->reserved->uvars->powerState; // early terminate if true return allPowerStates; }); } if (kIODKLogPM & gIODKDebug) { DKLOG(DKS "::powerStateDidChangeTo(%ld) %d, %d\n", DKN(service), state, allPowerStates, fSystemPowerAck); } if (!allPowerStates && (pmAck = fSystemPowerAck)) { fSystemPowerAck = false; fSystemOff = true; } IOLockUnlock(fLock); if (pmAck) { IOServicePH::serverAck(this); } return kIOPMAckImplied; } bool IOUserServer::checkPMReady() { bool __block ready = true; IOLockLock(fLock); // Check if any services have not completely joined the PM tree (i.e. // addPowerChild has not compeleted). fServices->iterateObjects(^bool (OSObject * obj) { IOPowerConnection *conn; IOService *service = (IOService *) obj; IORegistryEntry *parent = service->getParentEntry(gIOPowerPlane); if ((conn = OSDynamicCast(IOPowerConnection, parent))) { if (!conn->getReadyFlag()) { ready = false; return true; } } return false; }); IOLockUnlock(fLock); return ready; } kern_return_t IOService::JoinPMTree_Impl(void) { if (!reserved->uvars || !reserved->uvars->userServer) { return kIOReturnNotReady; } return reserved->uvars->userServer->serviceJoinPMTree(this); } static kern_return_t acknowledgeSetPowerState(IOService * service) { if (service->reserved->uvars && service->reserved->uvars->userServer && service->reserved->uvars->willPower) { IOReturn ret; service->reserved->uvars->willPower = false; ret = service->reserved->uvars->controllingDriver->setPowerState(service->reserved->uvars->willPowerState, service); if (kIOPMAckImplied == ret) { service->acknowledgeSetPowerState(); } return kIOReturnSuccess; } return kIOReturnNotReady; } kern_return_t IOService::SetPowerState_Impl( uint32_t powerFlags) { if (kIODKLogPM & gIODKDebug) { DKLOG(DKS "::SetPowerState(%d), %d\n", DKN(this), powerFlags, reserved->uvars->willPower); } return ::acknowledgeSetPowerState(this); } kern_return_t IOService::ChangePowerState_Impl( uint32_t powerFlags) { switch (powerFlags) { case kIOServicePowerCapabilityOff: changePowerStateToPriv(0); break; case kIOServicePowerCapabilityLow: changePowerStateToPriv(1); break; case kIOServicePowerCapabilityOn: changePowerStateToPriv(2); break; default: return kIOReturnBadArgument; } return kIOReturnSuccess; } kern_return_t IOService::SetPowerOverride_Impl( bool enable) { kern_return_t ret; if (enable) { ret = powerOverrideOnPriv(); } else { ret = powerOverrideOffPriv(); } return ret == IOPMNoErr ? kIOReturnSuccess : kIOReturnError; } kern_return_t IOService::_ClaimSystemWakeEvent_Impl( IOService * device, uint64_t flags, const char * reason, OSContainer * details) { IOPMrootDomain * rootDomain; IOOptionBits pmFlags; rootDomain = getPMRootDomain(); if (!rootDomain) { return kIOReturnNotReady; } if (os_convert_overflow(flags, &pmFlags)) { return kIOReturnBadArgument; } rootDomain->claimSystemWakeEvent(device, pmFlags, reason, details); return kIOReturnSuccess; } kern_return_t IOService::Create_Impl( IOService * provider, const char * propertiesKey, IOService ** result) { OSObject * inst; IOService * service; OSString * str; const OSSymbol * sym; OSObject * prop = NULL; OSObject * moduleIdentifier = NULL; OSObject * userServerName = NULL; OSDictionary * properties = NULL; OSDictionary * copyProperties = NULL; kern_return_t ret; if (provider != this) { return kIOReturnUnsupported; } ret = kIOReturnUnsupported; inst = NULL; service = NULL; prop = copyProperty(propertiesKey); properties = OSDynamicCast(OSDictionary, prop); if (!properties) { ret = kIOReturnBadArgument; goto finish; } copyProperties = OSDynamicCast(OSDictionary, properties->copyCollection()); if (!copyProperties) { ret = kIOReturnNoMemory; goto finish; } moduleIdentifier = copyProperty(gIOModuleIdentifierKey); if (moduleIdentifier) { copyProperties->setObject(gIOModuleIdentifierKey, moduleIdentifier); } userServerName = reserved->uvars->userServer->copyProperty(gIOUserServerNameKey); if (userServerName) { copyProperties->setObject(gIOUserServerNameKey, userServerName); } str = OSDynamicCast(OSString, copyProperties->getObject(gIOClassKey)); if (!str) { ret = kIOReturnBadArgument; goto finish; } sym = OSSymbol::withString(str); if (sym) { inst = OSMetaClass::allocClassWithName(sym); service = OSDynamicCast(IOService, inst); if (service && service->init(copyProperties) && service->attach(this)) { reserved->uvars->userServer->serviceAttach(service, this); service->reserved->uvars->started = true; ret = kIOReturnSuccess; *result = service; } OSSafeReleaseNULL(sym); } finish: OSSafeReleaseNULL(prop); OSSafeReleaseNULL(copyProperties); OSSafeReleaseNULL(moduleIdentifier); OSSafeReleaseNULL(userServerName); if (kIOReturnSuccess != ret) { OSSafeReleaseNULL(inst); } return ret; } kern_return_t IOService::Terminate_Impl( uint64_t options) { IOUserServer * us; if (options) { return kIOReturnUnsupported; } us = (typeof(us))thread_iokit_tls_get(0); if (us && (!reserved->uvars || (reserved->uvars->userServer != us))) { return kIOReturnNotPermitted; } terminate(kIOServiceTerminateNeedWillTerminate); return kIOReturnSuccess; } kern_return_t IOService::NewUserClient_Impl( uint32_t type, IOUserClient ** userClient) { return kIOReturnError; } kern_return_t IOService::_NewUserClient_Impl( uint32_t type, OSDictionary * entitlements, IOUserClient ** userClient) { return kIOReturnError; } kern_return_t IOService::SearchProperty_Impl( const char * name, const char * plane, uint64_t options, OSContainer ** property) { OSObject * object __block; IOService * provider; IOOptionBits regOptions; if (kIOServiceSearchPropertyParents & options) { regOptions = kIORegistryIterateParents | kIORegistryIterateRecursively; } else { regOptions = 0; } object = copyProperty(name, IORegistryEntry::getPlane(plane), regOptions); if (NULL == object) { for (provider = this; provider; provider = provider->getProvider()) { provider->runPropertyActionBlock(^IOReturn (void) { OSDictionary * userProps; object = provider->getProperty(name); if (!object && (userProps = OSDynamicCast(OSDictionary, provider->getProperty(gIOUserServicePropertiesKey)))) { object = userProps->getObject(name); } if (object) { object->retain(); } return kIOReturnSuccess; }); if (object || !(kIORegistryIterateParents & options)) { break; } } } *property = object; return object ? kIOReturnSuccess : kIOReturnNotFound; } kern_return_t IOService::StringFromReturn_Impl( IOReturn retval, OSString ** str) { OSString *obj = OSString::withCString(stringFromReturn(retval)); *str = obj; return obj ? kIOReturnSuccess : kIOReturnError; } #if PRIVATE_WIFI_ONLY const char * IOService::StringFromReturn( IOReturn retval) { return stringFromReturn(retval); } #endif /* PRIVATE_WIFI_ONLY */ kern_return_t IOService::CopyProviderProperties_Impl( OSArray * propertyKeys, OSArray ** properties) { IOReturn ret; OSArray * result; IOService * provider; result = OSArray::withCapacity(8); if (!result) { return kIOReturnNoMemory; } ret = kIOReturnSuccess; for (provider = this; provider; provider = provider->getProvider()) { OSObject * obj; OSDictionary * props; obj = provider->copyProperty(gIOSupportedPropertiesKey); props = OSDynamicCast(OSDictionary, obj); if (!props) { OSSafeReleaseNULL(obj); props = provider->dictionaryWithProperties(); } if (!props) { ret = kIOReturnNoMemory; break; } bool __block addClass = true; if (propertyKeys) { OSDictionary * retProps; retProps = OSDictionary::withCapacity(4); addClass = false; if (!retProps) { ret = kIOReturnNoMemory; OSSafeReleaseNULL(props); break; } propertyKeys->iterateObjects(^bool (OSObject * _key) { OSString * key = OSDynamicCast(OSString, _key); if (gIOClassKey->isEqualTo(key)) { addClass = true; return false; } retProps->setObject(key, props->getObject(key)); return false; }); OSSafeReleaseNULL(props); props = retProps; } if (addClass) { OSArray * classes = OSArray::withCapacity(8); if (!classes) { OSSafeReleaseNULL(props); ret = kIOReturnNoMemory; break; } for (const OSMetaClass * meta = provider->getMetaClass(); meta; meta = meta->getSuperClass()) { classes->setObject(meta->getClassNameSymbol()); } props->setObject(gIOClassKey, classes); OSSafeReleaseNULL(classes); } bool ok = result->setObject(props); props->release(); if (!ok) { ret = kIOReturnNoMemory; break; } } if (kIOReturnSuccess != ret) { OSSafeReleaseNULL(result); } *properties = result; return ret; } IOReturn IOService::AdjustBusy_Impl(int32_t delta) { adjustBusy(delta); return kIOReturnSuccess; } IOReturn IOService::GetBusyState_Impl(uint32_t *busyState) { *busyState = getBusyState(); return kIOReturnSuccess; } void IOUserServer::systemPower(bool powerOff, bool hibernate) { OSArray * services; { OSDictionary * sleepDescription; OSObject * prop; sleepDescription = OSDictionary::withCapacity(4); if (sleepDescription) { prop = getPMRootDomain()->copyProperty(kRootDomainSleepReasonKey); if (prop) { sleepDescription->setObject(gIOSystemStateSleepDescriptionReasonKey, prop); OSSafeReleaseNULL(prop); } prop = getPMRootDomain()->copyProperty(kIOHibernateStateKey); if (prop) { sleepDescription->setObject(gIOSystemStateSleepDescriptionHibernateStateKey, prop); OSSafeReleaseNULL(prop); } if (hibernate) { uint32_t correctHibernateState = kIOSystemStateSleepDescriptionHibernateStateHibernating; OSData *correctHibernateStateData = OSData::withValue(correctHibernateState); assert(correctHibernateStateData != NULL); sleepDescription->setObject(gIOSystemStateSleepDescriptionHibernateStateKey, correctHibernateStateData); OSSafeReleaseNULL(correctHibernateStateData); } getSystemStateNotificationService()->StateNotificationItemSet(gIOSystemStateSleepDescriptionKey, sleepDescription); OSSafeReleaseNULL(sleepDescription); } } IOLockLock(fLock); services = OSArray::withArray(fServices); bool allPowerStates __block = 0; // any service on? fServices->iterateObjects(^bool (OSObject * obj) { int service __unused; // hide outer defn IOService * nextService; nextService = (IOService *) obj; allPowerStates = nextService->reserved->uvars->powerState; // early terminate if true return allPowerStates; }); if (kIODKLogPM & gIODKDebug) { DKLOG("%s::powerOff(%d) %d\n", getName(), powerOff, allPowerStates); } if (powerOff) { fSystemPowerAck = allPowerStates; if (!fSystemPowerAck) { fSystemOff = true; } IOLockUnlock(fLock); if (!fSystemPowerAck) { IOServicePH::serverAck(this); } else { if (services) { services->iterateObjects(^bool (OSObject * obj) { int service __unused; // hide outer defn IOService * nextService; nextService = (IOService *) obj; if (kIODKLogPM & gIODKDebug) { DKLOG("changePowerStateWithOverrideTo(" DKS ", %d)\n", DKN(nextService), 0); } nextService->reserved->uvars->powerOverride = nextService->reserved->uvars->userServerPM ? kUserServerMaxPowerState : nextService->getPowerState(); nextService->changePowerStateWithOverrideTo(0, 0); return false; }); } } } else { fSystemOff = false; IOLockUnlock(fLock); if (services) { services->iterateObjects(^bool (OSObject * obj) { int service __unused; // hide outer defn IOService * nextService; nextService = (IOService *) obj; if (-1U != nextService->reserved->uvars->powerOverride) { if (kIODKLogPM & gIODKDebug) { DKLOG("%schangePowerStateWithOverrideTo(" DKS ", %d)\n", nextService->reserved->uvars->resetPowerOnWake ? "!" : "", DKN(nextService), nextService->reserved->uvars->powerOverride); } if (!nextService->reserved->uvars->resetPowerOnWake) { nextService->changePowerStateWithOverrideTo(nextService->reserved->uvars->powerOverride, 0); } nextService->reserved->uvars->powerOverride = -1U; } return false; }); } } OSSafeReleaseNULL(services); } void IOUserServer::systemHalt(int howto) { OSArray * services; if (true || (kIODKLogPM & gIODKDebug)) { DKLOG("%s::systemHalt()\n", getName()); } { OSDictionary * haltDescription; OSNumber * state; uint64_t haltStateFlags; haltDescription = OSDictionary::withCapacity(4); if (haltDescription) { haltStateFlags = 0; if (RB_HALT & howto) { haltStateFlags |= kIOServiceHaltStatePowerOff; } else { haltStateFlags |= kIOServiceHaltStateRestart; } state = OSNumber::withNumber(haltStateFlags, 64); haltDescription->setObject(gIOSystemStateHaltDescriptionHaltStateKey, state); getSystemStateNotificationService()->StateNotificationItemSet(gIOSystemStateHaltDescriptionKey, haltDescription); OSSafeReleaseNULL(state); OSSafeReleaseNULL(haltDescription); } } IOLockLock(fLock); services = OSArray::withArray(fServices); IOLockUnlock(fLock); if (services) { services->iterateObjects(^bool (OSObject * obj) { int service __unused; // hide outer defn IOService * nextService; IOService * provider; IOOptionBits terminateOptions; bool root; nextService = (IOService *) obj; provider = nextService->getProvider(); if (!provider) { DKLOG("stale service " DKS " found, skipping termination\n", DKN(nextService)); return false; } root = (NULL == provider->getProperty(gIOUserServerNameKey, gIOServicePlane)); if (true || (kIODKLogPM & gIODKDebug)) { DKLOG("%d: terminate(" DKS ")\n", root, DKN(nextService)); } if (!root) { return false; } terminateOptions = kIOServiceRequired | kIOServiceTerminateNeedWillTerminate; if (!nextService->terminate(terminateOptions)) { IOLog("failed to terminate service %s-0x%llx\n", nextService->getName(), nextService->getRegistryEntryID()); } return false; }); } OSSafeReleaseNULL(services); } void IOUserServer::powerSourceChanged(bool acAttached) { OSDictionary * powerSourceDescription; powerSourceDescription = OSDictionary::withCapacity(4); if (!powerSourceDescription) { return; } powerSourceDescription->setObject(gIOSystemStatePowerSourceDescriptionACAttachedKey, acAttached ? kOSBooleanTrue : kOSBooleanFalse); getSystemStateNotificationService()->StateNotificationItemSet(gIOSystemStatePowerSourceDescriptionKey, powerSourceDescription); OSSafeReleaseNULL(powerSourceDescription); } IOReturn IOUserServer::serviceStarted(IOService * service, IOService * provider, bool result) { IOReturn ret; DKLOG(DKS "::start(" DKS ") %s\n", DKN(service), DKN(provider), result ? "ok" : "fail"); if (!result) { ret = kIOReturnSuccess; return ret; } ret = serviceJoinPMTree(service); service->reserved->uvars->started = true; if (service->reserved->uvars->deferredRegisterService) { service->registerService(kIOServiceAsynchronous | kIOServiceDextRequirePowerForMatching); service->reserved->uvars->deferredRegisterService = false; } return kIOReturnSuccess; } IOReturn IOUserServer::serviceOpen(IOService * provider, IOService * client) { OSObjectUserVars * uvars; IOReturn ret; IOLockLock(client->reserved->uvars->uvarsLock); uvars = client->reserved->uvars; if (uvars->willTerminate || uvars->stopped) { DKLOG(DKS "- " DKS " blocked attempt to open " DKS "\n", DKN(this), DKN(client), DKN(provider)); ret = kIOReturnBadArgument; } else { if (!uvars->openProviders) { uvars->openProviders = OSArray::withObjects((const OSObject **) &provider, 1); } else if (-1U == uvars->openProviders->getNextIndexOfObject(provider, 0)) { uvars->openProviders->setObject(provider); } ret = kIOReturnSuccess; } IOLockUnlock(client->reserved->uvars->uvarsLock); return ret; } IOReturn IOUserServer::serviceClose(IOService * provider, IOService * client) { OSObjectUserVars * uvars; unsigned int idx; IOReturn ret; IOLockLock(client->reserved->uvars->uvarsLock); uvars = client->reserved->uvars; if (!uvars->openProviders) { ret = kIOReturnNotOpen; goto finish; } idx = uvars->openProviders->getNextIndexOfObject(provider, 0); if (-1U == idx) { ret = kIOReturnNotOpen; goto finish; } uvars->openProviders->removeObject(idx); if (!uvars->openProviders->getCount()) { OSSafeReleaseNULL(uvars->openProviders); } ret = kIOReturnSuccess; finish: IOLockUnlock(client->reserved->uvars->uvarsLock); return ret; } IOReturn IOUserServer::serviceStop(IOService * service, IOService *) { IOReturn ret; uint32_t idx; bool pmAck; OSObjectUserVars * uvars; pmAck = false; IOLockLock(fLock); idx = fServices->getNextIndexOfObject(service, 0); if (-1U != idx) { fServices->removeObject(idx); // Remove the service from IOAssociatedServices OSObject * serviceArrayObj = copyProperty(gIOAssociatedServicesKey); OSArray * serviceArray = OSDynamicCast(OSArray, serviceArrayObj); assert(serviceArray != NULL); serviceArray = OSDynamicCast(OSArray, serviceArray->copyCollection()); assert(serviceArray != NULL); // Index should be the same as it was in fServices OSNumber * __assert_only registryEntryID = OSDynamicCast(OSNumber, serviceArray->getObject(idx)); assert(registryEntryID); // ensure it is the right service assert(registryEntryID->unsigned64BitValue() == service->getRegistryEntryID()); serviceArray->removeObject(idx); setProperty(gIOAssociatedServicesKey, serviceArray); OSSafeReleaseNULL(serviceArray); OSSafeReleaseNULL(serviceArrayObj); uvars = service->reserved->uvars; uvars->stopped = true; uvars->powerState = 0; bool allPowerStates __block = 0; // any service on? fServices->iterateObjects(^bool (OSObject * obj) { int service __unused; // hide outer defn IOService * nextService; nextService = (IOService *) obj; allPowerStates = nextService->reserved->uvars->powerState; // early terminate if true return allPowerStates; }); if (!allPowerStates && (pmAck = fSystemPowerAck)) { fSystemPowerAck = false; fSystemOff = true; } } IOLockUnlock(fLock); if (pmAck) { IOServicePH::serverAck(this); } if (-1U == idx) { return kIOReturnSuccess; } (void) service->deRegisterInterestedDriver(this); if (uvars->userServerPM) { service->PMstop(); service->acknowledgeSetPowerState(); } ret = kIOReturnSuccess; return ret; } void IOUserServer::serviceFree(IOService * service) { OSObjectUserVars * uvars; uint32_t idx, queueAlloc; IODispatchQueue ** unboundedQueueArray = NULL; uvars = service->reserved->uvars; if (!uvars) { return; } if (uvars->queueArray && uvars->userMeta) { queueAlloc = 1; if (uvars->userMeta->queueNames) { queueAlloc += uvars->userMeta->queueNames->count; } for (idx = 0; idx < queueAlloc; idx++) { OSSafeReleaseNULL(uvars->queueArray[idx]); } unboundedQueueArray = uvars->queueArray.data(); IOSafeDeleteNULL(unboundedQueueArray, IODispatchQueue *, queueAlloc); uvars->queueArray = OSBoundedArrayRef(); } OSSafeReleaseNULL(uvars->userServer); IOLockFree(uvars->uvarsLock); IOFreeType(service->reserved->uvars, OSObjectUserVars); } void IOUserServer::serviceWillTerminate(IOService * client, IOService * provider, IOOptionBits options) { IOReturn ret; bool willTerminate; willTerminate = false; IOLockLock(client->reserved->uvars->uvarsLock); if (!client->reserved->uvars->serverDied && !client->reserved->uvars->willTerminate) { client->reserved->uvars->willTerminate = true; willTerminate = true; } IOLockUnlock(client->reserved->uvars->uvarsLock); if (willTerminate) { if (provider->isInactive() || IOServicePH::serverSlept()) { client->Stop_async(provider); ret = kIOReturnOffline; } else { ret = client->Stop(provider); } if (kIOReturnSuccess != ret) { IOUserServer::serviceDidStop(client, provider); ret = kIOReturnSuccess; } } } void IOUserServer::serviceDidTerminate(IOService * client, IOService * provider, IOOptionBits options, bool * defer) { IOLockLock(client->reserved->uvars->uvarsLock); client->reserved->uvars->didTerminate = true; if (!client->reserved->uvars->serverDied && !client->reserved->uvars->stopped) { *defer = true; } IOLockUnlock(client->reserved->uvars->uvarsLock); } void IOUserServer::serviceDidStop(IOService * client, IOService * provider) { bool complete; OSArray * closeArray; complete = false; closeArray = NULL; IOLockLock(client->reserved->uvars->uvarsLock); if (client->reserved->uvars && client->reserved->uvars->willTerminate && !client->reserved->uvars->stopped) { client->reserved->uvars->stopped = true; complete = client->reserved->uvars->didTerminate; } if (client->reserved->uvars) { closeArray = client->reserved->uvars->openProviders; client->reserved->uvars->openProviders = NULL; } IOLockUnlock(client->reserved->uvars->uvarsLock); if (closeArray) { closeArray->iterateObjects(^bool (OSObject * obj) { IOService * toClose; toClose = OSDynamicCast(IOService, obj); if (toClose) { DKLOG(DKS ":force close (" DKS ")\n", DKN(client), DKN(toClose)); toClose->close(client); } return false; }); closeArray->release(); } if (complete) { bool defer = false; client->didTerminate(provider, 0, &defer); } } kern_return_t IOService::ClientCrashed_Impl( IOService * client, uint64_t options) { return kIOReturnUnsupported; } kern_return_t IOService::Stop_Impl( IOService * provider) { IOUserServer::serviceDidStop(this, provider); return kIOReturnSuccess; } void IOService::Stop_async_Impl( IOService * provider) { } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #undef super #define super IOUserClient OSDefineMetaClassAndStructors(IOUserUserClient, IOUserClient) /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ bool IOUserUserClient::init(OSDictionary * properties) { if (!super::init(properties)) { return false; } fWorkGroups = OSDictionary::withCapacity(0); if (fWorkGroups == NULL) { return false; } fEventLinks = OSDictionary::withCapacity(0); if (fEventLinks == NULL) { return false; } fLock = IOLockAlloc(); return true; } void IOUserUserClient::free() { OSSafeReleaseNULL(fWorkGroups); OSSafeReleaseNULL(fEventLinks); if (fLock) { IOLockFree(fLock); } super::free(); } IOReturn IOUserUserClient::setTask(task_t task) { task_reference(task); fTask = task; return kIOReturnSuccess; } void IOUserUserClient::stop(IOService * provider) { if (fTask) { task_deallocate(fTask); fTask = NULL; } super::stop(provider); } IOReturn IOUserUserClient::clientClose(void) { terminate(kIOServiceTerminateNeedWillTerminate); return kIOReturnSuccess; } IOReturn IOUserUserClient::setProperties(OSObject * properties) { IOReturn ret = kIOReturnUnsupported; return ret; } // p1 - name of object // p2 - length of object name // p3 - mach port name kern_return_t IOUserUserClient::eventlinkConfigurationTrap(void * p1, void * p2, void * p3, void * p4, void * p5, void * p6) { user_addr_t userObjectName = (user_addr_t)p1; mach_port_name_t portName = (mach_port_name_t)(uintptr_t)p3; mach_port_t port = MACH_PORT_NULL; ipc_kobject_type_t portType; char eventlinkName[kIOEventLinkMaxNameLength + 1] = {0}; size_t eventLinkNameLen; OSString * eventlinkNameStr = NULL; // must release IOEventLink * eventLink = NULL; // do not release kern_return_t ret; ret = copyinstr(userObjectName, &eventlinkName[0], sizeof(eventlinkName), &eventLinkNameLen); if (ret != kIOReturnSuccess) { goto finish; } // ensure string length matches trap argument if (eventLinkNameLen != (size_t)p2 + 1) { ret = kIOReturnBadArgument; goto finish; } eventlinkNameStr = OSString::withCStringNoCopy(eventlinkName); if (eventlinkNameStr == NULL) { ret = kIOReturnNoMemory; goto finish; } IOLockLock(fLock); eventLink = OSDynamicCast(IOEventLink, fEventLinks->getObject(eventlinkNameStr)); if (eventLink) { eventLink->retain(); } IOLockUnlock(fLock); if (eventLink == NULL) { ret = kIOReturnNotFound; goto finish; } port = iokit_lookup_raw_current_task(portName, &portType); if (port == NULL) { ret = kIOReturnNotFound; goto finish; } if (portType != IKOT_EVENTLINK) { ret = kIOReturnBadArgument; goto finish; } ret = eventLink->SetEventlinkPort(port); if (ret != kIOReturnSuccess) { if (kIODKLogSetup & gIODKDebug) { DKLOG(DKS " %s SetEventlinkPort() returned %x\n", DKN(this), eventlinkNameStr->getCStringNoCopy(), ret); } goto finish; } finish: if (port != NULL) { iokit_release_port_send(port); } OSSafeReleaseNULL(eventlinkNameStr); OSSafeReleaseNULL(eventLink); return ret; } kern_return_t IOUserUserClient::workgroupConfigurationTrap(void * p1, void * p2, void * p3, void * p4, void * p5, void * p6) { user_addr_t userObjectName = (user_addr_t)p1; mach_port_name_t portName = (mach_port_name_t)(uintptr_t)p3; mach_port_t port = MACH_PORT_NULL; ipc_kobject_type_t portType; char workgroupName[kIOWorkGroupMaxNameLength + 1] = {0}; size_t workgroupNameLen; OSString * workgroupNameStr = NULL; // must release IOWorkGroup * workgroup = NULL; // do not release kern_return_t ret; ret = copyinstr(userObjectName, &workgroupName[0], sizeof(workgroupName), &workgroupNameLen); if (ret != kIOReturnSuccess) { goto finish; } // ensure string length matches trap argument if (workgroupNameLen != (size_t)p2 + 1) { ret = kIOReturnBadArgument; goto finish; } workgroupNameStr = OSString::withCStringNoCopy(workgroupName); if (workgroupNameStr == NULL) { ret = kIOReturnNoMemory; goto finish; } IOLockLock(fLock); workgroup = OSDynamicCast(IOWorkGroup, fWorkGroups->getObject(workgroupNameStr)); if (workgroup) { workgroup->retain(); } IOLockUnlock(fLock); if (workgroup == NULL) { ret = kIOReturnNotFound; goto finish; } port = iokit_lookup_raw_current_task(portName, &portType); if (port == NULL) { ret = kIOReturnNotFound; goto finish; } if (portType != IKOT_WORK_INTERVAL) { ret = kIOReturnBadArgument; goto finish; } ret = workgroup->SetWorkGroupPort(port); if (ret != kIOReturnSuccess) { if (kIODKLogSetup & gIODKDebug) { DKLOG(DKS " %s SetWorkGroupPort() returned %x\n", DKN(this), workgroupNameStr->getCStringNoCopy(), ret); } goto finish; } finish: if (port != NULL) { iokit_release_port_send(port); } OSSafeReleaseNULL(workgroupNameStr); OSSafeReleaseNULL(workgroup); return ret; } IOExternalTrap * IOUserUserClient::getTargetAndTrapForIndex( IOService **targetP, UInt32 index ) { static const OSBoundedArray trapTemplate = {{ { NULL, (IOTrap) & IOUserUserClient::eventlinkConfigurationTrap}, { NULL, (IOTrap) & IOUserUserClient::workgroupConfigurationTrap}, }}; if (index >= trapTemplate.size()) { return NULL; } *targetP = this; return (IOExternalTrap *)&trapTemplate[index]; } kern_return_t IOUserClient::CopyClientEntitlements_Impl(OSDictionary ** entitlements) { return kIOReturnUnsupported; }; struct IOUserUserClientActionRef { OSAsyncReference64 asyncRef; }; void IOUserClient::KernelCompletion_Impl( OSAction * action, IOReturn status, const unsigned long long * asyncData, uint32_t asyncDataCount) { IOUserUserClientActionRef * ref; ref = (typeof(ref))action->GetReference(); IOUserClient::sendAsyncResult64(ref->asyncRef, status, (io_user_reference_t *) asyncData, asyncDataCount); } kern_return_t IOUserClient::_ExternalMethod_Impl( uint64_t selector, const unsigned long long * scalarInput, uint32_t scalarInputCount, OSData * structureInput, IOMemoryDescriptor * structureInputDescriptor, unsigned long long * scalarOutput, uint32_t * scalarOutputCount, uint64_t structureOutputMaximumSize, OSData ** structureOutput, IOMemoryDescriptor * structureOutputDescriptor, OSAction * completion) { return kIOReturnUnsupported; } IOReturn IOUserUserClient::clientMemoryForType(UInt32 type, IOOptionBits * koptions, IOMemoryDescriptor ** kmemory) { IOReturn kr; uint64_t options; IOMemoryDescriptor * memory; kr = CopyClientMemoryForType(type, &options, &memory); *koptions = 0; *kmemory = NULL; if (kIOReturnSuccess != kr) { return kr; } if (kIOUserClientMemoryReadOnly & options) { *koptions |= kIOMapReadOnly; } *kmemory = memory; return kr; } IOReturn IOUserUserClient::externalMethod(uint32_t selector, IOExternalMethodArguments * args, IOExternalMethodDispatch * dispatch, OSObject * target, void * reference) { IOReturn kr; OSData * structureInput; OSData * structureOutput; size_t copylen; uint64_t structureOutputSize; OSAction * action; IOUserUserClientActionRef * ref; mach_port_t wake_port = MACH_PORT_NULL; kr = kIOReturnUnsupported; structureInput = NULL; action = NULL; ref = NULL; if (args->structureInputSize) { structureInput = OSData::withBytesNoCopy((void *) args->structureInput, args->structureInputSize); } if (MACH_PORT_NULL != args->asyncWakePort) { // this retain is for the OSAction to release wake_port = ipc_port_make_send_mqueue(args->asyncWakePort); kr = CreateActionKernelCompletion(sizeof(IOUserUserClientActionRef), &action); assert(KERN_SUCCESS == kr); ref = (typeof(ref))action->GetReference(); bcopy(args->asyncReference, &ref->asyncRef[0], args->asyncReferenceCount * sizeof(ref->asyncRef[0])); kr = action->SetAbortedHandler(^(void) { IOUserUserClientActionRef * ref; IOReturn ret; ref = (typeof(ref))action->GetReference(); ret = releaseAsyncReference64(ref->asyncRef); assert(kIOReturnSuccess == ret); bzero(&ref->asyncRef[0], sizeof(ref->asyncRef)); }); assert(KERN_SUCCESS == kr); } if (args->structureVariableOutputData) { structureOutputSize = kIOUserClientVariableStructureSize; } else if (args->structureOutputDescriptor) { structureOutputSize = args->structureOutputDescriptor->getLength(); } else { structureOutputSize = args->structureOutputSize; } kr = _ExternalMethod(selector, &args->scalarInput[0], args->scalarInputCount, structureInput, args->structureInputDescriptor, args->scalarOutput, &args->scalarOutputCount, structureOutputSize, &structureOutput, args->structureOutputDescriptor, action); OSSafeReleaseNULL(structureInput); OSSafeReleaseNULL(action); if (kr == kIOReturnSuccess && structureOutput) { if (args->structureVariableOutputData) { *args->structureVariableOutputData = structureOutput; } else { copylen = structureOutput->getLength(); if (copylen > args->structureOutputSize) { kr = kIOReturnBadArgument; } else { bcopy((const void *) structureOutput->getBytesNoCopy(), args->structureOutput, copylen); args->structureOutputSize = (uint32_t) copylen; } OSSafeReleaseNULL(structureOutput); } } if (kIOReturnSuccess != kr) { // mig will destroy any async port return kr; } // We must never return error after this point in order to preserve MIG ownership semantics assert(kr == kIOReturnSuccess); if (MACH_PORT_NULL != wake_port) { // this release is for the mig created send right iokit_release_port_send(wake_port); } return kr; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ extern IORecursiveLock * gDriverKitLaunchLock; extern OSSet * gDriverKitLaunches; _IOUserServerCheckInCancellationHandler * IOUserServerCheckInToken::setCancellationHandler(IOUserServerCheckInCancellationHandler handler, void* handlerArgs) { _IOUserServerCheckInCancellationHandler * handlerObj = _IOUserServerCheckInCancellationHandler::withHandler(handler, handlerArgs); if (!handlerObj) { goto finish; } IORecursiveLockLock(gDriverKitLaunchLock); if (fState == kIOUserServerCheckInCanceled) { // Send cancel notification if we set the handler after this was canceled handlerObj->call(this); } else if (fState == kIOUserServerCheckInPending) { fHandlers->setObject(handlerObj); } IORecursiveLockUnlock(gDriverKitLaunchLock); finish: return handlerObj; } void IOUserServerCheckInToken::removeCancellationHandler(_IOUserServerCheckInCancellationHandler * handler) { IORecursiveLockLock(gDriverKitLaunchLock); fHandlers->removeObject(handler); IORecursiveLockUnlock(gDriverKitLaunchLock); } void IOUserServerCheckInToken::cancel() { IORecursiveLockLock(gDriverKitLaunchLock); if (fState == kIOUserServerCheckInPending) { fState = kIOUserServerCheckInCanceled; if (gDriverKitLaunches != NULL) { // Remove pending launch from list, if we have not shut down yet. gDriverKitLaunches->removeObject(this); } fHandlers->iterateObjects(^bool (OSObject * obj){ _IOUserServerCheckInCancellationHandler * handlerObj = OSDynamicCast(_IOUserServerCheckInCancellationHandler, obj); if (handlerObj) { handlerObj->call(this); } return false; }); fHandlers->flushCollection(); } IORecursiveLockUnlock(gDriverKitLaunchLock); } IOReturn IOUserServerCheckInToken::complete() { IOReturn ret; IORecursiveLockLock(gDriverKitLaunchLock); if (fState == kIOUserServerCheckInCanceled) { ret = kIOReturnError; } else { ret = kIOReturnSuccess; } if (fState == kIOUserServerCheckInPending && --fPendingCount == 0) { fState = kIOUserServerCheckInComplete; if (gDriverKitLaunches != NULL) { // Remove pending launch from list, if we have not shut down yet. gDriverKitLaunches->removeObject(this); } // No need to hold on to the cancellation handlers fHandlers->flushCollection(); } IORecursiveLockUnlock(gDriverKitLaunchLock); return ret; } bool IOUserServerCheckInToken::init(const OSSymbol * serverName, OSNumber * serverTag, OSKext *driverKext, OSData *serverDUI) { if (!OSObject::init()) { return false; } if (!serverName) { return false; } fServerName = serverName; fServerName->retain(); if (!serverTag) { return false; } fServerTag = serverTag; fServerTag->retain(); fHandlers = OSSet::withCapacity(0); if (!fHandlers) { return false; } fState = kIOUserServerCheckInPending; fPendingCount = 1; fKextBundleID = NULL; fNeedDextDec = false; fExecutableName = NULL; if (driverKext) { fExecutableName = OSDynamicCast(OSSymbol, driverKext->getBundleExecutable()); if (fExecutableName) { fExecutableName->retain(); } /* * We need to keep track of how many dexts we have started. * For every new dext we are going to create a new token, and * we consider the token creation as the initial step to * create a dext as it is the data structure that will back up * the userspace dance to start a dext. * We later have to decrement only once per token. * If no error occurs we consider the finalize() call on IOUserServer * as the moment in which we do not consider the dext "alive" anymore; * however in case of errors we will still need to decrement the count * otherwise upgrades of the dext will never make progress. */ if (OSKext::incrementDextLaunchCount(driverKext, serverDUI)) { /* * If fKext holds a pointer, * it is the indication that a decrements needs * to be called. */ fNeedDextDec = true; fKextBundleID = OSDynamicCast(OSString, driverKext->getIdentifier()); fKextBundleID->retain(); } else { return false; } } return true; } /* * Returns if the dext can be re-used * for matching. */ bool IOUserServerCheckInToken::dextTerminate(void) { bool ret = true; if (fNeedDextDec == true) { /* * We can decrement DextLaunchCount only * once per token. */ ret = !(OSKext::decrementDextLaunchCount(fKextBundleID)); fNeedDextDec = false; } return ret; } void IOUserServerCheckInToken::free() { OSSafeReleaseNULL(fServerName); OSSafeReleaseNULL(fServerTag); OSSafeReleaseNULL(fExecutableName); OSSafeReleaseNULL(fHandlers); if (fKextBundleID != NULL) { dextTerminate(); OSSafeReleaseNULL(fKextBundleID); } OSObject::free(); } const OSSymbol * IOUserServerCheckInToken::copyServerName() const { fServerName->retain(); return fServerName; } OSNumber * IOUserServerCheckInToken::copyServerTag() const { fServerTag->retain(); return fServerTag; } IOUserServer * IOUserServer::launchUserServer(OSString * bundleID, const OSSymbol * serverName, OSNumber * serverTag, bool reuseIfExists, IOUserServerCheckInToken ** resultToken, OSData *serverDUI) { IOUserServer *me = NULL; IOUserServerCheckInToken * token = NULL; OSDictionary * matching = NULL; // must release OSKext * driverKext = NULL; // must release OSDextStatistics * driverStatistics = NULL; // must release bool reslide = false; /* TODO: Check we are looking for same dextID * and if it is not the same * restart the matching process. */ driverKext = OSKext::lookupDextWithIdentifier(bundleID, serverDUI); if (driverKext != NULL) { driverStatistics = driverKext->copyDextStatistics(); if (driverStatistics == NULL) { panic("Kext %s was not a DriverKit OSKext", bundleID->getCStringNoCopy()); } IOLog("Driver %s has crashed %zu time(s)\n", bundleID->getCStringNoCopy(), driverStatistics->getCrashCount()); reslide = driverStatistics->getCrashCount() > 0; } else { DKLOG("Could not find OSKext for %s\n", bundleID->getCStringNoCopy()); *resultToken = NULL; return NULL; } IORecursiveLockLock(gDriverKitLaunchLock); if (gDriverKitLaunches == NULL) { // About to shut down, don't launch anything goto finish; } if (reuseIfExists) { const char * serverNameCStr; const char * bundleIDCStr; const char * endOrgCStr; serverNameCStr = serverName->getCStringNoCopy(); bundleIDCStr = bundleID->getCStringNoCopy(); (endOrgCStr = strchr(bundleIDCStr, '.')) && (endOrgCStr = strchr(endOrgCStr + 1, '.')); reuseIfExists = endOrgCStr && (0 == strncmp(bundleIDCStr, serverNameCStr, endOrgCStr + 1 - bundleIDCStr)); if (!reuseIfExists) { IOLog(kIOUserServerNameKey " \"%s\" not correct organization for bundleID \"%s\"\n", serverNameCStr, bundleIDCStr); } } // Find existing server if (reuseIfExists) { token = IOUserServerCheckInToken::findExistingToken(serverName); if (token) { // Launch in progress, return token goto finish; } else { // Check if launch completed matching = IOService::serviceMatching(gIOUserServerClassKey); if (!matching) { goto finish; } IOService::propertyMatching(gIOUserServerNameKey, serverName, matching); IOService * service = IOService::copyMatchingService(matching); IOUserServer * userServer = OSDynamicCast(IOUserServer, service); if (userServer) { // found existing user server me = userServer; goto finish; } else { OSSafeReleaseNULL(service); } } } // No existing server, request launch token = new IOUserServerCheckInToken; if (!token) { goto finish; } /* * TODO: If the init fails because the personalities are not up to date * restart the whole matching process. */ if (token && !token->init(serverName, serverTag, driverKext, serverDUI)) { IOLog("Could not initialize token\n"); OSSafeReleaseNULL(token); goto finish; } /* * If the launch fails at any point terminate() will * be called on this IOUserServer. */ gDriverKitLaunches->setObject(token); OSKext::requestDaemonLaunch(bundleID, (OSString *)serverName, serverTag, reslide ? kOSBooleanTrue : kOSBooleanFalse, token, serverDUI); finish: IORecursiveLockUnlock(gDriverKitLaunchLock); OSSafeReleaseNULL(matching); OSSafeReleaseNULL(driverStatistics); OSSafeReleaseNULL(driverKext); if (resultToken) { *resultToken = token; } else { OSSafeReleaseNULL(token); } return me; } /* * IOUserServerCheckInTokens are used to track dext launches. They have three possible states: * * - Pending: A dext launch is pending * - Canceled: Dext launch failed * - Complete: Dext launch is complete * * A token can be shared among multiple IOServices that are waiting for dexts if the IOUserServerName * is the same. This allows dexts to be reused and host multiple services. All pending tokens are stored * in gDriverKitLaunches and we check here before creating a new token when launching a dext. * * A token starts in the pending state with a pending count of 1. When we reuse a token, we increase the * pending count of the token. * * The token is sent to userspace as a mach port through kernelmanagerd/driverkitd to the dext. The dext then * uses that token to check in to the kernel. If any part of the dext launch failed (dext crashed, kmd crashed, etc.) * we get a no-senders notification for the token in the kernel and the token goes into the Canceled state. * * Once the dext checks in to the kernel, we decrement the pending count for the token. When the pending count reaches * 0, the token goes into the Complete state. So if the token is in the Complete state, there are no kernel matching threads * waiting on the dext to check in. */ IOUserServerCheckInToken * IOUserServerCheckInToken::findExistingToken(const OSSymbol * serverName) { IOUserServerCheckInToken * __block result = NULL; IORecursiveLockLock(gDriverKitLaunchLock); if (gDriverKitLaunches == NULL) { goto finish; } gDriverKitLaunches->iterateObjects(^(OSObject * obj) { IOUserServerCheckInToken * token = OSDynamicCast(IOUserServerCheckInToken, obj); if (token) { // Check if server name matches const OSSymbol * tokenServerName = token->fServerName; if (tokenServerName->isEqualTo(serverName)) { assert(token->fState == kIOUserServerCheckInPending); token->fPendingCount++; result = token; result->retain(); } } return result != NULL; }); finish: IORecursiveLockUnlock(gDriverKitLaunchLock); return result; } void IOUserServerCheckInToken::cancelAll() { OSSet * tokensToCancel; IORecursiveLockLock(gDriverKitLaunchLock); tokensToCancel = gDriverKitLaunches; gDriverKitLaunches = NULL; tokensToCancel->iterateObjects(^(OSObject *obj) { IOUserServerCheckInToken * token = OSDynamicCast(IOUserServerCheckInToken, obj); if (token) { token->cancel(); } return false; }); IORecursiveLockUnlock(gDriverKitLaunchLock); OSSafeReleaseNULL(tokensToCancel); } void _IOUserServerCheckInCancellationHandler::call(IOUserServerCheckInToken * token) { fHandler(token, fHandlerArgs); } _IOUserServerCheckInCancellationHandler * _IOUserServerCheckInCancellationHandler::withHandler(IOUserServerCheckInCancellationHandler handler, void * args) { _IOUserServerCheckInCancellationHandler * handlerObj = NULL; if (!handler) { goto finish; } handlerObj = new _IOUserServerCheckInCancellationHandler; if (!handlerObj) { goto finish; } handlerObj->fHandler = handler; handlerObj->fHandlerArgs = args; finish: return handlerObj; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct IOServiceStateNotificationDispatchSource_IVars { IOLock * fLock; IOService * fStateNotification; IOStateNotificationListenerRef fListener; OSAction * fAction; bool fEnable; bool fArmed; }; kern_return_t IOServiceStateNotificationDispatchSource::Create_Impl(IOService * service, OSArray * items, IODispatchQueue * queue, IOServiceStateNotificationDispatchSource ** outSource) { kern_return_t kr; IOServiceStateNotificationDispatchSource * source; source = OSTypeAlloc(IOServiceStateNotificationDispatchSource); source->init(); source->ivars->fStateNotification = service; kr = service->stateNotificationListenerAdd(items, &source->ivars->fListener, ^kern_return_t () { OSAction * action; action = NULL; IOLockLock(source->ivars->fLock); if (source->ivars->fArmed && source->ivars->fAction) { source->ivars->fArmed = false; action = source->ivars->fAction; action->retain(); } IOLockUnlock(source->ivars->fLock); if (action) { source->StateNotificationReady(action); OSSafeReleaseNULL(action); } return kIOReturnSuccess; }); if (kIOReturnSuccess != kr) { OSSafeReleaseNULL(source); } *outSource = source; return kr; } bool IOServiceStateNotificationDispatchSource::init() { if (!IODispatchSource::init()) { return false; } ivars = IOMallocType(IOServiceStateNotificationDispatchSource_IVars); if (!ivars) { return false; } ivars->fLock = IOLockAlloc(); if (!ivars->fLock) { return false; } ivars->fArmed = true; return true; } void IOServiceStateNotificationDispatchSource::free() { if (ivars) { if (ivars->fListener) { ivars->fStateNotification->stateNotificationListenerRemove(ivars->fListener); } if (ivars->fLock) { IOLockFree(ivars->fLock); } IOFreeType(ivars, IOServiceStateNotificationDispatchSource_IVars); } IODispatchSource::free(); } kern_return_t IOServiceStateNotificationDispatchSource::SetHandler_Impl(OSAction * action) { IOReturn ret; bool notifyReady; notifyReady = false; IOLockLock(ivars->fLock); action->retain(); OSSafeReleaseNULL(ivars->fAction); ivars->fAction = action; if (action) { notifyReady = true; } IOLockUnlock(ivars->fLock); if (notifyReady) { StateNotificationReady(action); } ret = kIOReturnSuccess; return ret; } kern_return_t IOServiceStateNotificationDispatchSource::SetEnableWithCompletion_Impl( bool enable, IODispatchSourceCancelHandler handler) { if (enable == ivars->fEnable) { return kIOReturnSuccess; } IOLockLock(ivars->fLock); ivars->fEnable = enable; IOLockUnlock(ivars->fLock); return kIOReturnSuccess; } kern_return_t IOServiceStateNotificationDispatchSource::Cancel_Impl( IODispatchSourceCancelHandler handler) { return kIOReturnUnsupported; } kern_return_t IOServiceStateNotificationDispatchSource::StateNotificationBegin_Impl(void) { IOLockLock(ivars->fLock); ivars->fArmed = true; IOLockUnlock(ivars->fLock); return kIOReturnSuccess; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include OSDefineMetaClassAndStructors(IOServiceStateNotificationEventSource, IOEventSource) OSMetaClassDefineReservedUnused(IOServiceStateNotificationEventSource, 0); OSMetaClassDefineReservedUnused(IOServiceStateNotificationEventSource, 1); OSMetaClassDefineReservedUnused(IOServiceStateNotificationEventSource, 2); OSMetaClassDefineReservedUnused(IOServiceStateNotificationEventSource, 3); OSMetaClassDefineReservedUnused(IOServiceStateNotificationEventSource, 4); OSMetaClassDefineReservedUnused(IOServiceStateNotificationEventSource, 5); OSMetaClassDefineReservedUnused(IOServiceStateNotificationEventSource, 6); OSMetaClassDefineReservedUnused(IOServiceStateNotificationEventSource, 7); OSPtr IOServiceStateNotificationEventSource::serviceStateNotificationEventSource(IOService *service, OSArray * items, ActionBlock inAction) { kern_return_t kr; IOServiceStateNotificationEventSource * source; source = OSTypeAlloc(IOServiceStateNotificationEventSource); if (source && !source->init(service, NULL)) { OSSafeReleaseNULL(source); } if (!source) { return nullptr; } source->fStateNotification = service; kr = service->stateNotificationListenerAdd(items, &source->fListener, ^kern_return_t () { if (!source->workLoop) { return kIOReturnSuccess; } source->workLoop->runActionBlock(^IOReturn (void) { source->fArmed = true; return kIOReturnSuccess; }); source->signalWorkAvailable(); return kIOReturnSuccess; }); if (kIOReturnSuccess != kr) { OSSafeReleaseNULL(source); } if (source) { source->setActionBlock((IOEventSource::ActionBlock) inAction); } return source; } void IOServiceStateNotificationEventSource::free() { if (fListener) { fStateNotification->stateNotificationListenerRemove(fListener); } IOEventSource::free(); } void IOServiceStateNotificationEventSource::enable() { fEnable = true; } void IOServiceStateNotificationEventSource::disable() { fEnable = false; } void IOServiceStateNotificationEventSource::setWorkLoop(IOWorkLoop *inWorkLoop) { IOEventSource::setWorkLoop(inWorkLoop); } bool IOServiceStateNotificationEventSource::checkForWork() { ActionBlock intActionBlock = (ActionBlock) actionBlock; if (fArmed) { fArmed = false; (intActionBlock)(); } return false; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ OSDefineMetaClassAndStructors(IOSystemStateNotification, IOService); class IOStateNotificationItem : public OSObject { OSDeclareDefaultStructors(IOStateNotificationItem); public: virtual bool init() override; OSDictionary * fSchema; OSDictionary * fValue; OSSet * fListeners; }; OSDefineMetaClassAndStructors(IOStateNotificationItem, OSObject); class IOStateNotificationListener : public OSObject { OSDeclareDefaultStructors(IOStateNotificationListener); public: virtual bool init() override; virtual void free() override; IOStateNotificationHandler fHandler; }; OSDefineMetaClassAndStructors(IOStateNotificationListener, OSObject); bool IOStateNotificationItem::init() { return OSObject::init(); } bool IOStateNotificationListener::init() { return OSObject::init(); } void IOStateNotificationListener::free() { if (fHandler) { Block_release(fHandler); } OSObject::free(); } struct IOServiceStateChangeVars { IOLock * fLock; OSDictionary * fItems; }; IOService * IOSystemStateNotification::initialize(void) { IOSystemStateNotification * me; IOServiceStateChangeVars * vars; me = OSTypeAlloc(IOSystemStateNotification); me->init(); vars = IOMallocType(IOServiceStateChangeVars); me->reserved->svars = vars; vars->fLock = IOLockAlloc(); vars->fItems = OSDictionary::withCapacity(16); { kern_return_t ret; gIOSystemStateSleepDescriptionKey = (OSString *)OSSymbol::withCStringNoCopy(kIOSystemStateSleepDescriptionKey); gIOSystemStateSleepDescriptionHibernateStateKey = OSSymbol::withCStringNoCopy(kIOSystemStateSleepDescriptionHibernateStateKey); gIOSystemStateSleepDescriptionReasonKey = OSSymbol::withCStringNoCopy(kIOSystemStateSleepDescriptionReasonKey); ret = me->StateNotificationItemCreate(gIOSystemStateSleepDescriptionKey, NULL); assert(kIOReturnSuccess == ret); gIOSystemStateWakeDescriptionKey = (OSString *)OSSymbol::withCStringNoCopy(kIOSystemStateWakeDescriptionKey); gIOSystemStateWakeDescriptionWakeReasonKey = OSSymbol::withCStringNoCopy(kIOSystemStateWakeDescriptionWakeReasonKey); gIOSystemStateWakeDescriptionContinuousTimeOffsetKey = OSSymbol::withCStringNoCopy(kIOSystemStateWakeDescriptionContinuousTimeOffsetKey); ret = me->StateNotificationItemCreate(gIOSystemStateWakeDescriptionKey, NULL); assert(kIOReturnSuccess == ret); gIOSystemStateHaltDescriptionKey = (OSString *)OSSymbol::withCStringNoCopy(kIOSystemStateHaltDescriptionKey); gIOSystemStateHaltDescriptionHaltStateKey = OSSymbol::withCStringNoCopy(kIOSystemStateHaltDescriptionHaltStateKey); ret = me->StateNotificationItemCreate(gIOSystemStateHaltDescriptionKey, NULL); assert(kIOReturnSuccess == ret); gIOSystemStatePowerSourceDescriptionKey = (OSString *)OSSymbol::withCStringNoCopy(kIOSystemStatePowerSourceDescriptionKey); gIOSystemStatePowerSourceDescriptionACAttachedKey = OSSymbol::withCStringNoCopy(kIOSystemStatePowerSourceDescriptionACAttachedKey); ret = me->StateNotificationItemCreate(gIOSystemStatePowerSourceDescriptionKey, NULL); assert(kIOReturnSuccess == ret); } return me; } bool IOSystemStateNotification::serializeProperties(OSSerialize * s) const { IOServiceStateChangeVars * ivars = reserved->svars; bool ok; OSDictionary * result; result = OSDictionary::withCapacity(16); IOLockLock(ivars->fLock); ivars->fItems->iterateObjects(^bool (const OSSymbol * key, OSObject * object) { IOStateNotificationItem * item; item = (typeof(item))object; if (!item->fValue) { return false; } result->setObject(key, item->fValue); return false; }); IOLockUnlock(ivars->fLock); ok = result->serialize(s); OSSafeReleaseNULL(result); return ok; } kern_return_t IOSystemStateNotification::setProperties(OSObject * properties) { kern_return_t kr; OSDictionary * dict; OSDictionary * schema; OSDictionary * value; OSString * itemName; dict = OSDynamicCast(OSDictionary, properties); if (!dict) { return kIOReturnBadArgument; } if (!IOCurrentTaskHasEntitlement(kIOSystemStateEntitlement)) { return kIOReturnNotPermitted; } if ((schema = OSDynamicCast(OSDictionary, dict->getObject(kIOStateNotificationItemCreateKey)))) { itemName = OSDynamicCast(OSString, schema->getObject(kIOStateNotificationNameKey)); kr = StateNotificationItemCreate(itemName, schema); } else if ((value = OSDynamicCast(OSDictionary, dict->getObject(kIOStateNotificationItemSetKey)))) { itemName = OSDynamicCast(OSString, value->getObject(kIOStateNotificationNameKey)); itemName->retain(); value->removeObject(kIOStateNotificationNameKey); kr = StateNotificationItemSet(itemName, value); itemName->release(); } else { kr = kIOReturnError; } return kr; } kern_return_t IOService::CopySystemStateNotificationService_Impl(IOService ** outService) { IOService * service; service = getSystemStateNotificationService(); service->retain(); *outService = service; return kIOReturnSuccess; } IOStateNotificationItem * IOService::stateNotificationItemCopy(OSString * itemName, OSDictionary * schema) { IOServiceStateChangeVars * ivars = reserved->svars; const OSSymbol * name; IOStateNotificationItem * item; name = OSSymbol::withString(itemName); IOLockLock(ivars->fLock); if ((item = (typeof(item))ivars->fItems->getObject(name))) { item->retain(); } else { item = OSTypeAlloc(IOStateNotificationItem); item->init(); item->fListeners = OSSet::withCapacity(16); if (schema) { schema->retain(); } else { schema = OSDictionary::withCapacity(8); } schema->setObject(kIOStateNotificationNameKey, name); item->fSchema = schema; ivars->fItems->setObject(name, item); } IOLockUnlock(ivars->fLock); OSSafeReleaseNULL(name); return item; } kern_return_t IOService::StateNotificationItemCreate_Impl(OSString * itemName, OSDictionary * schema) { IOStateNotificationItem * item; item = stateNotificationItemCopy(itemName, schema); if (!item) { return kIOReturnNoMemory; } item->release(); return kIOReturnSuccess; } kern_return_t IOService::StateNotificationItemSet_Impl(OSString * itemName, OSDictionary * value) { IOServiceStateChangeVars * ivars = reserved->svars; OSSet * listeners; IOStateNotificationItem * item; value->retain(); IOLockLock(ivars->fLock); item = (typeof(item))ivars->fItems->getObject(itemName); OSSafeReleaseNULL(item->fValue); item->fValue = value; listeners = NULL; if (item->fListeners->getCount()) { listeners = OSSet::withSet(item->fListeners); } IOLockUnlock(ivars->fLock); if (listeners) { listeners->iterateObjects(^bool (OSObject * object) { IOStateNotificationListener * listener; listener = (typeof(listener))object; listener->fHandler(); return false; }); OSSafeReleaseNULL(listeners); } return kIOReturnSuccess; } kern_return_t IOService::StateNotificationItemCopy_Impl(OSString * itemName, OSDictionary ** outValue) { IOServiceStateChangeVars * ivars = reserved->svars; kern_return_t ret; IOStateNotificationItem * item; OSDictionary * value; IOLockLock(ivars->fLock); item = (typeof(item))ivars->fItems->getObject(itemName); if (item) { value = item->fValue; } else { value = NULL; } if (!value) { ret = kIOReturnNotFound; } else { value->retain(); ret = kIOReturnSuccess; } IOLockUnlock(ivars->fLock); *outValue = value; return ret; } kern_return_t IOService::stateNotificationListenerAdd(OSArray * items, IOStateNotificationListenerRef * outRef, IOStateNotificationHandler handler) { IOServiceStateChangeVars * ivars = reserved->svars; kern_return_t kr __block; IOStateNotificationListener * listener; listener = OSTypeAlloc(IOStateNotificationListener); listener->init(); listener->fHandler = Block_copy(handler); kr = kIOReturnSuccess; items->iterateObjects(^bool (OSObject * object) { OSString * itemName; IOStateNotificationItem * item; itemName = OSDynamicCast(OSString, object); if (!itemName) { kr = kIOReturnBadArgument; return true; } item = stateNotificationItemCopy(itemName, NULL); if (!item) { kr = kIOReturnNoMemory; return true; } IOLockLock(ivars->fLock); item->fListeners->setObject(listener); IOLockUnlock(ivars->fLock); item->release(); return false; }); if (kIOReturnSuccess != kr) { stateNotificationListenerRemove(listener); OSSafeReleaseNULL(listener); } *outRef = listener; return kr; } kern_return_t IOService::stateNotificationListenerRemove(IOStateNotificationListenerRef ref) { IOServiceStateChangeVars * ivars = reserved->svars; IOStateNotificationListener * listener; kern_return_t kr; kr = kIOReturnSuccess; listener = (typeof(listener))ref; IOLockLock(ivars->fLock); ivars->fItems->iterateObjects(^bool (const OSSymbol * key, OSObject * object) { IOStateNotificationItem * item; item = (typeof(item))object; item->fListeners->removeObject(listener); return false; }); IOLockUnlock(ivars->fLock); return kr; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ kern_return_t IOWorkGroup::Create_Impl(OSString * name, IOUserClient * userClient, IOWorkGroup ** workgroup) { IOWorkGroup * inst = NULL; IOUserUserClient * uc = NULL; kern_return_t ret = kIOReturnError; IOUserServer * us; if (name == NULL) { ret = kIOReturnBadArgument; goto finish; } if (name->getLength() > kIOWorkGroupMaxNameLength) { ret = kIOReturnBadArgument; goto finish; } uc = OSDynamicCast(IOUserUserClient, userClient); if (uc == NULL) { ret = kIOReturnBadArgument; goto finish; } inst = OSTypeAlloc(IOWorkGroup); if (!inst->init()) { inst->free(); inst = NULL; ret = kIOReturnNoMemory; goto finish; } us = (typeof(us))thread_iokit_tls_get(0); inst->ivars->userServer = OSDynamicCast(IOUserServer, us); if (inst->ivars->userServer == NULL) { ret = kIOReturnBadArgument; goto finish; } inst->ivars->userServer->retain(); inst->ivars->name = name; inst->ivars->name->retain(); inst->ivars->userClient = uc; // no retain IOLockLock(uc->fLock); uc->fWorkGroups->setObject(name, inst); IOLockUnlock(uc->fLock); ret = kIOReturnSuccess; finish: if (ret != kIOReturnSuccess) { OSSafeReleaseNULL(inst); } else { *workgroup = inst; } return ret; } kern_return_t IOWorkGroup::InvalidateKernel_Impl(IOUserClient * client) { IOUserUserClient * uc = OSDynamicCast(IOUserUserClient, client); if (uc == NULL) { return kIOReturnBadArgument; } if (uc != ivars->userClient) { return kIOReturnBadArgument; } IOLockLock(uc->fLock); uc->fWorkGroups->removeObject(ivars->name); IOLockUnlock(uc->fLock); return kIOReturnSuccess; } kern_return_t IOWorkGroup::SetWorkGroupPort_Impl(mach_port_t port) { return kIOReturnUnsupported; } bool IOWorkGroup::init() { if (!OSObject::init()) { return false; } ivars = IOMallocType(IOWorkGroup_IVars); return true; } void IOWorkGroup::free() { if (ivars) { OSSafeReleaseNULL(ivars->userServer); OSSafeReleaseNULL(ivars->name); IOFreeType(ivars, IOWorkGroup_IVars); } OSObject::free(); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ kern_return_t IOEventLink::Create_Impl(OSString * name, IOUserClient * userClient, IOEventLink ** eventlink) { IOEventLink * inst = NULL; IOUserUserClient * uc = NULL; IOUserServer * us; kern_return_t ret = kIOReturnError; if (name == NULL) { ret = kIOReturnBadArgument; goto finish; } if (name->getLength() > kIOEventLinkMaxNameLength) { ret = kIOReturnBadArgument; goto finish; } uc = OSDynamicCast(IOUserUserClient, userClient); if (uc == NULL) { ret = kIOReturnBadArgument; goto finish; } inst = OSTypeAlloc(IOEventLink); if (!inst->init()) { inst->free(); inst = NULL; ret = kIOReturnNoMemory; goto finish; } us = (typeof(us))thread_iokit_tls_get(0); inst->ivars->userServer = OSDynamicCast(IOUserServer, us); if (inst->ivars->userServer == NULL) { ret = kIOReturnBadArgument; goto finish; } inst->ivars->userServer->retain(); inst->ivars->name = name; inst->ivars->name->retain(); inst->ivars->userClient = uc; // no retain IOLockLock(uc->fLock); uc->fEventLinks->setObject(name, inst); IOLockUnlock(uc->fLock); ret = kIOReturnSuccess; finish: if (ret != kIOReturnSuccess) { OSSafeReleaseNULL(inst); } else { *eventlink = inst; } return ret; } kern_return_t IOEventLink::InvalidateKernel_Impl(IOUserClient * client) { IOUserUserClient * uc = OSDynamicCast(IOUserUserClient, client); if (uc == NULL) { return kIOReturnBadArgument; } if (uc != ivars->userClient) { return kIOReturnBadArgument; } IOLockLock(uc->fLock); uc->fEventLinks->removeObject(ivars->name); IOLockUnlock(uc->fLock); return kIOReturnSuccess; } bool IOEventLink::init() { if (!OSObject::init()) { return false; } ivars = IOMallocType(IOEventLink_IVars); return true; } void IOEventLink::free() { if (ivars) { OSSafeReleaseNULL(ivars->userServer); OSSafeReleaseNULL(ivars->name); IOFreeType(ivars, IOEventLink_IVars); } OSObject::free(); } kern_return_t IOEventLink::SetEventlinkPort_Impl(mach_port_t port __unused) { return kIOReturnUnsupported; }