xref: /xnu-8796.141.3/tests/iokit/io_catalog_send_data.m (revision 1b191cb58250d0705d8a51287127505aa4bc0789)
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")
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