xref: /xnu-11215/libkdd/kcdata_core.m (revision 88cc0b97)
1/*
2 * Copyright (c) 2015 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <kcdata.h>
30#import <Foundation/Foundation.h>
31#import "kdd.h"
32#import "KCDBasicTypeDescription.h"
33#import "KCDStructTypeDescription.h"
34#import "KCDEmbeddedBufferDescription.h"
35
36#define LIB_KCD_ERR_DOMAIN @"KCDataError"
37
38#define GEN_ERROR(code, msg) gen_error(__LINE__, code, @msg)
39#define GEN_ERRORF(code, msg, ...) gen_error(__LINE__, code, [NSString stringWithFormat:@msg, __VA_ARGS__])
40
41#define MAX_KCDATATYPE_BUFFER_SIZE 2048
42extern struct kcdata_type_definition * kcdata_get_typedescription(unsigned type_id, uint8_t * buffer, uint32_t buffer_size);
43
44BOOL setKCDataTypeForID(uint32_t newTypeID, KCDataType *newTypeObj);
45
46static NSError *
47gen_error(int line, NSInteger code, NSString *message)
48{
49	return [NSError errorWithDomain:LIB_KCD_ERR_DOMAIN
50							   code:code
51						   userInfo:@{ @"line": @(line), @"message": message }];
52}
53
54static BOOL
55mergedict(NSMutableDictionary * container, NSDictionary * object, NSError ** error)
56{
57	for (id key in object) {
58		id existing = container[key];
59		id new = object[key];
60		if (existing) {
61			if ([existing isKindOfClass:[NSMutableArray class]] && [new isKindOfClass:[ NSArray class ]]) {
62				[existing addObjectsFromArray:new];
63			} else {
64				if (error) {
65					*error = GEN_ERRORF(KERN_INVALID_OBJECT, "repeated key: %@", key);
66				}
67				return FALSE;
68			}
69		} else {
70			[container setValue:new forKey:key];
71		}
72	}
73	return TRUE;
74}
75
76/*!
77 * @function getTypeFromTypeDef
78 *
79 * @abstract
80 * Build a KCDataType from a type definition.
81 *
82 * @param typeDef
83 * A pointer to kcdata_type_definition_t that specifies the type fields and has subtype definitions
84 * in the memory immediately following the type_definition.
85 *
86 * @return KCDataType * type object which can be used to parse data into dictionaries.
87 * This may return nil if it finds the data to be invalid.
88 *
89 * @discussion
90 * This routine tries to decode the typeDef structure and create either a basic type (KCDBasicTypeDescription)
91 * or a struct type.
92 */
93static KCDataType * getTypeFromTypeDef(struct kcdata_type_definition * typeDef);
94
95static KCDataType *
96getTypeFromTypeDef(struct kcdata_type_definition * typeDef)
97{
98	if (typeDef == NULL) {
99		return nil;
100	}
101	NSString * kct_name = [NSString stringWithFormat:@"%s", typeDef->kct_name];
102	if (typeDef->kct_num_elements == 1 && !(typeDef->kct_elements[0].kcs_flags & KCS_SUBTYPE_FLAGS_STRUCT)) {
103		KCDBasicTypeDescription * retval = [[KCDBasicTypeDescription alloc] initWithKCTypeDesc:&typeDef->kct_elements[0]];
104		return retval;
105	} else {
106		KCDStructTypeDescription * retval =
107		    [[KCDStructTypeDescription alloc] initWithType:typeDef->kct_type_identifier withName:kct_name];
108		/* need to do work here to get the array of elements setup here */
109		KCDBasicTypeDescription * curField = nil;
110		for (unsigned int i = 0; i < typeDef->kct_num_elements; i++) {
111			curField = [[KCDBasicTypeDescription alloc] initWithKCTypeDesc:&typeDef->kct_elements[i]];
112			[retval addFieldBasicType:curField];
113			if (typeDef->kct_elements[i].kcs_flags & KCS_SUBTYPE_FLAGS_MERGE) {
114				[retval setFlagsRequestedMerge];
115			}
116		}
117		return retval;
118	}
119	return nil;
120}
121
122static dispatch_once_t onceToken;
123static NSMutableDictionary * knownTypes = nil;
124
125KCDataType *
126getKCDataTypeForID(uint32_t typeID)
127{
128	dispatch_once(&onceToken, ^{
129	  if (!knownTypes) {
130		  knownTypes = [[NSMutableDictionary alloc] init];
131	  }
132	});
133
134	NSNumber * type = [NSNumber numberWithUnsignedInt:typeID];
135	if (!knownTypes[type]) {
136		if (typeID == KCDATA_TYPE_NESTED_KCDATA) {
137			knownTypes[type] = [[KCDEmbeddedBufferDescription alloc] init];
138			return knownTypes[type];
139		}
140		/* code to query system for type information */
141		uint8_t buffer[MAX_KCDATATYPE_BUFFER_SIZE];
142		struct kcdata_type_definition * sys_def = kcdata_get_typedescription(typeID, buffer, MAX_KCDATATYPE_BUFFER_SIZE);
143		if (sys_def == NULL) {
144			knownTypes[type] = [[KCDBasicTypeDescription alloc] createDefaultForType:typeID];
145		} else {
146			knownTypes[type] = getTypeFromTypeDef(sys_def);
147		}
148	}
149	assert(knownTypes[type] != nil);
150	return knownTypes[type];
151}
152
153BOOL
154setKCDataTypeForID(uint32_t newTypeID, KCDataType *newTypeObj) {
155    if (newTypeObj == NULL || newTypeID == 0) {
156        return FALSE;
157    }
158
159    dispatch_once(&onceToken, ^{
160        if (!knownTypes) {
161            knownTypes = [[NSMutableDictionary alloc] init];
162        }
163    });
164
165    NSNumber * type = [NSNumber numberWithUnsignedInt:newTypeID];
166
167    if (!knownTypes[type]) {
168        knownTypes[type] = newTypeObj;
169        return TRUE;
170    }
171
172    return FALSE;
173}
174
175
176NSString *
177KCDataTypeNameForID(uint32_t typeID)
178{
179	NSString * retval = [NSString stringWithFormat:@"%u", typeID];
180	KCDataType * t    = getKCDataTypeForID(typeID);
181
182	if (![[t name] containsString:@"Type_"]) {
183		retval = [t name];
184	}
185	return retval;
186}
187
188NSMutableDictionary *
189parseKCDataArray(kcdata_iter_t iter, NSError **error)
190{
191	if (!kcdata_iter_array_valid(iter)) {
192		if (error)
193			*error = GEN_ERROR(KERN_INVALID_OBJECT, "invalid array");
194		return NULL;
195	}
196
197	uint32_t typeID               = kcdata_iter_array_elem_type(iter);
198	uint32_t count                = kcdata_iter_array_elem_count(iter);
199	uint32_t size                 = kcdata_iter_array_elem_size(iter);
200	uint8_t * buffer              = (uint8_t *)kcdata_iter_payload(iter);
201	KCDataType * datatype         = getKCDataTypeForID(typeID);
202	NSMutableDictionary * retval  = [[NSMutableDictionary alloc] initWithCapacity:1];
203	NSMutableArray * arr          = [[NSMutableArray alloc] initWithCapacity:count];
204	retval[[datatype name]]       = arr;
205	NSDictionary * tmpdict = NULL;
206	for (uint32_t i = 0; i < count; i++) {
207		tmpdict = [datatype parseData:(void *)&buffer[i * size] ofLength:size];
208		if (!tmpdict) {
209			if (error)
210				*error = GEN_ERRORF(KERN_INVALID_OBJECT, "failed to parse array element.  type=0x%x", (int)typeID);
211			return NULL;
212		}
213		if ([datatype shouldMergeData]) {
214			assert([tmpdict count] == 1);
215			[arr addObject: [tmpdict allValues][0]];
216		} else {
217			[arr addObject:tmpdict];
218		}
219	}
220	return retval;
221}
222
223NSMutableDictionary *
224parseKCDataContainer(kcdata_iter_t *iter_p, NSError **error)
225{
226	kcdata_iter_t iter = *iter_p;
227
228	if (!kcdata_iter_container_valid(iter)) {
229		if (error)
230			*error = GEN_ERROR(KERN_INVALID_OBJECT, "invalid container");
231		return NULL;
232	}
233	uint64_t containerID = kcdata_iter_container_id(iter);
234
235	/* setup collection object for sub containers */
236	NSMutableDictionary * sub_containers = [[NSMutableDictionary alloc] init];
237	NSMutableDictionary * retval         = [[NSMutableDictionary alloc] init];
238	NSMutableDictionary * container      = [[NSMutableDictionary alloc] init];
239
240	KCDataType * tmptype;
241	uint32_t _t;
242	void * _d;
243	BOOL ok;
244	NSDictionary * tmpdict;
245	BOOL found_end = FALSE;
246	retval[KCDataTypeNameForID(kcdata_iter_container_type(iter))] = container;
247
248	iter = kcdata_iter_next(iter);
249
250	KCDATA_ITER_FOREACH(iter)
251	{
252		_t = kcdata_iter_type(iter);
253		_d = kcdata_iter_payload(iter);
254		if (_t == KCDATA_TYPE_CONTAINER_END) {
255			if (kcdata_iter_container_id(iter) != containerID) {
256				if (error)
257					*error = GEN_ERROR(KERN_INVALID_ARGUMENT, "container marker mismatch");
258				return NULL;
259			}
260			found_end = TRUE;
261			break;
262		}
263
264		if (_t == KCDATA_TYPE_ARRAY) {
265			tmpdict = parseKCDataArray(iter, error);
266			if (!tmpdict)
267				return NULL;
268
269			ok = mergedict(container, tmpdict, error);
270			if (!ok)
271				return NULL;
272
273			continue;
274		}
275
276		if (_t == KCDATA_TYPE_CONTAINER_BEGIN) {
277			NSString * subcontainerID = [NSString stringWithFormat:@"%llu", kcdata_iter_container_id(iter)];
278			tmpdict                   = parseKCDataContainer(&iter, error);
279			if (!tmpdict)
280				return NULL;
281			assert([tmpdict count] == 1);
282			for (NSString * k in [tmpdict keyEnumerator]) {
283				if (sub_containers[k] == nil) {
284					sub_containers[k] = [[NSMutableDictionary alloc] init];
285				}
286				if (sub_containers[k][subcontainerID] != nil) {
287					if (error)
288						*error = GEN_ERRORF(KERN_INVALID_OBJECT, "repeated container id: %@", subcontainerID);
289					return NULL;
290				}
291				sub_containers[k][subcontainerID] = tmpdict[k];
292			}
293			continue;
294		}
295
296		tmptype = getKCDataTypeForID(_t);
297		tmpdict = [tmptype parseData:_d ofLength:kcdata_iter_size(iter)];
298		if (!tmpdict) {
299			if (error)
300				*error = GEN_ERRORF(KERN_INVALID_OBJECT, "failed to parse. type=0x%x", (int)_t);
301			return NULL;
302		}
303		if (![tmptype shouldMergeData]) {
304			tmpdict = @{[tmptype name] : tmpdict};
305		}
306		ok = mergedict(container, tmpdict, error);
307		if (!ok)
308			return NULL;
309	}
310
311	if (!found_end) {
312		if (error)
313			*error = GEN_ERROR(KERN_INVALID_ARGUMENT, "missing container end");
314		return NULL;
315	}
316
317	ok = mergedict(container, sub_containers, error);
318	if (!ok)
319		return NULL;
320
321	*iter_p = iter;
322	return retval;
323}
324
325NSDictionary *
326parseKCDataBuffer(void * dataBuffer, uint32_t size, NSError ** error)
327{
328	if (dataBuffer == NULL) {
329		if (error)
330			*error = GEN_ERROR(KERN_INVALID_ARGUMENT, "buffer is null");
331		return NULL;
332	}
333
334	uint32_t _type        = (size >= sizeof(uint32_t)) ? *(uint32_t*)dataBuffer : 0;
335	uint32_t _size        = 0;
336	uint64_t _flags       = 0;
337	void * _datap         = NULL;
338	KCDataType * kcd_type = NULL;
339	NSString * rootKey    = NULL;
340	uint32_t rootType     = _type;
341	BOOL ok;
342
343	/* validate begin tag and get root key */
344	switch (_type) {
345	case KCDATA_BUFFER_BEGIN_CRASHINFO:
346		rootKey = @"kcdata_crashinfo";
347		break;
348	case KCDATA_BUFFER_BEGIN_STACKSHOT:
349		rootKey = @"kcdata_stackshot";
350		break;
351	case KCDATA_BUFFER_BEGIN_DELTA_STACKSHOT:
352		rootKey = @"kcdata_delta_stackshot";
353		break;
354	case KCDATA_BUFFER_BEGIN_OS_REASON:
355		rootKey = @"kcdata_reason";
356		break;
357	case KCDATA_BUFFER_BEGIN_XNUPOST_CONFIG:
358		rootKey = @"xnupost_testconfig";
359		break;
360	default: {
361		if (error)
362			*error = GEN_ERROR(KERN_INVALID_VALUE, "invalid magic number");
363		return NULL;
364		break;
365	}
366	}
367	assert(rootKey != NULL);
368
369	kcdata_iter_t iter = kcdata_iter(dataBuffer, size);
370
371	if (!kcdata_iter_valid(iter)) {
372		if (error) {
373			*error = GEN_ERROR(KERN_INVALID_OBJECT, "initial item is invalid");
374		}
375		return NULL;
376	}
377
378	NSMutableDictionary * rootObject = [NSMutableDictionary dictionary];
379	NSDictionary * retval            = [NSMutableDictionary dictionaryWithObject:rootObject forKey:rootKey];
380
381	/* iterate over each kcdata item */
382	KCDATA_ITER_FOREACH(iter)
383	{
384		_type  = kcdata_iter_type(iter);
385		_size  = kcdata_iter_size(iter);
386		_flags = kcdata_iter_flags(iter);
387		_datap = kcdata_iter_payload(iter);
388
389		if (_type == rootType)
390			continue;
391
392		if (_type == KCDATA_TYPE_ARRAY) {
393			NSDictionary * dict = parseKCDataArray(iter, error);
394			if (!dict)
395				return nil;
396
397			ok = mergedict(rootObject, dict, error);
398			if (!ok)
399				return NULL;
400
401			continue;
402		}
403
404		if (_type == KCDATA_TYPE_CONTAINER_BEGIN) {
405			NSString * containerID = [NSString stringWithFormat:@"%llu", kcdata_iter_container_id(iter)];
406			NSMutableDictionary *container = parseKCDataContainer(&iter, error);
407			if (!container)
408				return nil;
409			assert([container count] == 1);
410			for (NSString * k in [container keyEnumerator]) {
411				if (rootObject[k] == nil) {
412					rootObject[k] = [[NSMutableDictionary alloc] init];
413				}
414				if (rootObject[k][containerID] != nil) {
415					if (error)
416						*error = GEN_ERRORF(KERN_INVALID_OBJECT, "repeated container id: %@", containerID);
417					return NULL;
418				}
419				rootObject[k][containerID] = container[k];
420			}
421			continue;
422		}
423
424        if (_type == KCDATA_TYPE_TYPEDEFINTION) {
425            KCDataType *new_type = getTypeFromTypeDef((struct kcdata_type_definition *)_datap);
426            if (new_type != NULL) {
427                setKCDataTypeForID([new_type typeID], new_type);
428                kcd_type               = getKCDataTypeForID(_type);
429                NSDictionary * tmpdict = [kcd_type parseData:_datap ofLength:_size];
430                if (!tmpdict) {
431                    if (error)
432                        *error = GEN_ERRORF(KERN_INVALID_OBJECT, "failed to parse. type=0x%x", (int)_type);
433                    return NULL;
434                }
435                NSString *k = [NSString stringWithFormat:@"typedef[%@]", [new_type name]];
436                rootObject[k] = tmpdict;
437            }else {
438                if (error)
439                    *error = GEN_ERRORF(KERN_INVALID_OBJECT, "Failed to parse type definition for type %u", _type);
440                return NULL;
441            }
442            continue;
443        }
444
445		kcd_type               = getKCDataTypeForID(_type);
446		NSDictionary * tmpdict = [kcd_type parseData:_datap ofLength:_size];
447		if (!tmpdict) {
448			if (error)
449				*error = GEN_ERRORF(KERN_INVALID_OBJECT, "failed to parse. type=0x%x", (int)_type);
450			return NULL;
451		}
452		if (![kcd_type shouldMergeData]) {
453			tmpdict = @{[kcd_type name] : tmpdict};
454		}
455		ok = mergedict(rootObject, tmpdict, error);
456		if (!ok)
457			return NULL;
458	}
459
460	if (KCDATA_ITER_FOREACH_FAILED(iter)) {
461		retval = nil;
462		if (error) {
463			*error = GEN_ERROR(KERN_INVALID_OBJECT, "invalid item or missing buffer end marker");
464		}
465	}
466
467	return retval;
468}
469