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