| /* |
| * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. All rights reserved. |
| * Copyright (c) 2009 Stuart Crook <stuart@echus.demon.co.uk>. All rights reserved. |
| * |
| * This source code is a modified version of the CoreFoundation sources released by Apple Inc. under |
| * the terms of the APSL version 2.0 (see below). |
| * |
| * For information about changes from the original Apple source release can be found by reviewing the |
| * source control system for the project at https://sourceforge.net/svn/?group_id=246198. |
| * |
| * The original license information is as follows: |
| * |
| * Copyright (c) 2008 Apple Inc. All rights reserved. |
| * |
| * @APPLE_LICENSE_HEADER_START@ |
| * |
| * This file contains Original Code and/or Modifications of Original Code |
| * as defined in and that are subject to the Apple Public Source License |
| * Version 2.0 (the 'License'). You may not use this file except in |
| * compliance with the License. Please obtain a copy of the License at |
| * http://www.opensource.apple.com/apsl/ and read it before using this |
| * file. |
| * |
| * The Original Code and all software distributed under the License are |
| * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
| * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
| * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
| * Please see the License for the specific language governing rights and |
| * limitations under the License. |
| * |
| * @APPLE_LICENSE_HEADER_END@ |
| */ |
| /* CFBinaryPList.c |
| Copyright 2000-2002, Apple, Inc. All rights reserved. |
| Responsibility: Christopher Kane |
| */ |
| |
| |
| #include <CoreFoundation/CFString.h> |
| #include <CoreFoundation/CFNumber.h> |
| #include <CoreFoundation/CFDate.h> |
| #include <CoreFoundation/CFData.h> |
| #include <CoreFoundation/CFArray.h> |
| #include <CoreFoundation/CFDictionary.h> |
| #include <CoreFoundation/CFSet.h> |
| #include <CoreFoundation/CFPropertyList.h> |
| #include <CoreFoundation/CFByteOrder.h> |
| #include <CoreFoundation/CFRuntime.h> |
| #include <CoreFoundation/CFStream.h> |
| #include <stdio.h> |
| #include <stdint.h> |
| #include <limits.h> |
| #include <string.h> |
| #include "CFInternal.h" |
| |
| typedef struct { |
| int64_t high; |
| uint64_t low; |
| } CFSInt128Struct; |
| |
| enum { |
| kCFNumberSInt128Type = 17 |
| }; |
| |
| extern CFNumberType _CFNumberGetType2(CFNumberRef number); |
| |
| enum { |
| CF_NO_ERROR = 0, |
| CF_OVERFLOW_ERROR = (1 << 0), |
| }; |
| |
| CF_INLINE uint32_t __check_uint32_add_unsigned_unsigned(uint32_t x, uint32_t y, int32_t* err) { |
| if((UINT_MAX - y) < x) |
| *err = *err | CF_OVERFLOW_ERROR; |
| return x + y; |
| }; |
| |
| CF_INLINE uint64_t __check_uint64_add_unsigned_unsigned(uint64_t x, uint64_t y, int32_t* err) { |
| if((ULLONG_MAX - y) < x) |
| *err = *err | CF_OVERFLOW_ERROR; |
| return x + y; |
| }; |
| |
| CF_INLINE uint32_t __check_uint32_mul_unsigned_unsigned(uint32_t x, uint32_t y, int32_t* err) { |
| uint64_t tmp = (uint64_t) x * (uint64_t) y; |
| /* If any of the upper 32 bits touched, overflow */ |
| if(tmp & 0xffffffff00000000ULL) |
| *err = *err | CF_OVERFLOW_ERROR; |
| return (uint32_t) tmp; |
| }; |
| |
| CF_INLINE uint64_t __check_uint64_mul_unsigned_unsigned(uint64_t x, uint64_t y, int32_t* err) { |
| if(x == 0) return 0; |
| if(ULLONG_MAX/x < y) |
| *err = *err | CF_OVERFLOW_ERROR; |
| return x * y; |
| }; |
| |
| #if __LP64__ |
| #define check_ptr_add(p, a, err) (const uint8_t *)__check_uint64_add_unsigned_unsigned((uintptr_t)p, (uintptr_t)a, err) |
| #define check_size_t_mul(b, a, err) (size_t)__check_uint64_mul_unsigned_unsigned((size_t)b, (size_t)a, err) |
| #else |
| #define check_ptr_add(p, a, err) (const uint8_t *)__check_uint32_add_unsigned_unsigned((uintptr_t)p, (uintptr_t)a, err) |
| #define check_size_t_mul(b, a, err) (size_t)__check_uint32_mul_unsigned_unsigned((size_t)b, (size_t)a, err) |
| #endif |
| |
| |
| CF_INLINE CFTypeID __CFGenericTypeID_genericobj_inline(const void *cf) { |
| CFTypeID typeID = (*(uint32_t *)(((CFRuntimeBase *)cf)->_cfinfo) >> 8) & 0xFFFF; |
| return CF_IS_OBJC(typeID, cf) ? CFGetTypeID(cf) : typeID; |
| } |
| |
| struct __CFKeyedArchiverUID { |
| CFRuntimeBase _base; |
| uint32_t _value; |
| }; |
| |
| static CFStringRef __CFKeyedArchiverUIDCopyDescription(CFTypeRef cf) { |
| CFKeyedArchiverUIDRef uid = (CFKeyedArchiverUIDRef)cf; |
| return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFKeyedArchiverUID %p [%p]>{value = %u}"), cf, CFGetAllocator(cf), uid->_value); |
| } |
| |
| static CFStringRef __CFKeyedArchiverUIDCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) { |
| CFKeyedArchiverUIDRef uid = (CFKeyedArchiverUIDRef)cf; |
| return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("@%u@"), uid->_value); |
| } |
| |
| static CFTypeID __kCFKeyedArchiverUIDTypeID = _kCFRuntimeNotATypeID; |
| |
| static const CFRuntimeClass __CFKeyedArchiverUIDClass = { |
| 0, |
| "CFKeyedArchiverUID", |
| NULL, // init |
| NULL, // copy |
| NULL, // finalize |
| NULL, // equal -- pointer equality only |
| NULL, // hash -- pointer hashing only |
| __CFKeyedArchiverUIDCopyFormattingDescription, |
| __CFKeyedArchiverUIDCopyDescription |
| }; |
| |
| __private_extern__ void __CFKeyedArchiverUIDInitialize(void) { |
| __kCFKeyedArchiverUIDTypeID = _CFRuntimeRegisterClass(&__CFKeyedArchiverUIDClass); |
| } |
| |
| CFTypeID _CFKeyedArchiverUIDGetTypeID(void) { |
| return __kCFKeyedArchiverUIDTypeID; |
| } |
| |
| CFKeyedArchiverUIDRef _CFKeyedArchiverUIDCreate(CFAllocatorRef allocator, uint32_t value) { |
| CFKeyedArchiverUIDRef uid; |
| uid = (CFKeyedArchiverUIDRef)_CFRuntimeCreateInstance(allocator, __kCFKeyedArchiverUIDTypeID, sizeof(struct __CFKeyedArchiverUID) - sizeof(CFRuntimeBase), NULL); |
| if (NULL == uid) { |
| return NULL; |
| } |
| ((struct __CFKeyedArchiverUID *)uid)->_value = value; |
| return uid; |
| } |
| |
| |
| uint32_t _CFKeyedArchiverUIDGetValue(CFKeyedArchiverUIDRef uid) { |
| return uid->_value; |
| } |
| |
| |
| typedef struct { |
| CFTypeRef stream; |
| CFTypeRef error; |
| uint64_t written; |
| int32_t used; |
| bool streamIsData; |
| uint8_t buffer[8192 - 32]; |
| } __CFBinaryPlistWriteBuffer; |
| |
| static void writeBytes(__CFBinaryPlistWriteBuffer *buf, const UInt8 *bytes, CFIndex length) { |
| if (0 == length) return; |
| if (buf->error) return; |
| if (buf->streamIsData) { |
| CFDataAppendBytes((CFMutableDataRef)buf->stream, bytes, length); |
| buf->written += length; |
| } else { |
| /* SystemConfiguration relies on being able to serialize a plist to a write stream. |
| * There seems no reason why this isn't supported. The old code read: |
| CFAssert(false, __kCFLogAssertion, "Streams are not supported on this platform"); |
| */ |
| CFIndex lengthWritten = CFWriteStreamWrite((CFWriteStreamRef)buf->stream, bytes, length); |
| buf->written += lengthWritten; |
| } |
| } |
| |
| static void bufferWrite(__CFBinaryPlistWriteBuffer *buf, const uint8_t *buffer, CFIndex count) { |
| if (0 == count) return; |
| if ((CFIndex)sizeof(buf->buffer) <= count) { |
| writeBytes(buf, buf->buffer, buf->used); |
| buf->used = 0; |
| writeBytes(buf, buffer, count); |
| return; |
| } |
| CFIndex copyLen = __CFMin(count, (CFIndex)sizeof(buf->buffer) - buf->used); |
| memmove(buf->buffer + buf->used, buffer, copyLen); |
| buf->used += copyLen; |
| if (sizeof(buf->buffer) == buf->used) { |
| writeBytes(buf, buf->buffer, sizeof(buf->buffer)); |
| memmove(buf->buffer, buffer + copyLen, count - copyLen); |
| buf->used = count - copyLen; |
| } |
| } |
| |
| static void bufferFlush(__CFBinaryPlistWriteBuffer *buf) { |
| writeBytes(buf, buf->buffer, buf->used); |
| buf->used = 0; |
| } |
| |
| /* |
| HEADER |
| magic number ("bplist") |
| file format version |
| |
| OBJECT TABLE |
| variable-sized objects |
| |
| Object Formats (marker byte followed by additional info in some cases) |
| null 0000 0000 |
| bool 0000 1000 // false |
| bool 0000 1001 // true |
| fill 0000 1111 // fill byte |
| int 0001 nnnn ... // # of bytes is 2^nnnn, big-endian bytes |
| real 0010 nnnn ... // # of bytes is 2^nnnn, big-endian bytes |
| date 0011 0011 ... // 8 byte float follows, big-endian bytes |
| data 0100 nnnn [int] ... // nnnn is number of bytes unless 1111 then int count follows, followed by bytes |
| string 0101 nnnn [int] ... // ASCII string, nnnn is # of chars, else 1111 then int count, then bytes |
| string 0110 nnnn [int] ... // Unicode string, nnnn is # of chars, else 1111 then int count, then big-endian 2-byte uint16_t |
| 0111 xxxx // unused |
| uid 1000 nnnn ... // nnnn+1 is # of bytes |
| 1001 xxxx // unused |
| array 1010 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows |
| 1011 xxxx // unused |
| set 1100 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows |
| dict 1101 nnnn [int] keyref* objref* // nnnn is count, unless '1111', then int count follows |
| 1110 xxxx // unused |
| 1111 xxxx // unused |
| |
| OFFSET TABLE |
| list of ints, byte size of which is given in trailer |
| -- these are the byte offsets into the file |
| -- number of these is in the trailer |
| |
| TRAILER |
| byte size of offset ints in offset table |
| byte size of object refs in arrays and dicts |
| number of offsets in offset table (also is number of objects) |
| element # in offset table which is top level object |
| offset table offset |
| |
| */ |
| |
| |
| static CFTypeID stringtype = -1, datatype = -1, numbertype = -1, datetype = -1; |
| static CFTypeID booltype = -1, nulltype = -1, dicttype = -1, arraytype = -1, settype = -1; |
| |
| static void _appendInt(__CFBinaryPlistWriteBuffer *buf, uint64_t bigint) { |
| uint8_t marker; |
| uint8_t *bytes; |
| CFIndex nbytes; |
| if (bigint <= (uint64_t)0xff) { |
| nbytes = 1; |
| marker = kCFBinaryPlistMarkerInt | 0; |
| } else if (bigint <= (uint64_t)0xffff) { |
| nbytes = 2; |
| marker = kCFBinaryPlistMarkerInt | 1; |
| } else if (bigint <= (uint64_t)0xffffffff) { |
| nbytes = 4; |
| marker = kCFBinaryPlistMarkerInt | 2; |
| } else { |
| nbytes = 8; |
| marker = kCFBinaryPlistMarkerInt | 3; |
| } |
| bigint = CFSwapInt64HostToBig(bigint); |
| bytes = (uint8_t *)&bigint + sizeof(bigint) - nbytes; |
| bufferWrite(buf, &marker, 1); |
| bufferWrite(buf, bytes, nbytes); |
| } |
| |
| static void _appendUID(__CFBinaryPlistWriteBuffer *buf, CFKeyedArchiverUIDRef uid) { |
| uint8_t marker; |
| uint8_t *bytes; |
| CFIndex nbytes; |
| uint64_t bigint = _CFKeyedArchiverUIDGetValue(uid); |
| if (bigint <= (uint64_t)0xff) { |
| nbytes = 1; |
| } else if (bigint <= (uint64_t)0xffff) { |
| nbytes = 2; |
| } else if (bigint <= (uint64_t)0xffffffff) { |
| nbytes = 4; |
| } else { |
| nbytes = 8; |
| } |
| marker = kCFBinaryPlistMarkerUID | (uint8_t)(nbytes - 1); |
| bigint = CFSwapInt64HostToBig(bigint); |
| bytes = (uint8_t *)&bigint + sizeof(bigint) - nbytes; |
| bufferWrite(buf, &marker, 1); |
| bufferWrite(buf, bytes, nbytes); |
| } |
| |
| static Boolean __plistNumberEqual(CFTypeRef cf1, CFTypeRef cf2) { |
| // As long as this equals function is more restrictive than the |
| // existing one, for any given type, the hash function need not |
| // also be provided for the uniquing set. |
| if (CFNumberIsFloatType((CFNumberRef)cf1) != CFNumberIsFloatType((CFNumberRef)cf2)) return false; |
| return CFEqual(cf1, cf2); |
| } |
| |
| static CFHashCode __plistDataHash(CFTypeRef cf) { |
| CFDataRef data = (CFDataRef)cf; |
| return CFHashBytes((UInt8 *)CFDataGetBytePtr(data), __CFMin(CFDataGetLength(data), 1280)); |
| } |
| |
| static void _flattenPlist(CFPropertyListRef plist, CFMutableArrayRef objlist, CFMutableDictionaryRef objtable, CFMutableSetRef uniquingsets[]) { |
| CFPropertyListRef unique; |
| uint32_t refnum; |
| CFTypeID type = __CFGenericTypeID_genericobj_inline(plist); |
| CFIndex idx; |
| CFPropertyListRef *list, buffer[256]; |
| |
| // Do not unique dictionaries or arrays, because: they |
| // are slow to compare, and have poor hash codes. |
| // Uniquing bools is unnecessary. |
| int which = -1; |
| if (stringtype == type) { |
| which = 0; |
| } else if (numbertype == type) { |
| which = 1; |
| } else if (datetype == type) { |
| which = 2; |
| } else if (datatype == type) { |
| which = 3; |
| } |
| if (1 && -1 != which) { |
| CFMutableSetRef uniquingset = uniquingsets[which]; |
| CFIndex before = CFSetGetCount(uniquingset); |
| CFSetAddValue(uniquingset, plist); |
| CFIndex after = CFSetGetCount(uniquingset); |
| if (after == before) { // already in set |
| unique = CFSetGetValue(uniquingset, plist); |
| if (unique != plist) { |
| refnum = (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable, unique); |
| CFDictionaryAddValue(objtable, plist, (const void *)(uintptr_t)refnum); |
| } |
| return; |
| } |
| } |
| refnum = CFArrayGetCount(objlist); |
| CFArrayAppendValue(objlist, plist); |
| CFDictionaryAddValue(objtable, plist, (const void *)(uintptr_t)refnum); |
| if (dicttype == type) { |
| CFIndex count = CFDictionaryGetCount((CFDictionaryRef)plist); |
| list = (count <= 128) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, 2 * count * sizeof(CFTypeRef), 0); |
| CFDictionaryGetKeysAndValues((CFDictionaryRef)plist, list, list + count); |
| for (idx = 0; idx < 2 * count; idx++) { |
| _flattenPlist(list[idx], objlist, objtable, uniquingsets); |
| } |
| if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); |
| } else if (arraytype == type) { |
| CFIndex count = CFArrayGetCount((CFArrayRef)plist); |
| list = (count <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, count * sizeof(CFTypeRef), 0); |
| CFArrayGetValues((CFArrayRef)plist, CFRangeMake(0, count), list); |
| for (idx = 0; idx < count; idx++) { |
| _flattenPlist(list[idx], objlist, objtable, uniquingsets); |
| } |
| if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); |
| } |
| } |
| |
| // stream must be a CFMutableDataRef |
| CFIndex __CFBinaryPlistWriteToStream(CFPropertyListRef plist, CFTypeRef stream) { |
| CFMutableDictionaryRef objtable; |
| CFMutableArrayRef objlist; |
| CFBinaryPlistTrailer trailer; |
| uint64_t *offsets, length_so_far; |
| uint64_t mask, refnum; |
| int64_t idx, idx2, cnt; |
| __CFBinaryPlistWriteBuffer *buf; |
| |
| if ((CFTypeID)-1 == stringtype) { |
| stringtype = CFStringGetTypeID(); |
| } |
| if ((CFTypeID)-1 == datatype) { |
| datatype = CFDataGetTypeID(); |
| } |
| if ((CFTypeID)-1 == numbertype) { |
| numbertype = CFNumberGetTypeID(); |
| } |
| if ((CFTypeID)-1 == booltype) { |
| booltype = CFBooleanGetTypeID(); |
| } |
| if ((CFTypeID)-1 == datetype) { |
| datetype = CFDateGetTypeID(); |
| } |
| if ((CFTypeID)-1 == dicttype) { |
| dicttype = CFDictionaryGetTypeID(); |
| } |
| if ((CFTypeID)-1 == arraytype) { |
| arraytype = CFArrayGetTypeID(); |
| } |
| if ((CFTypeID)-1 == settype) { |
| settype = CFSetGetTypeID(); |
| } |
| if ((CFTypeID)-1 == nulltype) { |
| nulltype = CFNullGetTypeID(); |
| } |
| |
| objtable = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL); |
| _CFDictionarySetCapacity(objtable, 640); |
| objlist = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, NULL); |
| _CFArraySetCapacity(objlist, 640); |
| CFSetCallBacks cb = kCFTypeSetCallBacks; |
| cb.retain = NULL; |
| cb.release = NULL; |
| CFMutableSetRef uniquingsets[4]; |
| uniquingsets[0] = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &cb); |
| _CFSetSetCapacity(uniquingsets[0], 1000); |
| cb.equal = __plistNumberEqual; |
| uniquingsets[1] = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &cb); |
| _CFSetSetCapacity(uniquingsets[1], 500); |
| cb.equal = kCFTypeSetCallBacks.equal; |
| uniquingsets[2] = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &cb); |
| _CFSetSetCapacity(uniquingsets[2], 500); |
| cb.hash = __plistDataHash; |
| uniquingsets[3] = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &cb); |
| _CFSetSetCapacity(uniquingsets[3], 500); |
| |
| _flattenPlist(plist, objlist, objtable, uniquingsets); |
| |
| CFRelease(uniquingsets[0]); |
| CFRelease(uniquingsets[1]); |
| CFRelease(uniquingsets[2]); |
| CFRelease(uniquingsets[3]); |
| |
| cnt = CFArrayGetCount(objlist); |
| offsets = (uint64_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault, (CFIndex)(cnt * sizeof(*offsets)), 0); |
| |
| buf = (__CFBinaryPlistWriteBuffer *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(__CFBinaryPlistWriteBuffer), 0); |
| buf->stream = stream; |
| buf->error = NULL; |
| buf->streamIsData = (CFGetTypeID(stream) == CFDataGetTypeID()); |
| buf->written = 0; |
| buf->used = 0; |
| bufferWrite(buf, (uint8_t *)"bplist00", 8); // header |
| |
| memset(&trailer, 0, sizeof(trailer)); |
| trailer._numObjects = CFSwapInt64HostToBig(cnt); |
| trailer._topObject = 0; // true for this implementation |
| mask = ~(uint64_t)0; |
| while (cnt & mask) { |
| trailer._objectRefSize++; |
| mask = mask << 8; |
| } |
| |
| for (idx = 0; idx < cnt; idx++) { |
| CFPropertyListRef obj = CFArrayGetValueAtIndex(objlist, (CFIndex)idx); |
| CFTypeID type = __CFGenericTypeID_genericobj_inline(obj); |
| offsets[idx] = buf->written + buf->used; |
| if (stringtype == type) { |
| CFIndex ret, count = CFStringGetLength((CFStringRef)obj); |
| CFIndex needed; |
| uint8_t *bytes, buffer[1024]; |
| bytes = (count <= 1024) ? buffer : (uint8_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault, count, 0); |
| // presumption, believed to be true, is that ASCII encoding may need |
| // less bytes, but will not need greater, than the # of unichars |
| ret = CFStringGetBytes((CFStringRef)obj, CFRangeMake(0, count), kCFStringEncodingASCII, 0, false, bytes, count, &needed); |
| if (ret == count) { |
| uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerASCIIString | (needed < 15 ? needed : 0xf)); |
| bufferWrite(buf, &marker, 1); |
| if (15 <= needed) { |
| _appendInt(buf, (uint64_t)needed); |
| } |
| bufferWrite(buf, bytes, needed); |
| } else { |
| UniChar *chars; |
| uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerUnicode16String | (count < 15 ? count : 0xf)); |
| bufferWrite(buf, &marker, 1); |
| if (15 <= count) { |
| _appendInt(buf, (uint64_t)count); |
| } |
| chars = (UniChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, count * sizeof(UniChar), 0); |
| CFStringGetCharacters((CFStringRef)obj, CFRangeMake(0, count), chars); |
| for (idx2 = 0; idx2 < count; idx2++) { |
| chars[idx2] = CFSwapInt16HostToBig(chars[idx2]); |
| } |
| bufferWrite(buf, (uint8_t *)chars, count * sizeof(UniChar)); |
| CFAllocatorDeallocate(kCFAllocatorSystemDefault, chars); |
| } |
| if (bytes != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, bytes); |
| } else if (numbertype == type) { |
| uint8_t marker; |
| uint64_t bigint; |
| uint8_t *bytes; |
| CFIndex nbytes; |
| if (CFNumberIsFloatType((CFNumberRef)obj)) { |
| CFSwappedFloat64 swapped64; |
| CFSwappedFloat32 swapped32; |
| if (CFNumberGetByteSize((CFNumberRef)obj) <= (CFIndex)sizeof(float)) { |
| float v; |
| CFNumberGetValue((CFNumberRef)obj, kCFNumberFloat32Type, &v); |
| swapped32 = CFConvertFloat32HostToSwapped(v); |
| bytes = (uint8_t *)&swapped32; |
| nbytes = sizeof(float); |
| marker = kCFBinaryPlistMarkerReal | 2; |
| } else { |
| double v; |
| CFNumberGetValue((CFNumberRef)obj, kCFNumberFloat64Type, &v); |
| swapped64 = CFConvertFloat64HostToSwapped(v); |
| bytes = (uint8_t *)&swapped64; |
| nbytes = sizeof(double); |
| marker = kCFBinaryPlistMarkerReal | 3; |
| } |
| bufferWrite(buf, &marker, 1); |
| bufferWrite(buf, bytes, nbytes); |
| } else { |
| CFNumberType type = _CFNumberGetType2((CFNumberRef)obj); |
| if (kCFNumberSInt128Type == type) { |
| CFSInt128Struct s; |
| CFNumberGetValue((CFNumberRef)obj, kCFNumberSInt128Type, &s); |
| struct { |
| int64_t high; |
| uint64_t low; |
| } storage; |
| storage.high = CFSwapInt64HostToBig(s.high); |
| storage.low = CFSwapInt64HostToBig(s.low); |
| uint8_t *bytes = (uint8_t *)&storage; |
| uint8_t marker = kCFBinaryPlistMarkerInt | 4; |
| CFIndex nbytes = 16; |
| bufferWrite(buf, &marker, 1); |
| bufferWrite(buf, bytes, nbytes); |
| } else { |
| CFNumberGetValue((CFNumberRef)obj, kCFNumberSInt64Type, &bigint); |
| _appendInt(buf, bigint); |
| } |
| } |
| } else if (_CFKeyedArchiverUIDGetTypeID() == type) { |
| _appendUID(buf, (CFKeyedArchiverUIDRef)obj); |
| } else if (booltype == type) { |
| uint8_t marker = CFBooleanGetValue((CFBooleanRef)obj) ? kCFBinaryPlistMarkerTrue : kCFBinaryPlistMarkerFalse; |
| bufferWrite(buf, &marker, 1); |
| } else if (datatype == type) { |
| CFIndex count = CFDataGetLength((CFDataRef)obj); |
| uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerData | (count < 15 ? count : 0xf)); |
| bufferWrite(buf, &marker, 1); |
| if (15 <= count) { |
| _appendInt(buf, (uint64_t)count); |
| } |
| bufferWrite(buf, CFDataGetBytePtr((CFDataRef)obj), count); |
| } else if (datetype == type) { |
| CFSwappedFloat64 swapped; |
| uint8_t marker = kCFBinaryPlistMarkerDate; |
| bufferWrite(buf, &marker, 1); |
| swapped = CFConvertFloat64HostToSwapped(CFDateGetAbsoluteTime((CFDateRef)obj)); |
| bufferWrite(buf, (uint8_t *)&swapped, sizeof(swapped)); |
| } else if (dicttype == type) { |
| CFIndex count = CFDictionaryGetCount((CFDictionaryRef)obj); |
| CFPropertyListRef *list, buffer[512]; |
| uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerDict | (count < 15 ? count : 0xf)); |
| bufferWrite(buf, &marker, 1); |
| if (15 <= count) { |
| _appendInt(buf, (uint64_t)count); |
| } |
| list = (count <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, 2 * count * sizeof(CFTypeRef), 0); |
| CFDictionaryGetKeysAndValues((CFDictionaryRef)obj, list, list + count); |
| for (idx2 = 0; idx2 < 2 * count; idx2++) { |
| CFPropertyListRef value = list[idx2]; |
| uint32_t swapped = 0; |
| uint8_t *source = (uint8_t *)&swapped; |
| refnum = (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable, value); |
| swapped = CFSwapInt32HostToBig((uint32_t)refnum); |
| bufferWrite(buf, source + sizeof(swapped) - trailer._objectRefSize, trailer._objectRefSize); |
| } |
| if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); |
| } else if (arraytype == type) { |
| CFIndex count = CFArrayGetCount((CFArrayRef)obj); |
| CFPropertyListRef *list, buffer[256]; |
| uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerArray | (count < 15 ? count : 0xf)); |
| bufferWrite(buf, &marker, 1); |
| if (15 <= count) { |
| _appendInt(buf, (uint64_t)count); |
| } |
| list = (count <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, count * sizeof(CFTypeRef), 0); |
| CFArrayGetValues((CFArrayRef)obj, CFRangeMake(0, count), list); |
| for (idx2 = 0; idx2 < count; idx2++) { |
| CFPropertyListRef value = list[idx2]; |
| uint32_t swapped = 0; |
| uint8_t *source = (uint8_t *)&swapped; |
| refnum = (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable, value); |
| swapped = CFSwapInt32HostToBig((uint32_t)refnum); |
| bufferWrite(buf, source + sizeof(swapped) - trailer._objectRefSize, trailer._objectRefSize); |
| } |
| if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); |
| } else { |
| CFRelease(objtable); |
| CFRelease(objlist); |
| if (buf->error) CFRelease(buf->error); |
| CFAllocatorDeallocate(kCFAllocatorSystemDefault, buf); |
| CFAllocatorDeallocate(kCFAllocatorSystemDefault, offsets); |
| return 0; |
| } |
| } |
| CFRelease(objtable); |
| CFRelease(objlist); |
| |
| length_so_far = buf->written + buf->used; |
| trailer._offsetTableOffset = CFSwapInt64HostToBig(length_so_far); |
| trailer._offsetIntSize = 0; |
| mask = ~(uint64_t)0; |
| while (length_so_far & mask) { |
| trailer._offsetIntSize++; |
| mask = mask << 8; |
| } |
| |
| for (idx = 0; idx < cnt; idx++) { |
| uint64_t swapped = CFSwapInt64HostToBig(offsets[idx]); |
| uint8_t *source = (uint8_t *)&swapped; |
| bufferWrite(buf, source + sizeof(*offsets) - trailer._offsetIntSize, trailer._offsetIntSize); |
| } |
| length_so_far += cnt * trailer._offsetIntSize; |
| |
| bufferWrite(buf, (uint8_t *)&trailer, sizeof(trailer)); |
| bufferFlush(buf); |
| length_so_far += sizeof(trailer); |
| if (buf->error) { |
| CFRelease(buf->error); |
| return 0; |
| } |
| CFAllocatorDeallocate(kCFAllocatorSystemDefault, buf); |
| CFAllocatorDeallocate(kCFAllocatorSystemDefault, offsets); |
| return (CFIndex)length_so_far; |
| } |
| |
| |
| #define FAIL_FALSE do { return false; } while (0) |
| #define FAIL_MAXOFFSET do { return UINT64_MAX; } while (0) |
| |
| bool __CFBinaryPlistGetTopLevelInfo(const uint8_t *databytes, uint64_t datalen, uint8_t *marker, uint64_t *offset, CFBinaryPlistTrailer *trailer) { |
| CFBinaryPlistTrailer trail; |
| |
| if ((CFTypeID)-1 == stringtype) { |
| stringtype = CFStringGetTypeID(); |
| } |
| if ((CFTypeID)-1 == datatype) { |
| datatype = CFDataGetTypeID(); |
| } |
| if ((CFTypeID)-1 == numbertype) { |
| numbertype = CFNumberGetTypeID(); |
| } |
| if ((CFTypeID)-1 == booltype) { |
| booltype = CFBooleanGetTypeID(); |
| } |
| if ((CFTypeID)-1 == datetype) { |
| datetype = CFDateGetTypeID(); |
| } |
| if ((CFTypeID)-1 == dicttype) { |
| dicttype = CFDictionaryGetTypeID(); |
| } |
| if ((CFTypeID)-1 == arraytype) { |
| arraytype = CFArrayGetTypeID(); |
| } |
| if ((CFTypeID)-1 == settype) { |
| settype = CFSetGetTypeID(); |
| } |
| if ((CFTypeID)-1 == nulltype) { |
| nulltype = CFNullGetTypeID(); |
| } |
| |
| if (!databytes || datalen < sizeof(trail) + 8 + 1) FAIL_FALSE; |
| if (0 != memcmp("bplist00", databytes, 8) && 0 != memcmp("bplist01", databytes, 8)) return false; |
| memmove(&trail, databytes + datalen - sizeof(trail), sizeof(trail)); |
| if (trail._unused[0] != 0 || trail._unused[1] != 0 || trail._unused[2] != 0 || trail._unused[3] != 0 || trail._unused[4] != 0 || trail._unused[5] != 0) FAIL_FALSE; |
| trail._numObjects = CFSwapInt64BigToHost(trail._numObjects); |
| trail._topObject = CFSwapInt64BigToHost(trail._topObject); |
| trail._offsetTableOffset = CFSwapInt64BigToHost(trail._offsetTableOffset); |
| if (LONG_MAX < trail._numObjects) FAIL_FALSE; |
| if (LONG_MAX < trail._offsetTableOffset) FAIL_FALSE; |
| if (trail._numObjects < 1) FAIL_FALSE; |
| if (trail._numObjects <= trail._topObject) FAIL_FALSE; |
| if (trail._offsetTableOffset < 9) FAIL_FALSE; |
| if (datalen - sizeof(trail) <= trail._offsetTableOffset) FAIL_FALSE; |
| if (trail._offsetIntSize < 1) FAIL_FALSE; |
| if (trail._objectRefSize < 1) FAIL_FALSE; |
| int32_t err = CF_NO_ERROR; |
| uint64_t offsetIntSize = trail._offsetIntSize; |
| uint64_t offsetTableSize = __check_uint64_mul_unsigned_unsigned(trail._numObjects, offsetIntSize, &err); |
| if (CF_NO_ERROR!= err) FAIL_FALSE; |
| if (offsetTableSize < 1) FAIL_FALSE; |
| uint64_t objectDataSize = trail._offsetTableOffset - 8; |
| uint64_t tmpSum = __check_uint64_add_unsigned_unsigned(8, objectDataSize, &err); |
| tmpSum = __check_uint64_add_unsigned_unsigned(tmpSum, offsetTableSize, &err); |
| tmpSum = __check_uint64_add_unsigned_unsigned(tmpSum, sizeof(trail), &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| if (datalen != tmpSum) FAIL_FALSE; |
| if (trail._objectRefSize < 8 && (1ULL << (8 * trail._objectRefSize)) <= trail._numObjects) FAIL_FALSE; |
| if (trail._offsetIntSize < 8 && (1ULL << (8 * trail._offsetIntSize)) <= trail._offsetTableOffset) FAIL_FALSE; |
| const uint8_t *objectsFirstByte; |
| objectsFirstByte = check_ptr_add(databytes, 8, &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| const uint8_t *offsetsFirstByte = check_ptr_add(databytes, trail._offsetTableOffset, &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| const uint8_t *offsetsLastByte; |
| offsetsLastByte = check_ptr_add(offsetsFirstByte, offsetTableSize - 1, &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| |
| const uint8_t *bytesptr = databytes + trail._offsetTableOffset; |
| uint64_t maxOffset = trail._offsetTableOffset - 1; |
| for (CFIndex idx = 0; idx < trail._numObjects; idx++) { |
| uint64_t off = 0; |
| for (CFIndex idx2 = 0; idx2 < trail._offsetIntSize; idx2++) { |
| off = (off << 8) + bytesptr[idx2]; |
| } |
| if (maxOffset < off) FAIL_FALSE; |
| bytesptr += trail._offsetIntSize; |
| } |
| |
| bytesptr = databytes + trail._offsetTableOffset + trail._topObject * trail._offsetIntSize; |
| uint64_t off = 0; |
| for (CFIndex idx = 0; idx < trail._offsetIntSize; idx++) { |
| off = (off << 8) + bytesptr[idx]; |
| } |
| if (off < 8 || trail._offsetTableOffset <= off) FAIL_FALSE; |
| if (trailer) *trailer = trail; |
| if (offset) *offset = off; |
| if (marker) *marker = *(databytes + off); |
| return true; |
| } |
| |
| CF_INLINE Boolean _plistIsPrimitive(CFPropertyListRef pl) { |
| CFTypeID type = __CFGenericTypeID_genericobj_inline(pl); |
| if (dicttype == type || arraytype == type || settype == type) FAIL_FALSE; |
| return true; |
| } |
| |
| CF_INLINE bool _readInt(const uint8_t *ptr, const uint8_t *end_byte_ptr, uint64_t *bigint, const uint8_t **newptr) { |
| if (end_byte_ptr < ptr) FAIL_FALSE; |
| uint8_t marker = *ptr++; |
| if ((marker & 0xf0) != kCFBinaryPlistMarkerInt) FAIL_FALSE; |
| uint64_t cnt = 1 << (marker & 0x0f); |
| int32_t err = CF_NO_ERROR; |
| const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1; |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| if (end_byte_ptr < extent) FAIL_FALSE; |
| // integers are not required to be in the most compact possible representation, but only the last 64 bits are significant currently |
| *bigint = 0; |
| for (CFIndex idx = 0; idx < cnt; idx++) { |
| *bigint = (*bigint << 8) + *ptr++; |
| } |
| if (newptr) *newptr = ptr; |
| return true; |
| } |
| |
| // bytesptr points at a ref |
| CF_INLINE uint64_t _getOffsetOfRefAt(const uint8_t *databytes, const uint8_t *bytesptr, const CFBinaryPlistTrailer *trailer) { |
| // *trailer contents are trusted, even for overflows -- was checked when the trailer was parsed; |
| // this pointer arithmetic and the multiplication was also already done once and checked, |
| // and the offsetTable was already validated. |
| const uint8_t *objectsFirstByte = databytes + 8; |
| const uint8_t *offsetsFirstByte = databytes + trailer->_offsetTableOffset; |
| if (bytesptr < objectsFirstByte || offsetsFirstByte - trailer->_objectRefSize < bytesptr) FAIL_MAXOFFSET; |
| |
| uint64_t ref = 0; |
| for (CFIndex idx = 0; idx < trailer->_objectRefSize; idx++) { |
| ref = (ref << 8) + bytesptr[idx]; |
| } |
| if (trailer->_numObjects <= ref) FAIL_MAXOFFSET; |
| |
| bytesptr = databytes + trailer->_offsetTableOffset + ref * trailer->_offsetIntSize; |
| uint64_t off = 0; |
| for (CFIndex idx = 0; idx < trailer->_offsetIntSize; idx++) { |
| off = (off << 8) + bytesptr[idx]; |
| } |
| return off; |
| } |
| |
| static bool __CFBinaryPlistCreateObject2(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFAllocatorRef allocator, CFOptionFlags mutabilityOption, CFMutableDictionaryRef objects, CFMutableSetRef set, CFIndex curDepth, CFPropertyListRef *plist); |
| |
| bool __CFBinaryPlistGetOffsetForValueFromArray2(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFIndex idx, uint64_t *offset, CFMutableDictionaryRef objects) { |
| uint64_t objectsRangeStart = 8, objectsRangeEnd = trailer->_offsetTableOffset - 1; |
| if (startOffset < objectsRangeStart || objectsRangeEnd < startOffset) FAIL_FALSE; |
| const uint8_t *ptr = databytes + startOffset; |
| uint8_t marker = *ptr; |
| if ((marker & 0xf0) != kCFBinaryPlistMarkerArray) FAIL_FALSE; |
| int32_t err = CF_NO_ERROR; |
| ptr = check_ptr_add(ptr, 1, &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| uint64_t cnt = (marker & 0x0f); |
| if (0xf == cnt) { |
| uint64_t bigint; |
| if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE; |
| if (LONG_MAX < bigint) FAIL_FALSE; |
| cnt = bigint; |
| } |
| if (cnt <= idx) FAIL_FALSE; |
| size_t byte_cnt = check_size_t_mul(cnt, trailer->_objectRefSize, &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| const uint8_t *extent = check_ptr_add(ptr, byte_cnt, &err) - 1; |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| if (databytes + objectsRangeEnd < extent) FAIL_FALSE; |
| uint64_t off = _getOffsetOfRefAt(databytes, ptr + idx * trailer->_objectRefSize, trailer); |
| if (offset) *offset = off; |
| return true; |
| } |
| |
| bool __CFBinaryPlistGetOffsetForValueFromDictionary2(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFTypeRef key, uint64_t *koffset, uint64_t *voffset, CFMutableDictionaryRef objects) { |
| if (!key || !_plistIsPrimitive(key)) FAIL_FALSE; |
| uint64_t objectsRangeStart = 8, objectsRangeEnd = trailer->_offsetTableOffset - 1; |
| if (startOffset < objectsRangeStart || objectsRangeEnd < startOffset) FAIL_FALSE; |
| const uint8_t *ptr = databytes + startOffset; |
| uint8_t marker = *ptr; |
| if ((marker & 0xf0) != kCFBinaryPlistMarkerDict) FAIL_FALSE; |
| int32_t err = CF_NO_ERROR; |
| ptr = check_ptr_add(ptr, 1, &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| uint64_t cnt = (marker & 0x0f); |
| if (0xf == cnt) { |
| uint64_t bigint = 0; |
| if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE; |
| if (LONG_MAX < bigint) FAIL_FALSE; |
| cnt = bigint; |
| } |
| cnt = check_size_t_mul(cnt, 2, &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| size_t byte_cnt = check_size_t_mul(cnt, trailer->_objectRefSize, &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| const uint8_t *extent = check_ptr_add(ptr, byte_cnt, &err) - 1; |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| if (databytes + objectsRangeEnd < extent) FAIL_FALSE; |
| CFIndex stringKeyLen = -1; |
| UniChar ubuffer[16]; |
| if (__CFGenericTypeID_genericobj_inline(key) == stringtype) { |
| stringKeyLen = CFStringGetLength((CFStringRef)key); |
| if (stringKeyLen < 0xf) { |
| CFStringGetCharacters((CFStringRef)key, CFRangeMake(0, stringKeyLen), ubuffer); |
| } |
| } |
| cnt = cnt / 2; |
| for (CFIndex idx = 0; idx < cnt; idx++) { |
| uint64_t off = _getOffsetOfRefAt(databytes, ptr, trailer); |
| uint8_t marker = *(databytes + off); |
| CFIndex len = marker & 0x0f; |
| // if it is a short ascii string in the data, and the key is a string |
| if ((marker & 0xf0) == kCFBinaryPlistMarkerASCIIString && len < 0xf && stringKeyLen != -1) { |
| if (len != stringKeyLen) goto miss; |
| err = CF_NO_ERROR; |
| const uint8_t *ptr2 = databytes + off; |
| extent = check_ptr_add(ptr2, len, &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| if (databytes + trailer->_offsetTableOffset <= extent) FAIL_FALSE; |
| for (CFIndex idx2 = 0; idx2 < stringKeyLen; idx2++) { |
| if ((UniChar)ptr2[idx2 + 1] != ubuffer[idx2]) goto miss; |
| } |
| if (koffset) *koffset = off; |
| if (voffset) { |
| off = _getOffsetOfRefAt(databytes, ptr + cnt * trailer->_objectRefSize, trailer); |
| *voffset = off; |
| } |
| return true; |
| miss:; |
| } else { |
| CFPropertyListRef pl = NULL; |
| if (!__CFBinaryPlistCreateObject2(databytes, datalen, off, trailer, kCFAllocatorSystemDefault, kCFPropertyListImmutable, objects, NULL, 0, &pl) || !_plistIsPrimitive(pl)) { |
| if (pl) CFRelease(pl); |
| FAIL_FALSE; |
| } |
| if (CFEqual(key, pl)) { |
| CFRelease(pl); |
| if (koffset) *koffset = off; |
| if (voffset) { |
| off = _getOffsetOfRefAt(databytes, ptr + cnt * trailer->_objectRefSize, trailer); |
| *voffset = off; |
| } |
| return true; |
| } |
| CFRelease(pl); |
| } |
| ptr += trailer->_objectRefSize; |
| } |
| return false; |
| } |
| |
| extern CFArrayRef _CFArrayCreate_ex(CFAllocatorRef allocator, Boolean isMutable, const void **values, CFIndex numValues); |
| extern CFSetRef _CFSetCreate_ex(CFAllocatorRef allocator, Boolean isMutable, const void **values, CFIndex numValues); |
| extern CFDictionaryRef _CFDictionaryCreate_ex(CFAllocatorRef allocator, Boolean isMutable, const void **keys, const void **values, CFIndex numValues); |
| |
| static bool __CFBinaryPlistCreateObject2(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFAllocatorRef allocator, CFOptionFlags mutabilityOption, CFMutableDictionaryRef objects, CFMutableSetRef set, CFIndex curDepth, CFPropertyListRef *plist) { |
| |
| if (objects) { |
| *plist = CFDictionaryGetValue(objects, (const void *)(uintptr_t)startOffset); |
| if (*plist) { |
| CFRetain(*plist); |
| return true; |
| } |
| } |
| |
| // at any one invocation of this function, set should contain the offsets in the "path" down to this object |
| if (set && CFSetContainsValue(set, (const void *)(uintptr_t)startOffset)) return false; |
| |
| // databytes is trusted to be at least datalen bytes long |
| // *trailer contents are trusted, even for overflows -- was checked when the trailer was parsed |
| uint64_t objectsRangeStart = 8, objectsRangeEnd = trailer->_offsetTableOffset - 1; |
| if (startOffset < objectsRangeStart || objectsRangeEnd < startOffset) FAIL_FALSE; |
| |
| uint64_t off; |
| CFPropertyListRef *list, buffer[256]; |
| CFAllocatorRef listAllocator; |
| |
| uint8_t marker = *(databytes + startOffset); |
| switch (marker & 0xf0) { |
| case kCFBinaryPlistMarkerNull: |
| switch (marker) { |
| case kCFBinaryPlistMarkerNull: |
| *plist = kCFNull; |
| return true; |
| case kCFBinaryPlistMarkerFalse: |
| *plist = CFRetain(kCFBooleanFalse); |
| return true; |
| case kCFBinaryPlistMarkerTrue: |
| *plist = CFRetain(kCFBooleanTrue); |
| return true; |
| } |
| FAIL_FALSE; |
| case kCFBinaryPlistMarkerInt: |
| { |
| const uint8_t *ptr = (databytes + startOffset); |
| int32_t err = CF_NO_ERROR; |
| ptr = check_ptr_add(ptr, 1, &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| uint64_t cnt = 1 << (marker & 0x0f); |
| const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1; |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| if (databytes + objectsRangeEnd < extent) FAIL_FALSE; |
| if (16 < cnt) FAIL_FALSE; |
| // in format version '00', 1, 2, and 4-byte integers have to be interpreted as unsigned, |
| // whereas 8-byte integers are signed (and 16-byte when available) |
| // negative 1, 2, 4-byte integers are always emitted as 8 bytes in format '00' |
| uint64_t bigint = 0; |
| // integers are not required to be in the most compact possible representation, but only the last 64 bits are significant currently |
| for (CFIndex idx = 0; idx < cnt; idx++) { |
| bigint = (bigint << 8) + *ptr++; |
| } |
| if (8 < cnt) { |
| CFSInt128Struct val; |
| val.high = 0; |
| val.low = bigint; |
| *plist = CFNumberCreate(allocator, kCFNumberSInt128Type, &val); |
| } else { |
| *plist = CFNumberCreate(allocator, kCFNumberSInt64Type, &bigint); |
| } |
| // these are always immutable |
| if (objects && *plist) { |
| CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist); |
| } |
| return (*plist) ? true : false; |
| } |
| case kCFBinaryPlistMarkerReal: |
| switch (marker & 0x0f) { |
| case 2: { |
| const uint8_t *ptr = (databytes + startOffset); |
| int32_t err = CF_NO_ERROR; |
| ptr = check_ptr_add(ptr, 1, &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| const uint8_t *extent = check_ptr_add(ptr, 4, &err) - 1; |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| if (databytes + objectsRangeEnd < extent) FAIL_FALSE; |
| CFSwappedFloat32 swapped32; |
| memmove(&swapped32, ptr, 4); |
| float f = CFConvertFloat32SwappedToHost(swapped32); |
| *plist = CFNumberCreate(allocator, kCFNumberFloat32Type, &f); |
| // these are always immutable |
| if (objects && *plist) { |
| CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist); |
| } |
| return (*plist) ? true : false; |
| } |
| case 3: { |
| const uint8_t *ptr = (databytes + startOffset); |
| int32_t err = CF_NO_ERROR; |
| ptr = check_ptr_add(ptr, 1, &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| const uint8_t *extent = check_ptr_add(ptr, 8, &err) - 1; |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| if (databytes + objectsRangeEnd < extent) FAIL_FALSE; |
| CFSwappedFloat64 swapped64; |
| memmove(&swapped64, ptr, 8); |
| double d = CFConvertFloat64SwappedToHost(swapped64); |
| *plist = CFNumberCreate(allocator, kCFNumberFloat64Type, &d); |
| // these are always immutable |
| if (objects && *plist) { |
| CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist); |
| } |
| return (*plist) ? true : false; |
| } |
| } |
| FAIL_FALSE; |
| case kCFBinaryPlistMarkerDate & 0xf0: |
| switch (marker) { |
| case kCFBinaryPlistMarkerDate: { |
| const uint8_t *ptr = (databytes + startOffset); |
| int32_t err = CF_NO_ERROR; |
| ptr = check_ptr_add(ptr, 1, &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| const uint8_t *extent = check_ptr_add(ptr, 8, &err) - 1; |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| if (databytes + objectsRangeEnd < extent) FAIL_FALSE; |
| CFSwappedFloat64 swapped64; |
| memmove(&swapped64, ptr, 8); |
| double d = CFConvertFloat64SwappedToHost(swapped64); |
| *plist = CFDateCreate(allocator, d); |
| // these are always immutable |
| if (objects && *plist) { |
| CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist); |
| } |
| return (*plist) ? true : false; |
| } |
| } |
| FAIL_FALSE; |
| case kCFBinaryPlistMarkerData: { |
| const uint8_t *ptr = databytes + startOffset; |
| int32_t err = CF_NO_ERROR; |
| ptr = check_ptr_add(ptr, 1, &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| CFIndex cnt = marker & 0x0f; |
| if (0xf == cnt) { |
| uint64_t bigint = 0; |
| if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE; |
| if (LONG_MAX < bigint) FAIL_FALSE; |
| cnt = (CFIndex)bigint; |
| } |
| const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1; |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| if (databytes + objectsRangeEnd < extent) FAIL_FALSE; |
| if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) { |
| *plist = CFDataCreateMutable(allocator, 0); |
| if (*plist) CFDataAppendBytes((CFMutableDataRef)*plist, ptr, cnt); |
| } else { |
| *plist = CFDataCreate(allocator, ptr, cnt); |
| } |
| if (objects && *plist && (mutabilityOption != kCFPropertyListMutableContainersAndLeaves)) { |
| CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist); |
| } |
| return (*plist) ? true : false; |
| } |
| case kCFBinaryPlistMarkerASCIIString: { |
| const uint8_t *ptr = databytes + startOffset; |
| int32_t err = CF_NO_ERROR; |
| ptr = check_ptr_add(ptr, 1, &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| CFIndex cnt = marker & 0x0f; |
| if (0xf == cnt) { |
| uint64_t bigint = 0; |
| if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE; |
| if (LONG_MAX < bigint) FAIL_FALSE; |
| cnt = (CFIndex)bigint; |
| } |
| const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1; |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| if (databytes + objectsRangeEnd < extent) FAIL_FALSE; |
| if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) { |
| CFStringRef str = CFStringCreateWithBytes(allocator, ptr, cnt, kCFStringEncodingASCII, false); |
| *plist = str ? CFStringCreateMutableCopy(allocator, 0, str) : NULL; |
| if (str) CFRelease(str); |
| } else { |
| *plist = CFStringCreateWithBytes(allocator, ptr, cnt, kCFStringEncodingASCII, false); |
| } |
| if (objects && *plist && (mutabilityOption != kCFPropertyListMutableContainersAndLeaves)) { |
| CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist); |
| } |
| return (*plist) ? true : false; |
| } |
| case kCFBinaryPlistMarkerUnicode16String: { |
| const uint8_t *ptr = databytes + startOffset; |
| int32_t err = CF_NO_ERROR; |
| ptr = check_ptr_add(ptr, 1, &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| CFIndex cnt = marker & 0x0f; |
| if (0xf == cnt) { |
| uint64_t bigint = 0; |
| if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE; |
| if (LONG_MAX < bigint) FAIL_FALSE; |
| cnt = (CFIndex)bigint; |
| } |
| const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1; |
| extent = check_ptr_add(extent, cnt, &err); // 2 bytes per character |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| if (databytes + objectsRangeEnd < extent) FAIL_FALSE; |
| size_t byte_cnt = check_size_t_mul(cnt, sizeof(UniChar), &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| UniChar *chars = (UniChar *)CFAllocatorAllocate(allocator, byte_cnt, 0); |
| if (!chars) FAIL_FALSE; |
| memmove(chars, ptr, byte_cnt); |
| for (CFIndex idx = 0; idx < cnt; idx++) { |
| chars[idx] = CFSwapInt16BigToHost(chars[idx]); |
| } |
| if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) { |
| CFStringRef str = CFStringCreateWithCharactersNoCopy(allocator, chars, cnt, allocator); |
| *plist = str ? CFStringCreateMutableCopy(allocator, 0, str) : NULL; |
| if (str) CFRelease(str); |
| } else { |
| *plist = CFStringCreateWithCharactersNoCopy(allocator, chars, cnt, allocator); |
| } |
| if (objects && *plist && (mutabilityOption != kCFPropertyListMutableContainersAndLeaves)) { |
| CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist); |
| } |
| return (*plist) ? true : false; |
| } |
| case kCFBinaryPlistMarkerUID: { |
| const uint8_t *ptr = databytes + startOffset; |
| int32_t err = CF_NO_ERROR; |
| ptr = check_ptr_add(ptr, 1, &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| CFIndex cnt = (marker & 0x0f) + 1; |
| const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1; |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| if (databytes + objectsRangeEnd < extent) FAIL_FALSE; |
| // uids are not required to be in the most compact possible representation, but only the last 64 bits are significant currently |
| uint64_t bigint = 0; |
| for (CFIndex idx = 0; idx < cnt; idx++) { |
| bigint = (bigint << 8) + *ptr++; |
| } |
| if (UINT32_MAX < bigint) FAIL_FALSE; |
| *plist = _CFKeyedArchiverUIDCreate(allocator, (uint32_t)bigint); |
| // these are always immutable |
| if (objects && *plist) { |
| CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist); |
| } |
| return (*plist) ? true : false; |
| } |
| case kCFBinaryPlistMarkerArray: |
| case kCFBinaryPlistMarkerSet: { |
| const uint8_t *ptr = databytes + startOffset; |
| int32_t err = CF_NO_ERROR; |
| ptr = check_ptr_add(ptr, 1, &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| CFIndex cnt = marker & 0x0f; |
| if (0xf == cnt) { |
| uint64_t bigint = 0; |
| if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE; |
| if (LONG_MAX < bigint) FAIL_FALSE; |
| cnt = (CFIndex)bigint; |
| } |
| size_t byte_cnt = check_size_t_mul(cnt, trailer->_objectRefSize, &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| const uint8_t *extent = check_ptr_add(ptr, byte_cnt, &err) - 1; |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| if (databytes + objectsRangeEnd < extent) FAIL_FALSE; |
| byte_cnt = check_size_t_mul(cnt, sizeof(CFPropertyListRef), &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| list = (cnt <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, byte_cnt, __kCFAllocatorGCScannedMemory); |
| listAllocator = (list == buffer ? kCFAllocatorNull : kCFAllocatorSystemDefault); |
| if (!list) FAIL_FALSE; |
| Boolean madeSet = false; |
| if (!set && 15 < curDepth) { |
| set = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, NULL); |
| madeSet = set ? true : false; |
| } |
| if (set) CFSetAddValue(set, (const void *)(uintptr_t)startOffset); |
| for (CFIndex idx = 0; idx < cnt; idx++) { |
| CFPropertyListRef pl; |
| off = _getOffsetOfRefAt(databytes, ptr, trailer); |
| if (!__CFBinaryPlistCreateObject2(databytes, datalen, off, trailer, allocator, mutabilityOption, objects, set, curDepth + 1, &pl)) { |
| if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { |
| while (idx--) { |
| CFRelease(list[idx]); |
| } |
| } |
| if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); |
| FAIL_FALSE; |
| } |
| if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { |
| CF_WRITE_BARRIER_BASE_ASSIGN(listAllocator, list, list[idx], CFMakeCollectable(pl)); |
| } else { |
| list[idx] = pl; |
| } |
| ptr += trailer->_objectRefSize; |
| } |
| if (set) CFSetRemoveValue(set, (const void *)(uintptr_t)startOffset); |
| if (madeSet) { |
| CFRelease(set); |
| set = NULL; |
| } |
| if ((marker & 0xf0) == kCFBinaryPlistMarkerArray) { |
| *plist = _CFArrayCreate_ex(allocator, (mutabilityOption != kCFPropertyListImmutable), list, cnt); |
| } else { |
| *plist = _CFSetCreate_ex(allocator, (mutabilityOption != kCFPropertyListImmutable), list, cnt); |
| } |
| if (objects && *plist && (mutabilityOption == kCFPropertyListImmutable)) { |
| CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist); |
| } |
| if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); |
| return (*plist) ? true : false; |
| } |
| case kCFBinaryPlistMarkerDict: { |
| const uint8_t *ptr = databytes + startOffset; |
| int32_t err = CF_NO_ERROR; |
| ptr = check_ptr_add(ptr, 1, &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| CFIndex cnt = marker & 0x0f; |
| if (0xf == cnt) { |
| uint64_t bigint = 0; |
| if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE; |
| if (LONG_MAX < bigint) FAIL_FALSE; |
| cnt = (CFIndex)bigint; |
| } |
| cnt = (CFIndex)check_size_t_mul(cnt, 2, &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| size_t byte_cnt = check_size_t_mul(cnt, trailer->_objectRefSize, &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| const uint8_t *extent = check_ptr_add(ptr, byte_cnt, &err) - 1; |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| if (databytes + objectsRangeEnd < extent) FAIL_FALSE; |
| byte_cnt = check_size_t_mul(cnt, sizeof(CFPropertyListRef), &err); |
| if (CF_NO_ERROR != err) FAIL_FALSE; |
| list = (cnt <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, byte_cnt, __kCFAllocatorGCScannedMemory); |
| listAllocator = (list == buffer ? kCFAllocatorNull : kCFAllocatorSystemDefault); |
| if (!list) FAIL_FALSE; |
| Boolean madeSet = false; |
| if (!set && 15 < curDepth) { |
| set = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, NULL); |
| madeSet = set ? true : false; |
| } |
| if (set) CFSetAddValue(set, (const void *)(uintptr_t)startOffset); |
| for (CFIndex idx = 0; idx < cnt; idx++) { |
| CFPropertyListRef pl = NULL; |
| off = _getOffsetOfRefAt(databytes, ptr, trailer); |
| if (!__CFBinaryPlistCreateObject2(databytes, datalen, off, trailer, allocator, mutabilityOption, objects, set, curDepth + 1, &pl) || (idx < cnt / 2 && !_plistIsPrimitive(pl))) { |
| if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { |
| if (pl) CFRelease(pl); |
| while (idx--) { |
| CFRelease(list[idx]); |
| } |
| } |
| if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); |
| FAIL_FALSE; |
| } |
| if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { |
| CF_WRITE_BARRIER_BASE_ASSIGN(listAllocator, list, list[idx], CFMakeCollectable(pl)); |
| } else { |
| list[idx] = pl; |
| } |
| ptr += trailer->_objectRefSize; |
| } |
| if (set) CFSetRemoveValue(set, (const void *)(uintptr_t)startOffset); |
| if (madeSet) { |
| CFRelease(set); |
| set = NULL; |
| } |
| *plist = _CFDictionaryCreate_ex(allocator, (mutabilityOption != kCFPropertyListImmutable), list, list + cnt / 2, cnt / 2); |
| if (objects && *plist && (mutabilityOption == kCFPropertyListImmutable)) { |
| CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist); |
| } |
| if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); |
| return (*plist) ? true : false; |
| } |
| } |
| FAIL_FALSE; |
| } |
| |
| bool __CFBinaryPlistCreateObject(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFAllocatorRef allocator, CFOptionFlags mutabilityOption, CFMutableDictionaryRef objects, CFPropertyListRef *plist) { |
| // for compatibility with Foundation's use, need to leave this here |
| return __CFBinaryPlistCreateObject2(databytes, datalen, startOffset, trailer, allocator, mutabilityOption, objects, NULL, 0, plist); |
| } |
| |
| __private_extern__ bool __CFTryParseBinaryPlist(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags option, CFPropertyListRef *plist, CFStringRef *errorString) { |
| uint8_t marker; |
| CFBinaryPlistTrailer trailer; |
| uint64_t offset; |
| const uint8_t *databytes = CFDataGetBytePtr(data); |
| uint64_t datalen = CFDataGetLength(data); |
| |
| if (8 <= datalen && __CFBinaryPlistGetTopLevelInfo(databytes, datalen, &marker, &offset, &trailer)) { |
| // FALSE: We know for binary plist parsing that the result objects will be retained |
| // by their containing collections as the parsing proceeds, so we do not need |
| // to use retaining callbacks for the objects map in this case. WHY: the file might |
| // be malformed and contain hash-equal keys for the same dictionary (for example) |
| // and the later key will cause the previous one to be released when we set the second |
| // in the dictionary. |
| CFMutableDictionaryRef objects = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks); |
| _CFDictionarySetCapacity(objects, 4000); |
| CFPropertyListRef pl = NULL; |
| if (__CFBinaryPlistCreateObject2(databytes, datalen, offset, &trailer, allocator, option, objects, NULL, 0, &pl)) { |
| if (plist) *plist = pl; |
| } else { |
| if (plist) *plist = NULL; |
| if (errorString) *errorString = (CFStringRef)CFRetain(CFSTR("binary data is corrupt")); |
| } |
| CFRelease(objects); |
| return true; |
| } |
| return false; |
| } |
| |