]> git.saurik.com Git - apple/cf.git/blob - CFBinaryPList.c
CF-744.tar.gz
[apple/cf.git] / CFBinaryPList.c
1 /*
2 * Copyright (c) 2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /* CFBinaryPList.c
25 Copyright (c) 2000-2012, Apple Inc. All rights reserved.
26 Responsibility: Tony Parker
27 */
28
29
30 #include <CoreFoundation/CFString.h>
31 #include <CoreFoundation/CFNumber.h>
32 #include <CoreFoundation/CFDate.h>
33 #include <CoreFoundation/CFData.h>
34 #include <CoreFoundation/CFError.h>
35 #include <CoreFoundation/CFArray.h>
36 #include <CoreFoundation/CFDictionary.h>
37 #include <CoreFoundation/CFSet.h>
38 #include <CoreFoundation/CFPropertyList.h>
39 #include <CoreFoundation/CFByteOrder.h>
40 #include <CoreFoundation/CFRuntime.h>
41 #include <CoreFoundation/CFUUID.h>
42 #include <stdio.h>
43 #include <limits.h>
44 #include <string.h>
45 #include "CFInternal.h"
46 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
47 #include <CoreFoundation/CFStream.h>
48 #endif
49
50 typedef struct {
51 int64_t high;
52 uint64_t low;
53 } CFSInt128Struct;
54
55 enum {
56 kCFNumberSInt128Type = 17
57 };
58
59 enum {
60 CF_NO_ERROR = 0,
61 CF_OVERFLOW_ERROR = (1 << 0),
62 };
63
64 CF_INLINE uint32_t __check_uint32_add_unsigned_unsigned(uint32_t x, uint32_t y, int32_t* err) {
65 if((UINT_MAX - y) < x)
66 *err = *err | CF_OVERFLOW_ERROR;
67 return x + y;
68 };
69
70 CF_INLINE uint64_t __check_uint64_add_unsigned_unsigned(uint64_t x, uint64_t y, int32_t* err) {
71 if((ULLONG_MAX - y) < x)
72 *err = *err | CF_OVERFLOW_ERROR;
73 return x + y;
74 };
75
76 CF_INLINE uint32_t __check_uint32_mul_unsigned_unsigned(uint32_t x, uint32_t y, int32_t* err) {
77 uint64_t tmp = (uint64_t) x * (uint64_t) y;
78 /* If any of the upper 32 bits touched, overflow */
79 if(tmp & 0xffffffff00000000ULL)
80 *err = *err | CF_OVERFLOW_ERROR;
81 return (uint32_t) tmp;
82 };
83
84 CF_INLINE uint64_t __check_uint64_mul_unsigned_unsigned(uint64_t x, uint64_t y, int32_t* err) {
85 if(x == 0) return 0;
86 if(ULLONG_MAX/x < y)
87 *err = *err | CF_OVERFLOW_ERROR;
88 return x * y;
89 };
90
91 #if __LP64__
92 #define check_ptr_add(p, a, err) (const uint8_t *)__check_uint64_add_unsigned_unsigned((uintptr_t)p, (uintptr_t)a, err)
93 #define check_size_t_mul(b, a, err) (size_t)__check_uint64_mul_unsigned_unsigned((size_t)b, (size_t)a, err)
94 #else
95 #define check_ptr_add(p, a, err) (const uint8_t *)__check_uint32_add_unsigned_unsigned((uintptr_t)p, (uintptr_t)a, err)
96 #define check_size_t_mul(b, a, err) (size_t)__check_uint32_mul_unsigned_unsigned((size_t)b, (size_t)a, err)
97 #endif
98
99 #pragma mark -
100 #pragma mark Keyed Archiver UID
101
102 struct __CFKeyedArchiverUID {
103 CFRuntimeBase _base;
104 uint32_t _value;
105 };
106
107 static CFStringRef __CFKeyedArchiverUIDCopyDescription(CFTypeRef cf) {
108 CFKeyedArchiverUIDRef uid = (CFKeyedArchiverUIDRef)cf;
109 return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFKeyedArchiverUID %p [%p]>{value = %u}"), cf, CFGetAllocator(cf), uid->_value);
110 }
111
112 static CFStringRef __CFKeyedArchiverUIDCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
113 CFKeyedArchiverUIDRef uid = (CFKeyedArchiverUIDRef)cf;
114 return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("@%u@"), uid->_value);
115 }
116
117 static CFTypeID __kCFKeyedArchiverUIDTypeID = _kCFRuntimeNotATypeID;
118
119 static const CFRuntimeClass __CFKeyedArchiverUIDClass = {
120 0,
121 "CFKeyedArchiverUID",
122 NULL, // init
123 NULL, // copy
124 NULL, // finalize
125 NULL, // equal -- pointer equality only
126 NULL, // hash -- pointer hashing only
127 __CFKeyedArchiverUIDCopyFormattingDescription,
128 __CFKeyedArchiverUIDCopyDescription
129 };
130
131 __private_extern__ void __CFKeyedArchiverUIDInitialize(void) {
132 __kCFKeyedArchiverUIDTypeID = _CFRuntimeRegisterClass(&__CFKeyedArchiverUIDClass);
133 }
134
135 CFTypeID _CFKeyedArchiverUIDGetTypeID(void) {
136 return __kCFKeyedArchiverUIDTypeID;
137 }
138
139 CFKeyedArchiverUIDRef _CFKeyedArchiverUIDCreate(CFAllocatorRef allocator, uint32_t value) {
140 CFKeyedArchiverUIDRef uid;
141 uid = (CFKeyedArchiverUIDRef)_CFRuntimeCreateInstance(allocator, __kCFKeyedArchiverUIDTypeID, sizeof(struct __CFKeyedArchiverUID) - sizeof(CFRuntimeBase), NULL);
142 if (NULL == uid) {
143 return NULL;
144 }
145 ((struct __CFKeyedArchiverUID *)uid)->_value = value;
146 return uid;
147 }
148
149
150 uint32_t _CFKeyedArchiverUIDGetValue(CFKeyedArchiverUIDRef uid) {
151 return uid->_value;
152 }
153
154 #pragma mark -
155 #pragma mark Writing
156
157 __private_extern__ CFErrorRef __CFPropertyListCreateError(CFIndex code, CFStringRef debugString, ...);
158
159 typedef struct {
160 CFTypeRef stream;
161 CFErrorRef error;
162 uint64_t written;
163 int32_t used;
164 bool streamIsData;
165 uint8_t buffer[8192 - 32];
166 } __CFBinaryPlistWriteBuffer;
167
168 static void writeBytes(__CFBinaryPlistWriteBuffer *buf, const UInt8 *bytes, CFIndex length) {
169 if (0 == length) return;
170 if (buf->error) return;
171 if (buf->streamIsData) {
172 CFDataAppendBytes((CFMutableDataRef)buf->stream, bytes, length);
173 buf->written += length;
174 } else {
175 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
176 while (0 < length) {
177 CFIndex ret = CFWriteStreamWrite((CFWriteStreamRef)buf->stream, bytes, length);
178 if (ret == 0) {
179 buf->error = __CFPropertyListCreateError(kCFPropertyListWriteStreamError, CFSTR("Binary property list writing could not be completed because stream is full."));
180 return;
181 }
182 if (ret < 0) {
183 CFErrorRef err = CFWriteStreamCopyError((CFWriteStreamRef)buf->stream);
184 buf->error = err ? err : __CFPropertyListCreateError(kCFPropertyListWriteStreamError, CFSTR("Binary property list writing could not be completed because the stream had an unknown error."));
185 return;
186 }
187 buf->written += ret;
188 length -= ret;
189 bytes += ret;
190 }
191 #else
192 CFAssert(false, __kCFLogAssertion, "Streams are not supported on this platform");
193 #endif
194 }
195 }
196
197 static void bufferWrite(__CFBinaryPlistWriteBuffer *buf, const uint8_t *buffer, CFIndex count) {
198 if (0 == count) return;
199 if ((CFIndex)sizeof(buf->buffer) <= count) {
200 writeBytes(buf, buf->buffer, buf->used);
201 buf->used = 0;
202 writeBytes(buf, buffer, count);
203 return;
204 }
205 CFIndex copyLen = __CFMin(count, (CFIndex)sizeof(buf->buffer) - buf->used);
206 memmove(buf->buffer + buf->used, buffer, copyLen);
207 buf->used += copyLen;
208 if (sizeof(buf->buffer) == buf->used) {
209 writeBytes(buf, buf->buffer, sizeof(buf->buffer));
210 memmove(buf->buffer, buffer + copyLen, count - copyLen);
211 buf->used = count - copyLen;
212 }
213 }
214
215 static void bufferFlush(__CFBinaryPlistWriteBuffer *buf) {
216 writeBytes(buf, buf->buffer, buf->used);
217 buf->used = 0;
218 }
219
220 /*
221 HEADER
222 magic number ("bplist")
223 file format version
224 byte length of plist incl. header, an encoded int number object (as below) [v.2+ only]
225 32-bit CRC (ISO/IEC 8802-3:1989) of plist bytes w/o CRC, encoded always as
226 "0x12 0x__ 0x__ 0x__ 0x__", big-endian, may be 0 to indicate no CRC [v.2+ only]
227
228 OBJECT TABLE
229 variable-sized objects
230
231 Object Formats (marker byte followed by additional info in some cases)
232 null 0000 0000 // null object [v1+ only]
233 bool 0000 1000 // false
234 bool 0000 1001 // true
235 url 0000 1100 string // URL with no base URL, recursive encoding of URL string [v1+ only]
236 url 0000 1101 base string // URL with base URL, recursive encoding of base URL, then recursive encoding of URL string [v1+ only]
237 uuid 0000 1110 // 16-byte UUID [v1+ only]
238 fill 0000 1111 // fill byte
239 int 0001 0nnn ... // # of bytes is 2^nnn, big-endian bytes
240 real 0010 0nnn ... // # of bytes is 2^nnn, big-endian bytes
241 date 0011 0011 ... // 8 byte float follows, big-endian bytes
242 data 0100 nnnn [int] ... // nnnn is number of bytes unless 1111 then int count follows, followed by bytes
243 string 0101 nnnn [int] ... // ASCII string, nnnn is # of chars, else 1111 then int count, then bytes
244 string 0110 nnnn [int] ... // Unicode string, nnnn is # of chars, else 1111 then int count, then big-endian 2-byte uint16_t
245 0111 xxxx // unused
246 uid 1000 nnnn ... // nnnn+1 is # of bytes
247 1001 xxxx // unused
248 array 1010 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows
249 ordset 1011 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows [v1+ only]
250 set 1100 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows [v1+ only]
251 dict 1101 nnnn [int] keyref* objref* // nnnn is count, unless '1111', then int count follows
252 1110 xxxx // unused
253 1111 xxxx // unused
254
255 OFFSET TABLE
256 list of ints, byte size of which is given in trailer
257 -- these are the byte offsets into the file
258 -- number of these is in the trailer
259
260 TRAILER
261 byte size of offset ints in offset table
262 byte size of object refs in arrays and dicts
263 number of offsets in offset table (also is number of objects)
264 element # in offset table which is top level object
265 offset table offset
266
267 Version 1.5 binary plists do not use object references (uid),
268 but instead inline the object serialization itself at that point.
269 It also doesn't use an offset table or a trailer. It does have
270 an extended header, and the top-level object follows the header.
271
272 */
273
274
275 static CFTypeID stringtype = -1, datatype = -1, numbertype = -1, datetype = -1;
276 static CFTypeID booltype = -1, nulltype = -1, dicttype = -1, arraytype = -1;
277 static CFTypeID uuidtype = -1, urltype = -1, osettype = -1, settype = -1;
278
279 static void initStatics() {
280 if ((CFTypeID)-1 == stringtype) {
281 stringtype = CFStringGetTypeID();
282 }
283 if ((CFTypeID)-1 == datatype) {
284 datatype = CFDataGetTypeID();
285 }
286 if ((CFTypeID)-1 == numbertype) {
287 numbertype = CFNumberGetTypeID();
288 }
289 if ((CFTypeID)-1 == booltype) {
290 booltype = CFBooleanGetTypeID();
291 }
292 if ((CFTypeID)-1 == datetype) {
293 datetype = CFDateGetTypeID();
294 }
295 if ((CFTypeID)-1 == dicttype) {
296 dicttype = CFDictionaryGetTypeID();
297 }
298 if ((CFTypeID)-1 == arraytype) {
299 arraytype = CFArrayGetTypeID();
300 }
301 if ((CFTypeID)-1 == settype) {
302 settype = CFSetGetTypeID();
303 }
304 if ((CFTypeID)-1 == nulltype) {
305 nulltype = CFNullGetTypeID();
306 }
307 if ((CFTypeID)-1 == uuidtype) {
308 uuidtype = CFUUIDGetTypeID();
309 }
310 if ((CFTypeID)-1 == urltype) {
311 urltype = CFURLGetTypeID();
312 }
313 if ((CFTypeID)-1 == osettype) {
314 osettype = -1;
315 }
316 }
317
318 static void _appendInt(__CFBinaryPlistWriteBuffer *buf, uint64_t bigint) {
319 uint8_t marker;
320 uint8_t *bytes;
321 CFIndex nbytes;
322 if (bigint <= (uint64_t)0xff) {
323 nbytes = 1;
324 marker = kCFBinaryPlistMarkerInt | 0;
325 } else if (bigint <= (uint64_t)0xffff) {
326 nbytes = 2;
327 marker = kCFBinaryPlistMarkerInt | 1;
328 } else if (bigint <= (uint64_t)0xffffffff) {
329 nbytes = 4;
330 marker = kCFBinaryPlistMarkerInt | 2;
331 } else {
332 nbytes = 8;
333 marker = kCFBinaryPlistMarkerInt | 3;
334 }
335 bigint = CFSwapInt64HostToBig(bigint);
336 bytes = (uint8_t *)&bigint + sizeof(bigint) - nbytes;
337 bufferWrite(buf, &marker, 1);
338 bufferWrite(buf, bytes, nbytes);
339 }
340
341 static void _appendUID(__CFBinaryPlistWriteBuffer *buf, CFKeyedArchiverUIDRef uid) {
342 uint8_t marker;
343 uint8_t *bytes;
344 CFIndex nbytes;
345 uint64_t bigint = _CFKeyedArchiverUIDGetValue(uid);
346 if (bigint <= (uint64_t)0xff) {
347 nbytes = 1;
348 } else if (bigint <= (uint64_t)0xffff) {
349 nbytes = 2;
350 } else if (bigint <= (uint64_t)0xffffffff) {
351 nbytes = 4;
352 } else {
353 nbytes = 8;
354 }
355 marker = kCFBinaryPlistMarkerUID | (uint8_t)(nbytes - 1);
356 bigint = CFSwapInt64HostToBig(bigint);
357 bytes = (uint8_t *)&bigint + sizeof(bigint) - nbytes;
358 bufferWrite(buf, &marker, 1);
359 bufferWrite(buf, bytes, nbytes);
360 }
361
362 static void _appendString(__CFBinaryPlistWriteBuffer *buf, CFStringRef str) {
363 CFIndex ret, count = CFStringGetLength(str);
364 CFIndex needed, idx2;
365 uint8_t *bytes, buffer[1024];
366 bytes = (count <= 1024) ? buffer : (uint8_t *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, count, 0);
367 // presumption, believed to be true, is that ASCII encoding may need
368 // less bytes, but will not need greater, than the # of unichars
369 ret = CFStringGetBytes(str, CFRangeMake(0, count), kCFStringEncodingASCII, 0, false, bytes, count, &needed);
370 if (ret == count) {
371 uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerASCIIString | (needed < 15 ? needed : 0xf));
372 bufferWrite(buf, &marker, 1);
373 if (15 <= needed) {
374 _appendInt(buf, (uint64_t)needed);
375 }
376 bufferWrite(buf, bytes, needed);
377 } else {
378 UniChar *chars;
379 uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerUnicode16String | (count < 15 ? count : 0xf));
380 bufferWrite(buf, &marker, 1);
381 if (15 <= count) {
382 _appendInt(buf, (uint64_t)count);
383 }
384 chars = (UniChar *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, count * sizeof(UniChar), 0);
385 CFStringGetCharacters(str, CFRangeMake(0, count), chars);
386 for (idx2 = 0; idx2 < count; idx2++) {
387 chars[idx2] = CFSwapInt16HostToBig(chars[idx2]);
388 }
389 bufferWrite(buf, (uint8_t *)chars, count * sizeof(UniChar));
390 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, chars);
391 }
392 if (bytes != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, bytes);
393 }
394
395 CF_EXPORT CFNumberType _CFNumberGetType2(CFNumberRef number);
396
397 static void _appendNumber(__CFBinaryPlistWriteBuffer *buf, CFNumberRef num) {
398 uint8_t marker;
399 uint64_t bigint;
400 uint8_t *bytes;
401 CFIndex nbytes;
402 if (CFNumberIsFloatType(num)) {
403 CFSwappedFloat64 swapped64;
404 CFSwappedFloat32 swapped32;
405 if (CFNumberGetByteSize(num) <= (CFIndex)sizeof(float)) {
406 float v;
407 CFNumberGetValue(num, kCFNumberFloat32Type, &v);
408 swapped32 = CFConvertFloat32HostToSwapped(v);
409 bytes = (uint8_t *)&swapped32;
410 nbytes = sizeof(float);
411 marker = kCFBinaryPlistMarkerReal | 2;
412 } else {
413 double v;
414 CFNumberGetValue(num, kCFNumberFloat64Type, &v);
415 swapped64 = CFConvertFloat64HostToSwapped(v);
416 bytes = (uint8_t *)&swapped64;
417 nbytes = sizeof(double);
418 marker = kCFBinaryPlistMarkerReal | 3;
419 }
420 bufferWrite(buf, &marker, 1);
421 bufferWrite(buf, bytes, nbytes);
422 } else {
423 CFNumberType type = _CFNumberGetType2(num);
424 if (kCFNumberSInt128Type == type) {
425 CFSInt128Struct s;
426 CFNumberGetValue(num, kCFNumberSInt128Type, &s);
427 struct {
428 int64_t high;
429 uint64_t low;
430 } storage;
431 storage.high = CFSwapInt64HostToBig(s.high);
432 storage.low = CFSwapInt64HostToBig(s.low);
433 uint8_t *bytes = (uint8_t *)&storage;
434 uint8_t marker = kCFBinaryPlistMarkerInt | 4;
435 CFIndex nbytes = 16;
436 bufferWrite(buf, &marker, 1);
437 bufferWrite(buf, bytes, nbytes);
438 } else {
439 CFNumberGetValue(num, kCFNumberSInt64Type, &bigint);
440 _appendInt(buf, bigint);
441 }
442 }
443 }
444
445 // 0 == objRefSize means version 1.5, else version 0.0 or 0.1
446 static Boolean _appendObject(__CFBinaryPlistWriteBuffer *buf, CFTypeRef obj, CFDictionaryRef objtable, uint32_t objRefSize) {
447 uint64_t refnum;
448 CFIndex idx2;
449 CFTypeID type = CFGetTypeID(obj);
450 if (stringtype == type) {
451 _appendString(buf, (CFStringRef)obj);
452 } else if (numbertype == type) {
453 _appendNumber(buf, (CFNumberRef)obj);
454 } else if (booltype == type) {
455 uint8_t marker = CFBooleanGetValue((CFBooleanRef)obj) ? kCFBinaryPlistMarkerTrue : kCFBinaryPlistMarkerFalse;
456 bufferWrite(buf, &marker, 1);
457 } else if (0 == objRefSize && nulltype == type) {
458 uint8_t marker = 0x00;
459 bufferWrite(buf, &marker, 1);
460 } else if (datatype == type) {
461 CFIndex count = CFDataGetLength((CFDataRef)obj);
462 uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerData | (count < 15 ? count : 0xf));
463 bufferWrite(buf, &marker, 1);
464 if (15 <= count) {
465 _appendInt(buf, (uint64_t)count);
466 }
467 bufferWrite(buf, CFDataGetBytePtr((CFDataRef)obj), count);
468 } else if (datetype == type) {
469 CFSwappedFloat64 swapped;
470 uint8_t marker = kCFBinaryPlistMarkerDate;
471 bufferWrite(buf, &marker, 1);
472 swapped = CFConvertFloat64HostToSwapped(CFDateGetAbsoluteTime((CFDateRef)obj));
473 bufferWrite(buf, (uint8_t *)&swapped, sizeof(swapped));
474 } else if (dicttype == type) {
475 CFIndex count = CFDictionaryGetCount((CFDictionaryRef)obj);
476 uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerDict | (count < 15 ? count : 0xf));
477 bufferWrite(buf, &marker, 1);
478 if (15 <= count) {
479 _appendInt(buf, (uint64_t)count);
480 }
481 CFPropertyListRef *list, buffer[512];
482 list = (count <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, 2 * count * sizeof(CFTypeRef), __kCFAllocatorGCScannedMemory);
483 CFDictionaryGetKeysAndValues((CFDictionaryRef)obj, list, list + count);
484 for (idx2 = 0; idx2 < 2 * count; idx2++) {
485 CFPropertyListRef value = list[idx2];
486 if (objtable) {
487 uint32_t swapped = 0;
488 uint8_t *source = (uint8_t *)&swapped;
489 refnum = (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable, value);
490 swapped = CFSwapInt32HostToBig((uint32_t)refnum);
491 bufferWrite(buf, source + sizeof(swapped) - objRefSize, objRefSize);
492 } else {
493 Boolean ret = _appendObject(buf, value, objtable, objRefSize);
494 if (!ret) {
495 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, list);
496 return false;
497 }
498 }
499 }
500 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, list);
501 } else if (arraytype == type) {
502 CFIndex count = CFArrayGetCount((CFArrayRef)obj);
503 CFPropertyListRef *list, buffer[256];
504 uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerArray | (count < 15 ? count : 0xf));
505 bufferWrite(buf, &marker, 1);
506 if (15 <= count) {
507 _appendInt(buf, (uint64_t)count);
508 }
509 list = (count <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, count * sizeof(CFTypeRef), __kCFAllocatorGCScannedMemory);
510 CFArrayGetValues((CFArrayRef)obj, CFRangeMake(0, count), list);
511 for (idx2 = 0; idx2 < count; idx2++) {
512 CFPropertyListRef value = list[idx2];
513 if (objtable) {
514 uint32_t swapped = 0;
515 uint8_t *source = (uint8_t *)&swapped;
516 refnum = (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable, value);
517 swapped = CFSwapInt32HostToBig((uint32_t)refnum);
518 bufferWrite(buf, source + sizeof(swapped) - objRefSize, objRefSize);
519 } else {
520 Boolean ret = _appendObject(buf, value, objtable, objRefSize);
521 if (!ret) {
522 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, list);
523 return false;
524 }
525 }
526 }
527 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, list);
528 } else if (0 == objRefSize && settype == type) {
529 CFIndex count = CFSetGetCount((CFSetRef)obj);
530 CFPropertyListRef *list, buffer[256];
531 uint8_t marker = (uint8_t)(0xc0 | (count < 15 ? count : 0xf));
532 bufferWrite(buf, &marker, 1);
533 if (15 <= count) {
534 _appendInt(buf, (uint64_t)count);
535 }
536 list = (count <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, count * sizeof(CFTypeRef), __kCFAllocatorGCScannedMemory);
537 CFSetGetValues((CFSetRef)obj, list);
538 for (idx2 = 0; idx2 < count; idx2++) {
539 CFPropertyListRef value = list[idx2];
540 if (objtable) {
541 uint32_t swapped = 0;
542 uint8_t *source = (uint8_t *)&swapped;
543 refnum = (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable, value);
544 swapped = CFSwapInt32HostToBig((uint32_t)refnum);
545 bufferWrite(buf, source + sizeof(swapped) - objRefSize, objRefSize);
546 } else {
547 Boolean ret = _appendObject(buf, value, objtable, objRefSize);
548 if (!ret) {
549 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, list);
550 return false;
551 }
552 }
553 }
554 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, list);
555 } else if (0 == objRefSize && osettype == type) {
556 // Not actually implemented
557 } else if (0 == objRefSize && uuidtype == type) {
558 uint8_t marker = (uint8_t)(0x0e);
559 bufferWrite(buf, &marker, 1);
560 {
561 CFUUIDBytes uu = CFUUIDGetUUIDBytes((CFUUIDRef)obj);
562 bufferWrite(buf, (const uint8_t *)&uu, 16);
563 }
564 } else if (0 == objRefSize && urltype == type) {
565 CFStringRef string = CFURLGetString((CFURLRef)obj);
566 if (!string) {
567 return false;
568 }
569 CFURLRef base = CFURLGetBaseURL((CFURLRef)obj);
570 uint8_t marker = (uint8_t)(0x00 | (base ? 0xd : 0xc));
571 bufferWrite(buf, &marker, 1);
572 if (base) {
573 _appendObject(buf, base, objtable, objRefSize);
574 }
575 _appendObject(buf, string, objtable, objRefSize);
576 } else if (0 != objRefSize && _CFKeyedArchiverUIDGetTypeID() == type) {
577 _appendUID(buf, (CFKeyedArchiverUIDRef)obj);
578 } else {
579 return false;
580 }
581 return true;
582 }
583
584 static void _flattenPlist(CFPropertyListRef plist, CFMutableArrayRef objlist, CFMutableDictionaryRef objtable, CFMutableSetRef uniquingset) {
585 CFPropertyListRef unique;
586 uint32_t refnum;
587 CFTypeID type = CFGetTypeID(plist);
588 CFIndex idx;
589 CFPropertyListRef *list, buffer[256];
590
591 // Do not unique dictionaries or arrays, because: they
592 // are slow to compare, and have poor hash codes.
593 // Uniquing bools is unnecessary.
594 if (stringtype == type || numbertype == type || datetype == type || datatype == type) {
595 CFIndex before = CFSetGetCount(uniquingset);
596 CFSetAddValue(uniquingset, plist);
597 CFIndex after = CFSetGetCount(uniquingset);
598 if (after == before) { // already in set
599 unique = CFSetGetValue(uniquingset, plist);
600 if (unique != plist) {
601 refnum = (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable, unique);
602 CFDictionaryAddValue(objtable, plist, (const void *)(uintptr_t)refnum);
603 }
604 return;
605 }
606 }
607 refnum = CFArrayGetCount(objlist);
608 CFArrayAppendValue(objlist, plist);
609 CFDictionaryAddValue(objtable, plist, (const void *)(uintptr_t)refnum);
610 if (dicttype == type) {
611 CFIndex count = CFDictionaryGetCount((CFDictionaryRef)plist);
612 list = (count <= 128) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, 2 * count * sizeof(CFTypeRef), __kCFAllocatorGCScannedMemory);
613 CFDictionaryGetKeysAndValues((CFDictionaryRef)plist, list, list + count);
614 for (idx = 0; idx < 2 * count; idx++) {
615 _flattenPlist(list[idx], objlist, objtable, uniquingset);
616 }
617 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, list);
618 } else if (arraytype == type) {
619 CFIndex count = CFArrayGetCount((CFArrayRef)plist);
620 list = (count <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, count * sizeof(CFTypeRef), __kCFAllocatorGCScannedMemory);
621 CFArrayGetValues((CFArrayRef)plist, CFRangeMake(0, count), list);
622 for (idx = 0; idx < count; idx++) {
623 _flattenPlist(list[idx], objlist, objtable, uniquingset);
624 }
625 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, list);
626 }
627 }
628
629 /* Get the number of bytes required to hold the value in 'count'. Will return a power of 2 value big enough to hold 'count'.
630 */
631 CF_INLINE uint8_t _byteCount(uint64_t count) {
632 uint64_t mask = ~(uint64_t)0;
633 uint8_t size = 0;
634
635 // Find something big enough to hold 'count'
636 while (count & mask) {
637 size++;
638 mask = mask << 8;
639 }
640
641 // Ensure that 'count' is a power of 2
642 // For sizes bigger than 8, just use the required count
643 while ((size != 1 && size != 2 && size != 4 && size != 8) && size <= 8) {
644 size++;
645 }
646
647 return size;
648 }
649
650 // stream can be a CFWriteStreamRef (on supported platforms) or a CFMutableDataRef
651 /* Write a property list to a stream, in binary format. plist is the property list to write (one of the basic property list types), stream is the destination of the property list, and estimate is a best-guess at the total number of objects in the property list. The estimate parameter is for efficiency in pre-allocating memory for the uniquing step. Pass in a 0 if no estimate is available. The options flag specifies sort options. If the error parameter is non-NULL and an error occurs, it will be used to return a CFError explaining the problem. It is the callers responsibility to release the error. */
652 CFIndex __CFBinaryPlistWrite(CFPropertyListRef plist, CFTypeRef stream, uint64_t estimate, CFOptionFlags options, CFErrorRef *error) {
653 CFMutableDictionaryRef objtable = NULL;
654 CFMutableArrayRef objlist = NULL;
655 CFMutableSetRef uniquingset = NULL;
656 CFBinaryPlistTrailer trailer;
657 uint64_t *offsets, length_so_far;
658 int64_t idx, cnt;
659 __CFBinaryPlistWriteBuffer *buf;
660
661 initStatics();
662
663 const CFDictionaryKeyCallBacks dictKeyCallbacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, 0, 0, 0};
664 objtable = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &dictKeyCallbacks, NULL);
665
666 const CFArrayCallBacks arrayCallbacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, 0, 0};
667 objlist = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &arrayCallbacks);
668
669
670 const CFSetCallBacks setCallbacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, 0, 0, 0};
671 uniquingset = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &setCallbacks);
672
673 #if DEPLOYMENT_TARGET_MACOSX
674 _CFDictionarySetCapacity(objtable, estimate ? estimate : 650);
675 _CFArraySetCapacity(objlist, estimate ? estimate : 650);
676 _CFSetSetCapacity(uniquingset, estimate ? estimate : 1000);
677 #endif
678
679 _flattenPlist(plist, objlist, objtable, uniquingset);
680
681 CFRelease(uniquingset);
682
683 cnt = CFArrayGetCount(objlist);
684 offsets = (uint64_t *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, (CFIndex)(cnt * sizeof(*offsets)), 0);
685
686 buf = (__CFBinaryPlistWriteBuffer *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, sizeof(__CFBinaryPlistWriteBuffer), 0);
687 buf->stream = stream;
688 buf->error = NULL;
689 buf->streamIsData = (CFGetTypeID(stream) == CFDataGetTypeID());
690 buf->written = 0;
691 buf->used = 0;
692 bufferWrite(buf, (uint8_t *)"bplist00", 8); // header
693
694 memset(&trailer, 0, sizeof(trailer));
695 trailer._numObjects = CFSwapInt64HostToBig(cnt);
696 trailer._topObject = 0; // true for this implementation
697 trailer._objectRefSize = _byteCount(cnt);
698 for (idx = 0; idx < cnt; idx++) {
699 offsets[idx] = buf->written + buf->used;
700 CFPropertyListRef obj = CFArrayGetValueAtIndex(objlist, (CFIndex)idx);
701 Boolean success = _appendObject(buf, obj, objtable, trailer._objectRefSize);
702 if (!success) {
703 CFRelease(objtable);
704 CFRelease(objlist);
705 if (error && buf->error) {
706 // caller will release error
707 *error = buf->error;
708 } else if (buf->error) {
709 // caller is not interested in error, release it here
710 CFRelease(buf->error);
711 }
712 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, buf);
713 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, offsets);
714 return 0;
715 }
716 }
717 CFRelease(objtable);
718 CFRelease(objlist);
719
720 length_so_far = buf->written + buf->used;
721 trailer._offsetTableOffset = CFSwapInt64HostToBig(length_so_far);
722 trailer._offsetIntSize = _byteCount(length_so_far);
723
724 for (idx = 0; idx < cnt; idx++) {
725 uint64_t swapped = CFSwapInt64HostToBig(offsets[idx]);
726 uint8_t *source = (uint8_t *)&swapped;
727 bufferWrite(buf, source + sizeof(*offsets) - trailer._offsetIntSize, trailer._offsetIntSize);
728 }
729 length_so_far += cnt * trailer._offsetIntSize;
730 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, offsets);
731
732 bufferWrite(buf, (uint8_t *)&trailer, sizeof(trailer));
733 bufferFlush(buf);
734 length_so_far += sizeof(trailer);
735 if (buf->error) {
736 if (error) {
737 // caller will release error
738 *error = buf->error;
739 } else {
740 CFRelease(buf->error);
741 }
742 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, buf);
743 return 0;
744 }
745 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, buf);
746 return (CFIndex)length_so_far;
747 }
748
749
750 CFIndex __CFBinaryPlistWriteToStream(CFPropertyListRef plist, CFTypeRef stream) {
751 return __CFBinaryPlistWrite(plist, stream, 0, 0, NULL);
752 }
753
754 // to be removed soon
755 CFIndex __CFBinaryPlistWriteToStreamWithEstimate(CFPropertyListRef plist, CFTypeRef stream, uint64_t estimate) {
756 return __CFBinaryPlistWrite(plist, stream, estimate, 0, NULL);
757 }
758
759 // to be removed soon
760 CFIndex __CFBinaryPlistWriteToStreamWithOptions(CFPropertyListRef plist, CFTypeRef stream, uint64_t estimate, CFOptionFlags options) {
761 return __CFBinaryPlistWrite(plist, stream, estimate, options, NULL);
762 }
763
764 // Write a version 1.5 plist to the data; extra objects + inlined objects (no references, no uniquing)
765 __private_extern__ Boolean __CFBinaryPlistWrite15(CFPropertyListRef plist, CFMutableDataRef data, CFErrorRef *error) {
766 initStatics();
767
768 __CFBinaryPlistWriteBuffer *buf = (__CFBinaryPlistWriteBuffer *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(__CFBinaryPlistWriteBuffer), 0);
769 memset(buf, 0, sizeof(__CFBinaryPlistWriteBuffer));
770 buf->stream = data;
771 buf->error = NULL;
772 buf->streamIsData = 1;
773 buf->written = 0;
774 buf->used = 0;
775
776 bufferWrite(buf, (uint8_t *)"bplist15", 8); // header
777 bufferWrite(buf, (uint8_t *)"\023\000\000\000\000\000\000\000\000", 9); // header (byte length)
778 bufferWrite(buf, (uint8_t *)"\022\000\000\000\000", 5); // header (crc)
779
780 Boolean success = _appendObject(buf, plist, NULL, 0);
781 if (!success) {
782 if (error && buf->error) {
783 // caller will release error
784 *error = buf->error;
785 } else if (buf->error) {
786 // caller is not interested in error, release it here
787 CFRelease(buf->error);
788 }
789 CFAllocatorDeallocate(kCFAllocatorSystemDefault, buf);
790 return false;
791 }
792 bufferFlush(buf);
793
794 uint64_t swapped_len = CFSwapInt64HostToBig(buf->written);
795 CFDataReplaceBytes(data, CFRangeMake(9, 8), (uint8_t *)&swapped_len, (CFIndex)sizeof(swapped_len));
796
797 CFAllocatorDeallocate(kCFAllocatorSystemDefault, buf);
798 return true;
799 }
800
801
802 #pragma mark -
803 #pragma mark Reading
804
805 #define FAIL_FALSE do { return false; } while (0)
806 #define FAIL_MAXOFFSET do { return UINT64_MAX; } while (0)
807
808 __private_extern__ bool __CFBinaryPlistCreateObjectFiltered(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFAllocatorRef allocator, CFOptionFlags mutabilityOption, CFMutableDictionaryRef objects, CFMutableSetRef set, CFIndex curDepth, CFSetRef keyPaths, CFPropertyListRef *plist);
809
810 /* Grab a valSize-bytes integer out of the buffer pointed at by data and return it.
811 */
812 CF_INLINE uint64_t _getSizedInt(const uint8_t *data, uint8_t valSize) {
813 #if defined(__i386__) || defined(__x86_64__)
814 if (valSize == 1) {
815 return (uint64_t)*data;
816 } else if (valSize == 2) {
817 uint16_t val = *(uint16_t *)data;
818 return (uint64_t)CFSwapInt16BigToHost(val);
819 } else if (valSize == 4) {
820 uint32_t val = *(uint32_t *)data;
821 return (uint64_t)CFSwapInt32BigToHost(val);
822 } else if (valSize == 8) {
823 uint64_t val = *(uint64_t *)data;
824 return CFSwapInt64BigToHost(val);
825 }
826 #endif
827 // Compatability with existing archives, including anything with a non-power-of-2
828 // size and 16-byte values, and architectures that don't support unaligned access
829 uint64_t res = 0;
830 for (CFIndex idx = 0; idx < valSize; idx++) {
831 res = (res << 8) + data[idx];
832 }
833 return res;
834 }
835
836 bool __CFBinaryPlistGetTopLevelInfo(const uint8_t *databytes, uint64_t datalen, uint8_t *marker, uint64_t *offset, CFBinaryPlistTrailer *trailer) {
837 CFBinaryPlistTrailer trail;
838
839 initStatics();
840
841 if (!databytes || datalen < sizeof(trail) + 8 + 1) FAIL_FALSE;
842 // Tiger and earlier will parse "bplist00"
843 // Leopard will parse "bplist00" or "bplist01"
844 // SnowLeopard will parse "bplist0?" where ? is any one character
845 if (0 != memcmp("bplist0", databytes, 7)) {
846 return false;
847 }
848 memmove(&trail, databytes + datalen - sizeof(trail), sizeof(trail));
849 // In Leopard, the unused bytes in the trailer must be 0 or the parse will fail
850 // This check is not present in Tiger and earlier or after Leopard
851 trail._numObjects = CFSwapInt64BigToHost(trail._numObjects);
852 trail._topObject = CFSwapInt64BigToHost(trail._topObject);
853 trail._offsetTableOffset = CFSwapInt64BigToHost(trail._offsetTableOffset);
854
855 // Don't overflow on the number of objects or offset of the table
856 if (LONG_MAX < trail._numObjects) FAIL_FALSE;
857 if (LONG_MAX < trail._offsetTableOffset) FAIL_FALSE;
858
859 // Must be a minimum of 1 object
860 if (trail._numObjects < 1) FAIL_FALSE;
861
862 // The ref to the top object must be a value in the range of 1 to the total number of objects
863 if (trail._numObjects <= trail._topObject) FAIL_FALSE;
864
865 // The offset table must be after at least 9 bytes of other data ('bplist??' + 1 byte of object table data).
866 if (trail._offsetTableOffset < 9) FAIL_FALSE;
867
868 // The trailer must point to a value before itself in the data.
869 if (datalen - sizeof(trail) <= trail._offsetTableOffset) FAIL_FALSE;
870
871 // Minimum of 1 byte for the size of integers and references in the data
872 if (trail._offsetIntSize < 1) FAIL_FALSE;
873 if (trail._objectRefSize < 1) FAIL_FALSE;
874
875 int32_t err = CF_NO_ERROR;
876
877 // The total size of the offset table (number of objects * size of each int in the table) must not overflow
878 uint64_t offsetIntSize = trail._offsetIntSize;
879 uint64_t offsetTableSize = __check_uint64_mul_unsigned_unsigned(trail._numObjects, offsetIntSize, &err);
880 if (CF_NO_ERROR!= err) FAIL_FALSE;
881
882 // The offset table must have at least 1 entry
883 if (offsetTableSize < 1) FAIL_FALSE;
884
885 // Make sure the size of the offset table and data sections do not overflow
886 uint64_t objectDataSize = trail._offsetTableOffset - 8;
887 uint64_t tmpSum = __check_uint64_add_unsigned_unsigned(8, objectDataSize, &err);
888 tmpSum = __check_uint64_add_unsigned_unsigned(tmpSum, offsetTableSize, &err);
889 tmpSum = __check_uint64_add_unsigned_unsigned(tmpSum, sizeof(trail), &err);
890 if (CF_NO_ERROR != err) FAIL_FALSE;
891
892 // The total size of the data should be equal to the sum of offsetTableOffset + sizeof(trailer)
893 if (datalen != tmpSum) FAIL_FALSE;
894
895 // The object refs must be the right size to point into the offset table. That is, if the count of objects is 260, but only 1 byte is used to store references (max value 255), something is wrong.
896 if (trail._objectRefSize < 8 && (1ULL << (8 * trail._objectRefSize)) <= trail._numObjects) FAIL_FALSE;
897
898 // The integers used for pointers in the offset table must be able to reach as far as the start of the offset table.
899 if (trail._offsetIntSize < 8 && (1ULL << (8 * trail._offsetIntSize)) <= trail._offsetTableOffset) FAIL_FALSE;
900
901
902 const uint8_t *objectsFirstByte;
903 objectsFirstByte = check_ptr_add(databytes, 8, &err);
904 if (CF_NO_ERROR != err) FAIL_FALSE;
905 const uint8_t *offsetsFirstByte = check_ptr_add(databytes, trail._offsetTableOffset, &err);
906 if (CF_NO_ERROR != err) FAIL_FALSE;
907 const uint8_t *offsetsLastByte;
908 offsetsLastByte = check_ptr_add(offsetsFirstByte, offsetTableSize - 1, &err);
909 if (CF_NO_ERROR != err) FAIL_FALSE;
910
911 const uint8_t *bytesptr = databytes + trail._offsetTableOffset;
912 uint64_t maxOffset = trail._offsetTableOffset - 1;
913 for (CFIndex idx = 0; idx < trail._numObjects; idx++) {
914 uint64_t off = _getSizedInt(bytesptr, trail._offsetIntSize);
915 if (maxOffset < off) FAIL_FALSE;
916 bytesptr += trail._offsetIntSize;
917 }
918
919 bytesptr = databytes + trail._offsetTableOffset + trail._topObject * trail._offsetIntSize;
920 uint64_t off = _getSizedInt(bytesptr, trail._offsetIntSize);
921 if (off < 8 || trail._offsetTableOffset <= off) FAIL_FALSE;
922 if (trailer) *trailer = trail;
923 if (offset) *offset = off;
924 if (marker) *marker = *(databytes + off);
925 return true;
926 }
927
928 CF_INLINE Boolean _plistIsPrimitive(CFPropertyListRef pl) {
929 CFTypeID type = CFGetTypeID(pl);
930 if (dicttype == type || arraytype == type || settype == type || osettype == type) FAIL_FALSE;
931 return true;
932 }
933
934 CF_INLINE bool _readInt(const uint8_t *ptr, const uint8_t *end_byte_ptr, uint64_t *bigint, const uint8_t **newptr) {
935 if (end_byte_ptr < ptr) FAIL_FALSE;
936 uint8_t marker = *ptr++;
937 if ((marker & 0xf0) != kCFBinaryPlistMarkerInt) FAIL_FALSE;
938 uint64_t cnt = 1 << (marker & 0x0f);
939 int32_t err = CF_NO_ERROR;
940 const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1;
941 if (CF_NO_ERROR != err) FAIL_FALSE;
942 if (end_byte_ptr < extent) FAIL_FALSE;
943 // integers are not required to be in the most compact possible representation, but only the last 64 bits are significant currently
944 *bigint = _getSizedInt(ptr, cnt);
945 ptr += cnt;
946 if (newptr) *newptr = ptr;
947 return true;
948 }
949
950 // bytesptr points at a ref
951 CF_INLINE uint64_t _getOffsetOfRefAt(const uint8_t *databytes, const uint8_t *bytesptr, const CFBinaryPlistTrailer *trailer) {
952 // *trailer contents are trusted, even for overflows -- was checked when the trailer was parsed;
953 // this pointer arithmetic and the multiplication was also already done once and checked,
954 // and the offsetTable was already validated.
955 const uint8_t *objectsFirstByte = databytes + 8;
956 const uint8_t *offsetsFirstByte = databytes + trailer->_offsetTableOffset;
957 if (bytesptr < objectsFirstByte || offsetsFirstByte - trailer->_objectRefSize < bytesptr) FAIL_MAXOFFSET;
958
959 uint64_t ref = _getSizedInt(bytesptr, trailer->_objectRefSize);
960 if (trailer->_numObjects <= ref) FAIL_MAXOFFSET;
961
962 bytesptr = databytes + trailer->_offsetTableOffset + ref * trailer->_offsetIntSize;
963 uint64_t off = _getSizedInt(bytesptr, trailer->_offsetIntSize);
964 return off;
965 }
966
967 bool __CFBinaryPlistGetOffsetForValueFromArray2(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFIndex idx, uint64_t *offset, CFMutableDictionaryRef objects) {
968 uint64_t objectsRangeStart = 8, objectsRangeEnd = trailer->_offsetTableOffset - 1;
969 if (startOffset < objectsRangeStart || objectsRangeEnd < startOffset) FAIL_FALSE;
970 const uint8_t *ptr = databytes + startOffset;
971 uint8_t marker = *ptr;
972 if ((marker & 0xf0) != kCFBinaryPlistMarkerArray) FAIL_FALSE;
973 int32_t err = CF_NO_ERROR;
974 ptr = check_ptr_add(ptr, 1, &err);
975 if (CF_NO_ERROR != err) FAIL_FALSE;
976 uint64_t cnt = (marker & 0x0f);
977 if (0xf == cnt) {
978 uint64_t bigint;
979 if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE;
980 if (LONG_MAX < bigint) FAIL_FALSE;
981 cnt = bigint;
982 }
983 if (cnt <= idx) FAIL_FALSE;
984 size_t byte_cnt = check_size_t_mul(cnt, trailer->_objectRefSize, &err);
985 if (CF_NO_ERROR != err) FAIL_FALSE;
986 const uint8_t *extent = check_ptr_add(ptr, byte_cnt, &err) - 1;
987 if (CF_NO_ERROR != err) FAIL_FALSE;
988 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
989 uint64_t off = _getOffsetOfRefAt(databytes, ptr + idx * trailer->_objectRefSize, trailer);
990 if (offset) *offset = off;
991 return true;
992 }
993
994 /* Get the offset for a value in a dictionary in a binary property list.
995 @param databytes A pointer to the start of the binary property list data.
996 @param datalen The length of the data.
997 @param startOffset The offset at which the dictionary starts.
998 @param trailer A pointer to a filled out trailer structure (use __CFBinaryPlistGetTopLevelInfo).
999 @param key A string key in the dictionary that should be searched for.
1000 @param koffset Will be filled out with the offset to the key in the data bytes.
1001 @param voffset Will be filled out with the offset to the value in the data bytes.
1002 @param unused Unused parameter.
1003 @param objects Used for caching objects. Should be a valid CFMutableDictionaryRef.
1004 @return True if the key was found, false otherwise.
1005 */
1006 bool __CFBinaryPlistGetOffsetForValueFromDictionary3(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFTypeRef key, uint64_t *koffset, uint64_t *voffset, Boolean unused, CFMutableDictionaryRef objects) {
1007
1008 // Require a key that is a plist primitive
1009 if (!key || !_plistIsPrimitive(key)) FAIL_FALSE;
1010
1011 // Require that startOffset is in the range of the object table
1012 uint64_t objectsRangeStart = 8, objectsRangeEnd = trailer->_offsetTableOffset - 1;
1013 if (startOffset < objectsRangeStart || objectsRangeEnd < startOffset) FAIL_FALSE;
1014
1015 // ptr is the start of the dictionary we are reading
1016 const uint8_t *ptr = databytes + startOffset;
1017
1018 // Check that the data pointer actually points to a dictionary
1019 uint8_t marker = *ptr;
1020 if ((marker & 0xf0) != kCFBinaryPlistMarkerDict) FAIL_FALSE;
1021
1022 // Get the number of objects in this dictionary
1023 int32_t err = CF_NO_ERROR;
1024 ptr = check_ptr_add(ptr, 1, &err);
1025 if (CF_NO_ERROR != err) FAIL_FALSE;
1026 uint64_t cnt = (marker & 0x0f);
1027 if (0xf == cnt) {
1028 uint64_t bigint = 0;
1029 if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE;
1030 if (LONG_MAX < bigint) FAIL_FALSE;
1031 cnt = bigint;
1032 }
1033
1034 // Total number of objects (keys + values) is cnt * 2
1035 cnt = check_size_t_mul(cnt, 2, &err);
1036 if (CF_NO_ERROR != err) FAIL_FALSE;
1037 size_t byte_cnt = check_size_t_mul(cnt, trailer->_objectRefSize, &err);
1038 if (CF_NO_ERROR != err) FAIL_FALSE;
1039
1040 // Find the end of the dictionary
1041 const uint8_t *extent = check_ptr_add(ptr, byte_cnt, &err) - 1;
1042 if (CF_NO_ERROR != err) FAIL_FALSE;
1043
1044 // Check that we didn't overflow the size of the dictionary
1045 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1046
1047 // For short keys (15 bytes or less) in ASCII form, we can do a quick comparison check
1048 // We get the pointer or copy the buffer here, outside of the loop
1049 CFIndex stringKeyLen = -1;
1050 if (CFGetTypeID(key) == stringtype) {
1051 stringKeyLen = CFStringGetLength((CFStringRef)key);
1052 }
1053
1054 // Find the object in the dictionary with this key
1055 cnt = cnt / 2;
1056 uint64_t totalKeySize = cnt * trailer->_objectRefSize;
1057 uint64_t off;
1058 Boolean match = false;
1059 CFPropertyListRef keyInData = NULL;
1060
1061 #define KEY_BUFF_SIZE 16
1062 char keyBuffer[KEY_BUFF_SIZE];
1063 const char *keyBufferPtr = NULL;
1064
1065 // If we have a string for the key, then we will grab the ASCII encoded version of it, if possible, and do a memcmp on it
1066 if (stringKeyLen != -1) {
1067 // Since we will only be comparing ASCII strings, we can attempt to get a pointer using MacRoman encoding
1068 // (this is cheaper than a copy)
1069 if (!(keyBufferPtr = CFStringGetCStringPtr((CFStringRef)key, kCFStringEncodingMacRoman)) && stringKeyLen < KEY_BUFF_SIZE) {
1070 CFStringGetCString((CFStringRef)key, keyBuffer, KEY_BUFF_SIZE, kCFStringEncodingMacRoman);
1071 // The pointer should now point to our keyBuffer instead of the original string buffer, since we've copied it
1072 keyBufferPtr = keyBuffer;
1073 }
1074 }
1075
1076 // Perform linear search of the keys
1077 for (CFIndex idx = 0; idx < cnt; idx++) {
1078 off = _getOffsetOfRefAt(databytes, ptr, trailer);
1079 marker = *(databytes + off);
1080 // if it is an ASCII string in the data, then we do a memcmp. If the key isn't ASCII, then it won't pass the compare, unless it hits some odd edge case of the ASCII string actually containing the unicode escape sequence.
1081 if (keyBufferPtr && (marker & 0xf0) == kCFBinaryPlistMarkerASCIIString) {
1082 CFIndex len = marker & 0x0f;
1083 // move past the marker
1084 const uint8_t *ptr2 = databytes + off;
1085 err = CF_NO_ERROR;
1086 ptr2 = check_ptr_add(ptr2, 1, &err);
1087 if (CF_NO_ERROR != err) FAIL_FALSE;
1088
1089 // If the key's length is large, and the length we are querying is also large, then we have to read it in. If stringKeyLen is less than 0xf, then len will never be equal to it if it was encoded as large.
1090 if (0xf == len && stringKeyLen >= 0xf) {
1091 uint64_t bigint = 0;
1092 if (!_readInt(ptr2, databytes + objectsRangeEnd, &bigint, &ptr2)) FAIL_FALSE;
1093 if (LONG_MAX < bigint) FAIL_FALSE;
1094 len = (CFIndex)bigint;
1095 }
1096
1097 if (len == stringKeyLen) {
1098 err = CF_NO_ERROR;
1099 extent = check_ptr_add(ptr2, len, &err);
1100 if (CF_NO_ERROR != err) FAIL_FALSE;
1101
1102 if (databytes + trailer->_offsetTableOffset <= extent) FAIL_FALSE;
1103
1104 // Compare the key to this potential match
1105 if (memcmp(ptr2, keyBufferPtr, stringKeyLen) == 0) {
1106 match = true;
1107 }
1108 }
1109 } else {
1110 // temp object not saved in 'objects', because we don't know what allocator to use
1111 // (what allocator __CFBinaryPlistCreateObjectFiltered() or __CFBinaryPlistCreateObject()
1112 // will eventually be called with which results in that object)
1113 keyInData = NULL;
1114 if (!__CFBinaryPlistCreateObjectFiltered(databytes, datalen, off, trailer, kCFAllocatorSystemDefault, kCFPropertyListImmutable, NULL /*objects*/, NULL, 0, NULL, &keyInData) || !_plistIsPrimitive(keyInData)) {
1115 if (keyInData) CFRelease(keyInData);
1116 return false;
1117 }
1118
1119 match = CFEqual(key, keyInData);
1120 CFRelease(keyInData);
1121 }
1122
1123 if (match) {
1124 if (koffset) *koffset = off;
1125 if (voffset) *voffset = _getOffsetOfRefAt(databytes, ptr + totalKeySize, trailer);
1126 return true;
1127 }
1128
1129 ptr += trailer->_objectRefSize;
1130 }
1131
1132 return false;
1133 }
1134
1135 extern CFDictionaryRef __CFDictionaryCreateTransfer(CFAllocatorRef allocator, const void * *klist, const void * *vlist, CFIndex numValues);
1136 extern CFSetRef __CFSetCreateTransfer(CFAllocatorRef allocator, const void * *klist, CFIndex numValues);
1137 extern CFArrayRef __CFArrayCreateTransfer(CFAllocatorRef allocator, const void * *klist, CFIndex numValues);
1138 __private_extern__ void __CFPropertyListCreateSplitKeypaths(CFAllocatorRef allocator, CFSetRef currentKeys, CFSetRef *theseKeys, CFSetRef *nextKeys);
1139
1140 __private_extern__ bool __CFBinaryPlistCreateObjectFiltered(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFAllocatorRef allocator, CFOptionFlags mutabilityOption, CFMutableDictionaryRef objects, CFMutableSetRef set, CFIndex curDepth, CFSetRef keyPaths, CFPropertyListRef *plist) {
1141
1142 if (objects) {
1143 *plist = CFDictionaryGetValue(objects, (const void *)(uintptr_t)startOffset);
1144 if (*plist) {
1145 // have to assume that '*plist' was previously created with same allocator that is now being passed in
1146 if (!_CFAllocatorIsGCRefZero(allocator)) CFRetain(*plist);
1147 return true;
1148 }
1149 }
1150
1151 // at any one invocation of this function, set should contain the offsets in the "path" down to this object
1152 if (set && CFSetContainsValue(set, (const void *)(uintptr_t)startOffset)) return false;
1153
1154 // databytes is trusted to be at least datalen bytes long
1155 // *trailer contents are trusted, even for overflows -- was checked when the trailer was parsed
1156 uint64_t objectsRangeStart = 8, objectsRangeEnd = trailer->_offsetTableOffset - 1;
1157 if (startOffset < objectsRangeStart || objectsRangeEnd < startOffset) FAIL_FALSE;
1158
1159 uint64_t off;
1160 CFPropertyListRef *list, buffer[256];
1161
1162 uint8_t marker = *(databytes + startOffset);
1163 switch (marker & 0xf0) {
1164 case kCFBinaryPlistMarkerNull:
1165 switch (marker) {
1166 case kCFBinaryPlistMarkerNull:
1167 *plist = kCFNull;
1168 return true;
1169 case kCFBinaryPlistMarkerFalse:
1170 *plist = !_CFAllocatorIsGCRefZero(allocator) ? CFRetain(kCFBooleanFalse) : kCFBooleanFalse;
1171 return true;
1172 case kCFBinaryPlistMarkerTrue:
1173 *plist = !_CFAllocatorIsGCRefZero(allocator) ? CFRetain(kCFBooleanTrue) : kCFBooleanTrue;
1174 return true;
1175 }
1176 FAIL_FALSE;
1177 case kCFBinaryPlistMarkerInt:
1178 {
1179 const uint8_t *ptr = (databytes + startOffset);
1180 int32_t err = CF_NO_ERROR;
1181 ptr = check_ptr_add(ptr, 1, &err);
1182 if (CF_NO_ERROR != err) FAIL_FALSE;
1183 uint64_t cnt = 1 << (marker & 0x0f);
1184 const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1;
1185 if (CF_NO_ERROR != err) FAIL_FALSE;
1186 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1187 if (16 < cnt) FAIL_FALSE;
1188 // in format version '00', 1, 2, and 4-byte integers have to be interpreted as unsigned,
1189 // whereas 8-byte integers are signed (and 16-byte when available)
1190 // negative 1, 2, 4-byte integers are always emitted as 8 bytes in format '00'
1191 // integers are not required to be in the most compact possible representation, but only the last 64 bits are significant currently
1192 uint64_t bigint = _getSizedInt(ptr, cnt);
1193 ptr += cnt;
1194 if (8 < cnt) {
1195 CFSInt128Struct val;
1196 val.high = 0;
1197 val.low = bigint;
1198 *plist = CFNumberCreate(allocator, kCFNumberSInt128Type, &val);
1199 } else {
1200 *plist = CFNumberCreate(allocator, kCFNumberSInt64Type, &bigint);
1201 }
1202 // these are always immutable
1203 if (objects && *plist) {
1204 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1205 }
1206 return (*plist) ? true : false;
1207 }
1208 case kCFBinaryPlistMarkerReal:
1209 switch (marker & 0x0f) {
1210 case 2: {
1211 const uint8_t *ptr = (databytes + startOffset);
1212 int32_t err = CF_NO_ERROR;
1213 ptr = check_ptr_add(ptr, 1, &err);
1214 if (CF_NO_ERROR != err) FAIL_FALSE;
1215 const uint8_t *extent = check_ptr_add(ptr, 4, &err) - 1;
1216 if (CF_NO_ERROR != err) FAIL_FALSE;
1217 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1218 CFSwappedFloat32 swapped32;
1219 memmove(&swapped32, ptr, 4);
1220 float f = CFConvertFloat32SwappedToHost(swapped32);
1221 *plist = CFNumberCreate(allocator, kCFNumberFloat32Type, &f);
1222 // these are always immutable
1223 if (objects && *plist) {
1224 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1225 }
1226 return (*plist) ? true : false;
1227 }
1228 case 3: {
1229 const uint8_t *ptr = (databytes + startOffset);
1230 int32_t err = CF_NO_ERROR;
1231 ptr = check_ptr_add(ptr, 1, &err);
1232 if (CF_NO_ERROR != err) FAIL_FALSE;
1233 const uint8_t *extent = check_ptr_add(ptr, 8, &err) - 1;
1234 if (CF_NO_ERROR != err) FAIL_FALSE;
1235 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1236 CFSwappedFloat64 swapped64;
1237 memmove(&swapped64, ptr, 8);
1238 double d = CFConvertFloat64SwappedToHost(swapped64);
1239 *plist = CFNumberCreate(allocator, kCFNumberFloat64Type, &d);
1240 // these are always immutable
1241 if (objects && *plist) {
1242 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1243 }
1244 return (*plist) ? true : false;
1245 }
1246 }
1247 FAIL_FALSE;
1248 case kCFBinaryPlistMarkerDate & 0xf0:
1249 switch (marker) {
1250 case kCFBinaryPlistMarkerDate: {
1251 const uint8_t *ptr = (databytes + startOffset);
1252 int32_t err = CF_NO_ERROR;
1253 ptr = check_ptr_add(ptr, 1, &err);
1254 if (CF_NO_ERROR != err) FAIL_FALSE;
1255 const uint8_t *extent = check_ptr_add(ptr, 8, &err) - 1;
1256 if (CF_NO_ERROR != err) FAIL_FALSE;
1257 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1258 CFSwappedFloat64 swapped64;
1259 memmove(&swapped64, ptr, 8);
1260 double d = CFConvertFloat64SwappedToHost(swapped64);
1261 *plist = CFDateCreate(allocator, d);
1262 // these are always immutable
1263 if (objects && *plist) {
1264 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1265 }
1266 return (*plist) ? true : false;
1267 }
1268 }
1269 FAIL_FALSE;
1270 case kCFBinaryPlistMarkerData: {
1271 const uint8_t *ptr = databytes + startOffset;
1272 int32_t err = CF_NO_ERROR;
1273 ptr = check_ptr_add(ptr, 1, &err);
1274 if (CF_NO_ERROR != err) FAIL_FALSE;
1275 CFIndex cnt = marker & 0x0f;
1276 if (0xf == cnt) {
1277 uint64_t bigint = 0;
1278 if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE;
1279 if (LONG_MAX < bigint) FAIL_FALSE;
1280 cnt = (CFIndex)bigint;
1281 }
1282 const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1;
1283 if (CF_NO_ERROR != err) FAIL_FALSE;
1284 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1285 if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) {
1286 *plist = CFDataCreateMutable(allocator, 0);
1287 if (*plist) CFDataAppendBytes((CFMutableDataRef)*plist, ptr, cnt);
1288 } else {
1289 *plist = CFDataCreate(allocator, ptr, cnt);
1290 }
1291 if (objects && *plist && (mutabilityOption != kCFPropertyListMutableContainersAndLeaves)) {
1292 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1293 }
1294 return (*plist) ? true : false;
1295 }
1296 case kCFBinaryPlistMarkerASCIIString: {
1297 const uint8_t *ptr = databytes + startOffset;
1298 int32_t err = CF_NO_ERROR;
1299 ptr = check_ptr_add(ptr, 1, &err);
1300 if (CF_NO_ERROR != err) FAIL_FALSE;
1301 CFIndex cnt = marker & 0x0f;
1302 if (0xf == cnt) {
1303 uint64_t bigint = 0;
1304 if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE;
1305 if (LONG_MAX < bigint) FAIL_FALSE;
1306 cnt = (CFIndex)bigint;
1307 }
1308 const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1;
1309 if (CF_NO_ERROR != err) FAIL_FALSE;
1310 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1311 if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) {
1312 CFStringRef str = CFStringCreateWithBytes(allocator, ptr, cnt, kCFStringEncodingASCII, false);
1313 *plist = str ? CFStringCreateMutableCopy(allocator, 0, str) : NULL;
1314 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(str);
1315 } else {
1316 *plist = CFStringCreateWithBytes(allocator, ptr, cnt, kCFStringEncodingASCII, false);
1317 }
1318 if (objects && *plist && (mutabilityOption != kCFPropertyListMutableContainersAndLeaves)) {
1319 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1320 }
1321 return (*plist) ? true : false;
1322 }
1323 case kCFBinaryPlistMarkerUnicode16String: {
1324 const uint8_t *ptr = databytes + startOffset;
1325 int32_t err = CF_NO_ERROR;
1326 ptr = check_ptr_add(ptr, 1, &err);
1327 if (CF_NO_ERROR != err) FAIL_FALSE;
1328 CFIndex cnt = marker & 0x0f;
1329 if (0xf == cnt) {
1330 uint64_t bigint = 0;
1331 if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE;
1332 if (LONG_MAX < bigint) FAIL_FALSE;
1333 cnt = (CFIndex)bigint;
1334 }
1335 const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1;
1336 extent = check_ptr_add(extent, cnt, &err); // 2 bytes per character
1337 if (CF_NO_ERROR != err) FAIL_FALSE;
1338 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1339 size_t byte_cnt = check_size_t_mul(cnt, sizeof(UniChar), &err);
1340 if (CF_NO_ERROR != err) FAIL_FALSE;
1341 UniChar *chars = (UniChar *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, byte_cnt, 0);
1342 if (!chars) FAIL_FALSE;
1343 memmove(chars, ptr, byte_cnt);
1344 for (CFIndex idx = 0; idx < cnt; idx++) {
1345 chars[idx] = CFSwapInt16BigToHost(chars[idx]);
1346 }
1347 if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) {
1348 CFStringRef str = CFStringCreateWithCharacters(allocator, chars, cnt);
1349 *plist = str ? CFStringCreateMutableCopy(allocator, 0, str) : NULL;
1350 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(str);
1351 } else {
1352 *plist = CFStringCreateWithCharacters(allocator, chars, cnt);
1353 }
1354 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, chars);
1355 if (objects && *plist && (mutabilityOption != kCFPropertyListMutableContainersAndLeaves)) {
1356 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1357 }
1358 return (*plist) ? true : false;
1359 }
1360 case kCFBinaryPlistMarkerUID: {
1361 const uint8_t *ptr = databytes + startOffset;
1362 int32_t err = CF_NO_ERROR;
1363 ptr = check_ptr_add(ptr, 1, &err);
1364 if (CF_NO_ERROR != err) FAIL_FALSE;
1365 CFIndex cnt = (marker & 0x0f) + 1;
1366 const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1;
1367 if (CF_NO_ERROR != err) FAIL_FALSE;
1368 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1369 // uids are not required to be in the most compact possible representation, but only the last 64 bits are significant currently
1370 uint64_t bigint = _getSizedInt(ptr, cnt);
1371 ptr += cnt;
1372 if (UINT32_MAX < bigint) FAIL_FALSE;
1373 *plist = _CFKeyedArchiverUIDCreate(allocator, (uint32_t)bigint);
1374 // these are always immutable
1375 if (objects && *plist) {
1376 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1377 }
1378 return (*plist) ? true : false;
1379 }
1380 case kCFBinaryPlistMarkerArray:
1381 case kCFBinaryPlistMarkerSet: {
1382 const uint8_t *ptr = databytes + startOffset;
1383 int32_t err = CF_NO_ERROR;
1384 ptr = check_ptr_add(ptr, 1, &err);
1385 if (CF_NO_ERROR != err) FAIL_FALSE;
1386 CFIndex arrayCount = marker & 0x0f;
1387 if (0xf == arrayCount) {
1388 uint64_t bigint = 0;
1389 if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE;
1390 if (LONG_MAX < bigint) FAIL_FALSE;
1391 arrayCount = (CFIndex)bigint;
1392 }
1393 size_t byte_cnt = check_size_t_mul(arrayCount, trailer->_objectRefSize, &err);
1394 if (CF_NO_ERROR != err) FAIL_FALSE;
1395 const uint8_t *extent = check_ptr_add(ptr, byte_cnt, &err) - 1;
1396 if (CF_NO_ERROR != err) FAIL_FALSE;
1397 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1398 byte_cnt = check_size_t_mul(arrayCount, sizeof(CFPropertyListRef), &err);
1399 if (CF_NO_ERROR != err) FAIL_FALSE;
1400 list = (arrayCount <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, byte_cnt, __kCFAllocatorGCScannedMemory);
1401 if (!list) FAIL_FALSE;
1402 Boolean madeSet = false;
1403 if (!set && 15 < curDepth) {
1404 set = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, NULL);
1405 madeSet = set ? true : false;
1406 }
1407
1408 if (set) CFSetAddValue(set, (const void *)(uintptr_t)startOffset);
1409 if ((marker & 0xf0) == kCFBinaryPlistMarkerArray && keyPaths) {
1410 // Only get a subset of this array
1411 CFSetRef theseKeys, nextKeys;
1412 __CFPropertyListCreateSplitKeypaths(kCFAllocatorSystemDefault, keyPaths, &theseKeys, &nextKeys);
1413
1414 Boolean success = true;
1415 CFMutableArrayRef array = CFArrayCreateMutable(allocator, CFSetGetCount(theseKeys), &kCFTypeArrayCallBacks);
1416 if (theseKeys) {
1417 CFTypeRef *keys = (CFTypeRef *)malloc(CFSetGetCount(theseKeys) * sizeof(CFTypeRef));
1418 CFSetGetValues(theseKeys, keys);
1419 for (CFIndex i = 0; i < CFSetGetCount(theseKeys); i++) {
1420 CFStringRef key = (CFStringRef)keys[i];
1421 SInt32 intValue = CFStringGetIntValue(key);
1422 if ((intValue == 0 && CFStringCompare(CFSTR("0"), key, 0) != kCFCompareEqualTo) || intValue == INT_MAX || intValue == INT_MIN || intValue < 0) {
1423 // skip, doesn't appear to be a proper integer
1424 } else {
1425 uint64_t valueOffset;
1426 Boolean found = __CFBinaryPlistGetOffsetForValueFromArray2(databytes, datalen, startOffset, trailer, (CFIndex)intValue, &valueOffset, objects);
1427 if (found) {
1428 CFPropertyListRef result;
1429 success = __CFBinaryPlistCreateObjectFiltered(databytes, datalen, valueOffset, trailer, allocator, mutabilityOption, objects, set, curDepth + 1, nextKeys, &result);
1430 if (success) {
1431 CFArrayAppendValue(array, result);
1432 CFRelease(result);
1433 } else {
1434 break;
1435 }
1436 }
1437 }
1438 }
1439
1440 free(keys);
1441 CFRelease(theseKeys);
1442 }
1443 if (nextKeys) CFRelease(nextKeys);
1444
1445 if (success) {
1446 if (!(mutabilityOption == kCFPropertyListMutableContainers || mutabilityOption == kCFPropertyListMutableContainersAndLeaves)) {
1447 // make immutable
1448 *plist = CFArrayCreateCopy(allocator, array);
1449 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(array);
1450 } else {
1451 *plist = array;
1452 }
1453 } else if (array) {
1454 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(array);
1455 }
1456 } else {
1457 for (CFIndex idx = 0; idx < arrayCount; idx++) {
1458 CFPropertyListRef pl;
1459 off = _getOffsetOfRefAt(databytes, ptr, trailer);
1460 if (!__CFBinaryPlistCreateObjectFiltered(databytes, datalen, off, trailer, allocator, mutabilityOption, objects, set, curDepth + 1, NULL, &pl)) {
1461 while (idx--) {
1462 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(list[idx]);
1463 }
1464 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, list);
1465 FAIL_FALSE;
1466 }
1467 __CFAssignWithWriteBarrier((void **)list + idx, (void *)pl);
1468 ptr += trailer->_objectRefSize;
1469 }
1470 if ((marker & 0xf0) == kCFBinaryPlistMarkerArray) {
1471 if (mutabilityOption != kCFPropertyListImmutable) {
1472 *plist = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
1473 CFArrayReplaceValues((CFMutableArrayRef)*plist, CFRangeMake(0, 0), list, arrayCount);
1474 for (CFIndex idx = 0; idx < arrayCount; idx++) {
1475 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(list[idx]);
1476 }
1477 } else {
1478 if (!kCFUseCollectableAllocator) {
1479 *plist = __CFArrayCreateTransfer(allocator, list, arrayCount);
1480 } else {
1481 *plist = CFArrayCreate(allocator, list, arrayCount, &kCFTypeArrayCallBacks);
1482 for (CFIndex idx = 0; idx < arrayCount; idx++) {
1483 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(list[idx]);
1484 }
1485 }
1486 }
1487 } else {
1488 if (mutabilityOption != kCFPropertyListImmutable) {
1489 *plist = CFSetCreateMutable(allocator, 0, &kCFTypeSetCallBacks);
1490 for (CFIndex idx = 0; idx < arrayCount; idx++) {
1491 CFSetAddValue((CFMutableSetRef)*plist, list[idx]);
1492 }
1493 for (CFIndex idx = 0; idx < arrayCount; idx++) {
1494 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(list[idx]);
1495 }
1496 } else {
1497 if (!kCFUseCollectableAllocator) {
1498 *plist = __CFSetCreateTransfer(allocator, list, arrayCount);
1499 } else {
1500 *plist = CFSetCreate(allocator, list, arrayCount, &kCFTypeSetCallBacks);
1501 for (CFIndex idx = 0; idx < arrayCount; idx++) {
1502 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(list[idx]);
1503 }
1504 }
1505 }
1506 }
1507 }
1508 if (set) CFSetRemoveValue(set, (const void *)(uintptr_t)startOffset);
1509 if (madeSet) {
1510 CFRelease(set);
1511 set = NULL;
1512 }
1513 if (objects && *plist && (mutabilityOption == kCFPropertyListImmutable)) {
1514 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1515 }
1516 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, list);
1517 return (*plist) ? true : false;
1518 }
1519 case kCFBinaryPlistMarkerDict: {
1520 const uint8_t *ptr = databytes + startOffset;
1521 int32_t err = CF_NO_ERROR;
1522 ptr = check_ptr_add(ptr, 1, &err);
1523 if (CF_NO_ERROR != err) FAIL_FALSE;
1524 CFIndex dictionaryCount = marker & 0x0f;
1525 if (0xf == dictionaryCount) {
1526 uint64_t bigint = 0;
1527 if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE;
1528 if (LONG_MAX < bigint) FAIL_FALSE;
1529 dictionaryCount = (CFIndex)bigint;
1530 }
1531 dictionaryCount = check_size_t_mul(dictionaryCount, 2, &err);
1532 if (CF_NO_ERROR != err) FAIL_FALSE;
1533 size_t byte_cnt = check_size_t_mul(dictionaryCount, trailer->_objectRefSize, &err);
1534 if (CF_NO_ERROR != err) FAIL_FALSE;
1535 const uint8_t *extent = check_ptr_add(ptr, byte_cnt, &err) - 1;
1536 if (CF_NO_ERROR != err) FAIL_FALSE;
1537 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1538 byte_cnt = check_size_t_mul(dictionaryCount, sizeof(CFPropertyListRef), &err);
1539 if (CF_NO_ERROR != err) FAIL_FALSE;
1540 list = (dictionaryCount <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, byte_cnt, __kCFAllocatorGCScannedMemory);
1541 if (!list) FAIL_FALSE;
1542 Boolean madeSet = false;
1543 if (!set && 15 < curDepth) {
1544 set = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, NULL);
1545 madeSet = set ? true : false;
1546 }
1547
1548 if (set) CFSetAddValue(set, (const void *)(uintptr_t)startOffset);
1549 if (keyPaths) {
1550 // Only get a subset of this dictionary
1551 CFSetRef theseKeys, nextKeys;
1552 __CFPropertyListCreateSplitKeypaths(kCFAllocatorSystemDefault, keyPaths, &theseKeys, &nextKeys);
1553
1554 Boolean success = true;
1555 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(allocator, CFSetGetCount(theseKeys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1556 if (theseKeys) {
1557 CFTypeRef *keys = (CFTypeRef *)malloc(CFSetGetCount(theseKeys) * sizeof(CFTypeRef));
1558 CFSetGetValues(theseKeys, keys);
1559 for (CFIndex i = 0; i < CFSetGetCount(theseKeys); i++) {
1560 CFStringRef key = (CFStringRef)keys[i];
1561 uint64_t keyOffset, valueOffset;
1562 Boolean found = __CFBinaryPlistGetOffsetForValueFromDictionary3(databytes, datalen, startOffset, trailer, key, &keyOffset, &valueOffset, false, objects);
1563 if (found) {
1564 CFPropertyListRef result;
1565 success = __CFBinaryPlistCreateObjectFiltered(databytes, datalen, valueOffset, trailer, allocator, mutabilityOption, objects, set, curDepth + 1, nextKeys, &result);
1566 if (success) {
1567 CFDictionarySetValue(dict, key, result);
1568 CFRelease(result);
1569 } else {
1570 break;
1571 }
1572 }
1573 }
1574
1575 free(keys);
1576 CFRelease(theseKeys);
1577 }
1578 if (nextKeys) CFRelease(nextKeys);
1579
1580 if (success) {
1581 if (!(mutabilityOption == kCFPropertyListMutableContainers || mutabilityOption == kCFPropertyListMutableContainersAndLeaves)) {
1582 // make immutable
1583 *plist = CFDictionaryCreateCopy(allocator, dict);
1584 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(dict);
1585 } else {
1586 *plist = dict;
1587 }
1588 } else if (dict) {
1589 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(dict);
1590 }
1591 } else {
1592 for (CFIndex idx = 0; idx < dictionaryCount; idx++) {
1593 CFPropertyListRef pl = NULL;
1594 off = _getOffsetOfRefAt(databytes, ptr, trailer);
1595 if (!__CFBinaryPlistCreateObjectFiltered(databytes, datalen, off, trailer, allocator, mutabilityOption, objects, set, curDepth + 1, NULL, &pl) || (idx < dictionaryCount / 2 && !_plistIsPrimitive(pl))) {
1596 if (pl && !_CFAllocatorIsGCRefZero(allocator)) CFRelease(pl);
1597 while (idx--) {
1598 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(list[idx]);
1599 }
1600 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, list);
1601 FAIL_FALSE;
1602 }
1603 __CFAssignWithWriteBarrier((void **)list + idx, (void *)pl);
1604 ptr += trailer->_objectRefSize;
1605 }
1606 if (mutabilityOption != kCFPropertyListImmutable) {
1607 *plist = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1608 for (CFIndex idx = 0; idx < dictionaryCount / 2; idx++) {
1609 CFDictionaryAddValue((CFMutableDictionaryRef)*plist, list[idx], list[idx + dictionaryCount / 2]);
1610 }
1611 for (CFIndex idx = 0; idx < dictionaryCount; idx++) {
1612 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(list[idx]);
1613 }
1614 } else {
1615 if (!kCFUseCollectableAllocator) {
1616 *plist = __CFDictionaryCreateTransfer(allocator, list, list + dictionaryCount / 2, dictionaryCount / 2);
1617 } else {
1618 *plist = CFDictionaryCreate(allocator, list, list + dictionaryCount / 2, dictionaryCount / 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1619 for (CFIndex idx = 0; idx < dictionaryCount; idx++) {
1620 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(list[idx]);
1621 }
1622 }
1623 }
1624 }
1625 if (set) CFSetRemoveValue(set, (const void *)(uintptr_t)startOffset);
1626 if (madeSet) {
1627 CFRelease(set);
1628 set = NULL;
1629 }
1630 if (objects && *plist && (mutabilityOption == kCFPropertyListImmutable)) {
1631 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1632 }
1633 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, list);
1634 return (*plist) ? true : false;
1635 }
1636 }
1637 FAIL_FALSE;
1638 }
1639
1640 bool __CFBinaryPlistCreateObject(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFAllocatorRef allocator, CFOptionFlags mutabilityOption, CFMutableDictionaryRef objects, CFPropertyListRef *plist) {
1641 // for compatibility with Foundation's use, need to leave this here
1642 return __CFBinaryPlistCreateObjectFiltered(databytes, datalen, startOffset, trailer, allocator, mutabilityOption, objects, NULL, 0, NULL, plist);
1643 }
1644
1645 __private_extern__ bool __CFTryParseBinaryPlist(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags option, CFPropertyListRef *plist, CFStringRef *errorString) {
1646 uint8_t marker;
1647 CFBinaryPlistTrailer trailer;
1648 uint64_t offset;
1649 const uint8_t *databytes = CFDataGetBytePtr(data);
1650 uint64_t datalen = CFDataGetLength(data);
1651
1652 if (8 <= datalen && __CFBinaryPlistGetTopLevelInfo(databytes, datalen, &marker, &offset, &trailer)) {
1653 // FALSE: We know for binary plist parsing that the result objects will be retained
1654 // by their containing collections as the parsing proceeds, so we do not need
1655 // to use retaining callbacks for the objects map in this case. WHY: the file might
1656 // be malformed and contain hash-equal keys for the same dictionary (for example)
1657 // and the later key will cause the previous one to be released when we set the second
1658 // in the dictionary.
1659 CFMutableDictionaryRef objects = CFDictionaryCreateMutable(kCFAllocatorSystemDefaultGCRefZero, 0, NULL, &kCFTypeDictionaryValueCallBacks);
1660 _CFDictionarySetCapacity(objects, trailer._numObjects);
1661 CFPropertyListRef pl = NULL;
1662 bool result = true;
1663 if (__CFBinaryPlistCreateObjectFiltered(databytes, datalen, offset, &trailer, allocator, option, objects, NULL, 0, NULL, &pl)) {
1664 if (plist) *plist = pl;
1665 } else {
1666 if (plist) *plist = NULL;
1667 if (errorString) *errorString = (CFStringRef)CFRetain(CFSTR("binary data is corrupt"));
1668 result = false;
1669 }
1670 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFRelease(objects);
1671 return result;
1672 }
1673 return false;
1674 }
1675
1676
1677 static CFPropertyListRef __CFBinaryPlistCreateObject15(const uint8_t *databytes, uint64_t datalen, const uint8_t **ptr) {
1678 const uint8_t *objectsRangeStart = databytes + 22, *objectsRangeEnd = databytes + datalen - 1;
1679 if (*ptr < objectsRangeStart || objectsRangeEnd < *ptr) return NULL;
1680 uint8_t marker = **ptr;
1681 int32_t err = CF_NO_ERROR;
1682 *ptr = check_ptr_add(*ptr, 1, &err);
1683 if (CF_NO_ERROR != err) return NULL;
1684 switch (marker & 0xf0) {
1685 case 0x0:
1686 switch (marker) {
1687 case kCFBinaryPlistMarkerNull:
1688 return kCFNull;
1689 case kCFBinaryPlistMarkerFalse:
1690 return kCFBooleanFalse;
1691 case kCFBinaryPlistMarkerTrue:
1692 return kCFBooleanTrue;
1693 case 0x0c: {
1694 CFStringRef string = (CFStringRef)__CFBinaryPlistCreateObject15(databytes, datalen, ptr);
1695 if (!string) {
1696 return NULL;
1697 }
1698 CFURLRef result = CFURLCreateWithString(kCFAllocatorSystemDefault, string, NULL);
1699 CFRelease(string);
1700 return result;
1701 }
1702 case 0x0d: {
1703 CFURLRef base = (CFURLRef)__CFBinaryPlistCreateObject15(databytes, datalen, ptr);
1704 if (!base) return NULL;
1705 CFStringRef string = (CFStringRef)__CFBinaryPlistCreateObject15(databytes, datalen, ptr);
1706 if (!string) {
1707 CFRelease(base);
1708 return NULL;
1709 }
1710 CFURLRef result = CFURLCreateWithString(kCFAllocatorSystemDefault, string, base);
1711 CFRelease(base);
1712 CFRelease(string);
1713 return result;
1714 }
1715 case 0x0e: {
1716 int32_t err = CF_NO_ERROR;
1717 const uint8_t *extent = check_ptr_add(*ptr, 16, &err) - 1;
1718 if (CF_NO_ERROR != err) return NULL;
1719 if (objectsRangeEnd < extent) return NULL;
1720 CFUUIDBytes uuid;
1721 memmove(&uuid, *ptr, 16);
1722 *ptr = extent + 1;
1723 return CFUUIDCreateFromUUIDBytes(kCFAllocatorSystemDefault, uuid);
1724 }
1725 case 0x0f:
1726 break;
1727 }
1728 return NULL;
1729 case kCFBinaryPlistMarkerInt: {
1730 int32_t err = CF_NO_ERROR;
1731 uint64_t cnt = 1 << (marker & 0x0f);
1732 const uint8_t *extent = check_ptr_add(*ptr, cnt, &err) - 1;
1733 if (CF_NO_ERROR != err) return NULL;
1734 if (objectsRangeEnd < extent) return NULL;
1735 if (16 < cnt) return NULL;
1736 // in format version '15', 1, 2, and 4-byte integers have to be interpreted as unsigned,
1737 // whereas 8-byte integers are signed (and 16-byte when available)
1738 // negative 1, 2, 4-byte integers are always emitted as 8 bytes in format '15'
1739 // integers are not required to be in the most compact possible representation, but only the last 64 bits are significant currently
1740 uint64_t bigint = _getSizedInt(*ptr, cnt);
1741 *ptr = extent + 1;
1742 CFPropertyListRef plist = NULL;
1743 if (8 < cnt) {
1744 CFSInt128Struct val;
1745 val.high = 0;
1746 val.low = bigint;
1747 plist = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt128Type, &val);
1748 } else {
1749 plist = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt64Type, &bigint);
1750 }
1751 return plist;
1752 }
1753 case kCFBinaryPlistMarkerReal:
1754 switch (marker & 0x0f) {
1755 case 2: {
1756 int32_t err = CF_NO_ERROR;
1757 const uint8_t *extent = check_ptr_add(*ptr, 4, &err) - 1;
1758 if (CF_NO_ERROR != err) return NULL;
1759 if (objectsRangeEnd < extent) return NULL;
1760 CFSwappedFloat32 swapped32;
1761 memmove(&swapped32, *ptr, 4);
1762 *ptr = extent + 1;
1763 float f = CFConvertFloat32SwappedToHost(swapped32);
1764 CFPropertyListRef plist = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberFloat32Type, &f);
1765 return plist;
1766 }
1767 case 3: {
1768 int32_t err = CF_NO_ERROR;
1769 const uint8_t *extent = check_ptr_add(*ptr, 8, &err) - 1;
1770 if (CF_NO_ERROR != err) return NULL;
1771 if (objectsRangeEnd < extent) return NULL;
1772 CFSwappedFloat64 swapped64;
1773 memmove(&swapped64, *ptr, 8);
1774 *ptr = extent + 1;
1775 double d = CFConvertFloat64SwappedToHost(swapped64);
1776 CFPropertyListRef plist = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberFloat64Type, &d);
1777 return plist;
1778 }
1779 }
1780 return NULL;
1781 case kCFBinaryPlistMarkerDate & 0xf0:
1782 switch (marker) {
1783 case kCFBinaryPlistMarkerDate: {
1784 int32_t err = CF_NO_ERROR;
1785 const uint8_t *extent = check_ptr_add(*ptr, 8, &err) - 1;
1786 if (CF_NO_ERROR != err) return NULL;
1787 if (objectsRangeEnd < extent) return NULL;
1788 CFSwappedFloat64 swapped64;
1789 memmove(&swapped64, *ptr, 8);
1790 *ptr = extent + 1;
1791 double d = CFConvertFloat64SwappedToHost(swapped64);
1792 CFPropertyListRef plist = CFDateCreate(kCFAllocatorSystemDefault, d);
1793 return plist;
1794 }
1795 }
1796 return NULL;
1797 case kCFBinaryPlistMarkerData: {
1798 int32_t err = CF_NO_ERROR;
1799 CFIndex cnt = marker & 0x0f;
1800 if (0xf == cnt) {
1801 uint64_t bigint = 0;
1802 if (!_readInt(*ptr, objectsRangeEnd, &bigint, ptr)) return NULL;
1803 if (LONG_MAX < bigint) return NULL;
1804 cnt = (CFIndex)bigint;
1805 }
1806 const uint8_t *extent = check_ptr_add(*ptr, cnt, &err) - 1;
1807 if (CF_NO_ERROR != err) return NULL;
1808 if (objectsRangeEnd < extent) return NULL;
1809 CFPropertyListRef plist = CFDataCreate(kCFAllocatorSystemDefault, *ptr, cnt);
1810 *ptr = extent + 1;
1811 return plist;
1812 }
1813 case kCFBinaryPlistMarkerASCIIString: {
1814 int32_t err = CF_NO_ERROR;
1815 CFIndex cnt = marker & 0x0f;
1816 if (0xf == cnt) {
1817 uint64_t bigint = 0;
1818 if (!_readInt(*ptr, objectsRangeEnd, &bigint, ptr)) return NULL;
1819 if (LONG_MAX < bigint) return NULL;
1820 cnt = (CFIndex)bigint;
1821 }
1822 const uint8_t *extent = check_ptr_add(*ptr, cnt, &err) - 1;
1823 if (CF_NO_ERROR != err) return NULL;
1824 if (objectsRangeEnd < extent) return NULL;
1825 CFPropertyListRef plist = CFStringCreateWithBytes(kCFAllocatorSystemDefault, *ptr, cnt, kCFStringEncodingASCII, false);
1826 *ptr = extent + 1;
1827 return plist;
1828 }
1829 case kCFBinaryPlistMarkerUnicode16String: {
1830 int32_t err = CF_NO_ERROR;
1831 CFIndex cnt = marker & 0x0f;
1832 if (0xf == cnt) {
1833 uint64_t bigint = 0;
1834 if (!_readInt(*ptr, objectsRangeEnd, &bigint, ptr)) return NULL;
1835 if (LONG_MAX < bigint) return NULL;
1836 cnt = (CFIndex)bigint;
1837 }
1838 const uint8_t *extent = check_ptr_add(*ptr, cnt, &err) - 1;
1839 extent = check_ptr_add(extent, cnt, &err); // 2 bytes per character
1840 if (CF_NO_ERROR != err) return NULL;
1841 if (objectsRangeEnd < extent) return NULL;
1842 size_t byte_cnt = check_size_t_mul(cnt, sizeof(UniChar), &err);
1843 if (CF_NO_ERROR != err) return NULL;
1844 UniChar *chars = (UniChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, byte_cnt, 0);
1845 if (!chars) return NULL;
1846 memmove(chars, *ptr, byte_cnt);
1847 for (CFIndex idx = 0; idx < cnt; idx++) {
1848 chars[idx] = CFSwapInt16BigToHost(chars[idx]);
1849 }
1850 CFPropertyListRef plist = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, chars, cnt);
1851 CFAllocatorDeallocate(kCFAllocatorSystemDefault, chars);
1852 *ptr = extent + 1;
1853 return plist;
1854 }
1855 case kCFBinaryPlistMarkerArray:
1856 case 0xb0: // ordset
1857 case kCFBinaryPlistMarkerSet:
1858 case kCFBinaryPlistMarkerDict: {
1859 int32_t err = CF_NO_ERROR;
1860 CFIndex cnt = marker & 0x0f;
1861 if (0xf == cnt) {
1862 uint64_t bigint = 0;
1863 if (!_readInt(*ptr, objectsRangeEnd, &bigint, ptr)) return NULL;
1864 if (LONG_MAX < bigint) return NULL;
1865 cnt = (CFIndex)bigint;
1866 }
1867 if ((marker & 0xf0) == kCFBinaryPlistMarkerDict) {
1868 cnt = check_size_t_mul(cnt, 2, &err);
1869 if (CF_NO_ERROR != err) return NULL;
1870 }
1871 size_t byte_cnt = check_size_t_mul(cnt, sizeof(CFPropertyListRef), &err); // check now for a later overflow
1872 if (CF_NO_ERROR != err) return NULL;
1873 CFPropertyListRef *list, buffer[256];
1874 list = (cnt <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, byte_cnt, 0);
1875 if (!list) return NULL;
1876
1877 CFMutableArrayRef tmparray = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
1878 for (CFIndex idx = 0; idx < cnt; idx++) {
1879 CFPropertyListRef pl = __CFBinaryPlistCreateObject15(databytes, datalen, ptr);
1880 if (!pl) {
1881 CFRelease(tmparray);
1882 return NULL;
1883 }
1884 if ((marker & 0xf0) == kCFBinaryPlistMarkerDict) {
1885 if (idx < cnt / 2 && !_plistIsPrimitive(pl)) {
1886 CFRelease(pl);
1887 CFRelease(tmparray);
1888 return NULL;
1889 }
1890 }
1891 CFArrayAppendValue(tmparray, pl);
1892 // CFRelease(pl); // don't release pl here, we're going to transfer the retain to the ultimate collection owner
1893 }
1894 CFArrayGetValues(tmparray, CFRangeMake(0, cnt), list);
1895 CFPropertyListRef plist = NULL;
1896 if ((marker & 0xf0) == kCFBinaryPlistMarkerArray) {
1897 plist = __CFArrayCreateTransfer(kCFAllocatorSystemDefault, list, cnt);
1898 } else if ((marker & 0xf0) == 0xb0) {
1899 plist = NULL; // Not actually implemented
1900 // leaks contents of tmparray, but this path shouldn't be exercised anyway
1901 } else if ((marker & 0xf0) == kCFBinaryPlistMarkerSet) {
1902 plist = __CFSetCreateTransfer(kCFAllocatorSystemDefault, list, cnt);
1903 } else {
1904 plist = __CFDictionaryCreateTransfer(kCFAllocatorSystemDefault, list, list + cnt / 2, cnt / 2);
1905 }
1906 CFRelease(tmparray);
1907 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
1908 return plist;
1909 }
1910 default:
1911 break;
1912 }
1913 return NULL;
1914 }
1915
1916 // *error is currently never set
1917 __private_extern__ CFPropertyListRef __CFBinaryPlistCreate15(CFDataRef data, CFErrorRef *error) {
1918 initStatics();
1919 const uint8_t *databytes = CFDataGetBytePtr(data);
1920 uint64_t datalen = CFDataGetLength(data);
1921 if (23 <= datalen) { // header is 22 bytes, plus 1 byte minimum for smallest object
1922 const uint8_t *ptr = databytes;
1923 if (0 != memcmp((uint8_t *)"bplist15", ptr, 8)) return NULL;
1924 ptr += 8;
1925 if (*ptr != (kCFBinaryPlistMarkerInt | 3)) return NULL;
1926 ptr += 1;
1927 uint64_t swapped_len;
1928 memmove(&swapped_len, ptr, 8);
1929 uint64_t bytelen = CFSwapInt64BigToHost(swapped_len);
1930 if (bytelen != datalen) return NULL;
1931 ptr += 8;
1932 if (*ptr != (kCFBinaryPlistMarkerInt | 2)) return NULL;
1933 ptr += 5; // skip crc
1934 CFPropertyListRef pl = __CFBinaryPlistCreateObject15(databytes, datalen, &ptr);
1935 // ptr should equal (databytes+datalen) if there is no junk at the end of top-level object
1936 return pl;
1937 }
1938 return NULL;
1939 }
1940