1// Copyright (c) 2019-2020 Apple Inc. 2 3#include <darwintest.h> 4#include <sys/sysctl.h> 5 6#include <Foundation/Foundation.h> 7#include <IOKit/IOCFSerialize.h> 8#include <IOKit/IOKitLib.h> 9 10T_GLOBAL_META(T_META_NAMESPACE("xnu.iokit"), 11 T_META_RUN_CONCURRENTLY(true)); 12 13static kern_return_t 14build_ioregistry_by_catalog_send_data(const char *match_name, 15 const char *userclient_name, const char *service_name) 16{ 17 NSArray *rootCatalogueArray = @[@{ 18 @kIOProviderClassKey: @kIOResourcesClass, 19 @kIOClassKey: (NSString * __nonnull)@(service_name), 20 @kIOUserClientClassKey: (NSString * __nonnull)@(userclient_name), 21 @kIOMatchCategoryKey: (NSString * __nonnull)@(match_name) 22 }]; 23 24 CFDataRef cfData = IOCFSerialize((__bridge CFTypeRef)rootCatalogueArray, 25 kIOCFSerializeToBinary); 26 T_QUIET; T_ASSERT_NOTNULL(cfData, "IOCFSerialize root catalogue array"); 27 kern_return_t kret = IOCatalogueSendData(MACH_PORT_NULL, 1, 28 (const char *)CFDataGetBytePtr(cfData), 29 (uint32_t)CFDataGetLength(cfData)); 30 CFRelease(cfData); 31 return kret; 32} 33 34static bool 35test_open_ioregistry(const char *match_name, const char *service_name, 36 bool exploit) 37{ 38 kern_return_t kret; 39 bool ioreg_found = false; 40 CFStringRef cfstrMatchName = NULL; 41 io_connect_t conn = IO_OBJECT_NULL; 42 io_iterator_t iter = IO_OBJECT_NULL, obj = IO_OBJECT_NULL; 43 CFMutableDictionaryRef service_info = NULL, properties = NULL; 44 45 service_info = IOServiceMatching(service_name); 46 kret = IOServiceGetMatchingServices(kIOMasterPortDefault, service_info, &iter); 47 T_QUIET; T_ASSERT_MACH_SUCCESS(kret, "IOServiceGetMatchingServices"); 48 cfstrMatchName = CFStringCreateWithCString(kCFAllocatorDefault, 49 match_name, kCFStringEncodingUTF8); 50 T_QUIET; T_ASSERT_NOTNULL(cfstrMatchName, 51 "created CFString from match name"); 52 53 while ((obj = IOIteratorNext(iter)) != 0) { 54 kret = IORegistryEntryCreateCFProperties(obj, &properties, 55 kCFAllocatorDefault, kNilOptions); 56 if (kret != KERN_SUCCESS) { 57 T_LOG("IORegistryEntryCreateCFProperties fails, 0x%08X", 58 (uint32_t)kret); 59 IOObjectRelease(obj); 60 continue; 61 } 62 63 CFStringRef value = CFDictionaryGetValue(properties, CFSTR("IOMatchCategory")); 64 if (value && CFGetTypeID(value) == CFStringGetTypeID() && 65 CFEqual(value, cfstrMatchName)) { 66 ioreg_found = true; 67 } else { 68 IOObjectRelease(obj); 69 continue; 70 } 71 72 if (!exploit) { 73 IOObjectRelease(obj); 74 break; 75 } 76 77 T_LOG("try to exploit by opening service, possibly panic..."); 78 IOServiceOpen(obj, mach_task_self(), 0, &conn); 79 IOObjectRelease(obj); 80 81 break; 82 } 83 84 CFRelease(cfstrMatchName); 85 86 if (properties) { 87 CFRelease(properties); 88 } 89 90 if (iter != IO_OBJECT_NULL) { 91 IOObjectRelease(iter); 92 } 93 94 if (conn != IO_OBJECT_NULL) { 95 IOServiceClose(conn); 96 } 97 98 return ioreg_found; 99} 100 101T_DECL(io_catalog_send_data_test, 102 "build an IORegistry entry with mismatching IOService and " 103 "IOUserClientClass by IOCatalogueSendData to check for DoS in " 104 "IOCatalogueSendData", T_META_TAG_VM_PREFERRED) 105{ 106 kern_return_t kret = build_ioregistry_by_catalog_send_data("fooBar", 107 "IOSurfaceRootUserClient", "IOReportHub"); 108#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) 109 int development = 0; 110 size_t development_size = sizeof(development); 111 112 T_ASSERT_POSIX_SUCCESS(sysctlbyname("kern.development", &development, 113 &development_size, NULL, 0), "sysctl kern.development"); 114 115 if (development) { 116 T_EXPECT_MACH_SUCCESS(kret, "IOCatalogueSendData should " 117 "return success with development kernel"); 118 } else { 119 /* this trick to build an entry by io_catalog_send_data should fail */ 120 T_EXPECT_EQ(kret, kIOReturnNotPrivileged, "build an entry with" 121 " mismatch IOService and IOUserClientClass by IOCatalogueSendData " 122 "should fail as kIOReturnNotPrivileged in none-dev kernel without kernelmanagerd"); 123 } 124#else 125 T_EXPECT_MACH_SUCCESS(kret, 126 "IOCatalogueSendData should return success with kernelmanagerd"); 127#endif /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */ 128 T_EXPECT_FALSE(test_open_ioregistry("fooBar", "IOReportHub", false), 129 "mismatched entry built by IOCatalogueSendData should not be opened"); 130} 131