--- /dev/null
+/*
+ * 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 <stdio.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 {
+ CFAssert(false, __kCFLogAssertion, "Streams are not supported on this platform");
+ }
+}
+
+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 = 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;
+}
+