]> git.saurik.com Git - apple/xnu.git/blob - libkdd/kcdata_core.m
xnu-4570.31.3.tar.gz
[apple/xnu.git] / libkdd / kcdata_core.m
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
42 extern struct kcdata_type_definition * kcdata_get_typedescription(unsigned type_id, uint8_t * buffer, uint32_t buffer_size);
43
44 BOOL setKCDataTypeForID(uint32_t newTypeID, KCDataType *newTypeObj);
45
46 static NSError *
47 gen_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
54 static BOOL
55 mergedict(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 */
93 static KCDataType * getTypeFromTypeDef(struct kcdata_type_definition * typeDef);
94
95 static KCDataType *
96 getTypeFromTypeDef(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
122 static dispatch_once_t onceToken;
123 static NSMutableDictionary * knownTypes = nil;
124
125 KCDataType *
126 getKCDataTypeForID(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
153 BOOL
154 setKCDataTypeForID(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
176 NSString *
177 KCDataTypeNameForID(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
188 NSMutableDictionary *
189 parseKCDataArray(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
223 NSMutableDictionary *
224 parseKCDataContainer(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
325 NSDictionary *
326 parseKCDataBuffer(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 }