]> git.saurik.com Git - apple/cf.git/blob - CFBinaryPList.c
CF-1152.14.tar.gz
[apple/cf.git] / CFBinaryPList.c
1 /*
2 * Copyright (c) 2015 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-2014, 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 uint64_t __check_uint64_add_unsigned_unsigned(uint64_t x, uint64_t y, int32_t* err) {
65 if((ULLONG_MAX - y) < x)
66 *err = *err | CF_OVERFLOW_ERROR;
67 return x + y;
68 };
69
70 CF_INLINE uint64_t __check_uint64_mul_unsigned_unsigned(uint64_t x, uint64_t y, int32_t* err) {
71 if(x == 0) return 0;
72 if(ULLONG_MAX/x < y)
73 *err = *err | CF_OVERFLOW_ERROR;
74 return x * y;
75 };
76
77 #if __LP64__
78 #define check_ptr_add(p, a, err) (const uint8_t *)__check_uint64_add_unsigned_unsigned((uintptr_t)p, (uintptr_t)a, err)
79 #define check_size_t_mul(b, a, err) (size_t)__check_uint64_mul_unsigned_unsigned((size_t)b, (size_t)a, err)
80 #else
81
82 CF_INLINE uint32_t __check_uint32_add_unsigned_unsigned(uint32_t x, uint32_t y, int32_t* err) {
83 if((UINT_MAX - y) < x)
84 *err = *err | CF_OVERFLOW_ERROR;
85 return x + y;
86 };
87
88 CF_INLINE uint32_t __check_uint32_mul_unsigned_unsigned(uint32_t x, uint32_t y, int32_t* err) {
89 uint64_t tmp = (uint64_t) x * (uint64_t) y;
90 /* If any of the upper 32 bits touched, overflow */
91 if(tmp & 0xffffffff00000000ULL)
92 *err = *err | CF_OVERFLOW_ERROR;
93 return (uint32_t) tmp;
94 };
95
96 #define check_ptr_add(p, a, err) (const uint8_t *)__check_uint32_add_unsigned_unsigned((uintptr_t)p, (uintptr_t)a, err)
97 #define check_size_t_mul(b, a, err) (size_t)__check_uint32_mul_unsigned_unsigned((size_t)b, (size_t)a, err)
98 #endif
99
100 #pragma mark -
101 #pragma mark Keyed Archiver UID
102
103 struct __CFKeyedArchiverUID {
104 CFRuntimeBase _base;
105 uint32_t _value;
106 };
107
108 static CFStringRef __CFKeyedArchiverUIDCopyDescription(CFTypeRef cf) {
109 CFKeyedArchiverUIDRef uid = (CFKeyedArchiverUIDRef)cf;
110 return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFKeyedArchiverUID %p [%p]>{value = %u}"), cf, CFGetAllocator(cf), uid->_value);
111 }
112
113 static CFStringRef __CFKeyedArchiverUIDCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
114 CFKeyedArchiverUIDRef uid = (CFKeyedArchiverUIDRef)cf;
115 return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("@%u@"), uid->_value);
116 }
117
118 static CFTypeID __kCFKeyedArchiverUIDTypeID = _kCFRuntimeNotATypeID;
119
120 static const CFRuntimeClass __CFKeyedArchiverUIDClass = {
121 0,
122 "CFKeyedArchiverUID",
123 NULL, // init
124 NULL, // copy
125 NULL, // finalize
126 NULL, // equal -- pointer equality only
127 NULL, // hash -- pointer hashing only
128 __CFKeyedArchiverUIDCopyFormattingDescription,
129 __CFKeyedArchiverUIDCopyDescription
130 };
131
132 CFTypeID _CFKeyedArchiverUIDGetTypeID(void) {
133 static dispatch_once_t initOnce;
134 dispatch_once(&initOnce, ^{ __kCFKeyedArchiverUIDTypeID = _CFRuntimeRegisterClass(&__CFKeyedArchiverUIDClass); });
135 return __kCFKeyedArchiverUIDTypeID;
136 }
137
138 CFKeyedArchiverUIDRef _CFKeyedArchiverUIDCreate(CFAllocatorRef allocator, uint32_t value) {
139 CFKeyedArchiverUIDRef uid;
140 uid = (CFKeyedArchiverUIDRef)_CFRuntimeCreateInstance(allocator, _CFKeyedArchiverUIDGetTypeID(), sizeof(struct __CFKeyedArchiverUID) - sizeof(CFRuntimeBase), NULL);
141 if (NULL == uid) {
142 return NULL;
143 }
144 ((struct __CFKeyedArchiverUID *)uid)->_value = value;
145 return uid;
146 }
147
148
149 uint32_t _CFKeyedArchiverUIDGetValue(CFKeyedArchiverUIDRef uid) {
150 return uid->_value;
151 }
152
153 #pragma mark -
154 #pragma mark Writing
155
156 CF_PRIVATE CFErrorRef __CFPropertyListCreateError(CFIndex code, CFStringRef debugString, ...);
157
158 typedef struct {
159 CFTypeRef stream;
160 void *databytes;
161 uint64_t datalen;
162 CFErrorRef error;
163 uint64_t written;
164 int32_t used;
165 bool streamIsData;
166 uint8_t buffer[8192 - 32];
167 } __CFBinaryPlistWriteBuffer;
168
169 static void writeBytes(__CFBinaryPlistWriteBuffer *buf, const UInt8 *bytes, CFIndex length) {
170 if (length <= 0) return;
171 if (buf->error) return;
172 if (buf->databytes) {
173 int32_t err = CF_NO_ERROR;
174 uint64_t tmpSum = __check_uint64_add_unsigned_unsigned(buf->written, (uint64_t)length, &err);
175 if ((CF_NO_ERROR != err) || buf->datalen < tmpSum) {
176 buf->error = __CFPropertyListCreateError(kCFPropertyListWriteStreamError, CFSTR("Binary property list writing could not be completed because databytes is full."));
177 return;
178 }
179 memmove((char *)buf->databytes + buf->written, bytes, length);
180 }
181 if (buf->streamIsData) {
182 if (buf->stream) CFDataAppendBytes((CFMutableDataRef)buf->stream, bytes, length);
183 buf->written += length;
184 } else {
185 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
186 while (0 < length) {
187 CFIndex ret = buf->stream ? CFWriteStreamWrite((CFWriteStreamRef)buf->stream, bytes, length) : length;
188 if (ret == 0) {
189 buf->error = __CFPropertyListCreateError(kCFPropertyListWriteStreamError, CFSTR("Binary property list writing could not be completed because stream is full."));
190 return;
191 }
192 if (ret < 0) {
193 CFErrorRef err = buf->stream ? CFWriteStreamCopyError((CFWriteStreamRef)buf->stream) : NULL;
194 buf->error = err ? err : __CFPropertyListCreateError(kCFPropertyListWriteStreamError, CFSTR("Binary property list writing could not be completed because the stream had an unknown error."));
195 return;
196 }
197 buf->written += ret;
198 length -= ret;
199 bytes += ret;
200 }
201 #else
202 CFAssert(false, __kCFLogAssertion, "Streams are not supported on this platform");
203 #endif
204 }
205 }
206
207 static void bufferFlush(__CFBinaryPlistWriteBuffer *buf) {
208 writeBytes(buf, buf->buffer, buf->used);
209 buf->used = 0;
210 }
211
212 static void bufferWrite(__CFBinaryPlistWriteBuffer *buf, const uint8_t *buffer, CFIndex count) {
213 if (0 == count) return;
214 if ((CFIndex)sizeof(buf->buffer) <= count) {
215 bufferFlush(buf);
216 writeBytes(buf, buffer, count);
217 return;
218 }
219 CFIndex copyLen = __CFMin(count, (CFIndex)sizeof(buf->buffer) - buf->used);
220 if (buf->stream || buf->databytes) {
221 switch (copyLen) {
222 case 4: buf->buffer[buf->used + 3] = buffer[3]; /* FALLTHROUGH */
223 case 3: buf->buffer[buf->used + 2] = buffer[2]; /* FALLTHROUGH */
224 case 2: buf->buffer[buf->used + 1] = buffer[1]; /* FALLTHROUGH */
225 case 1: buf->buffer[buf->used] = buffer[0]; break;
226 default: memmove(buf->buffer + buf->used, buffer, copyLen);
227 }
228 }
229 buf->used += copyLen;
230 if (sizeof(buf->buffer) == buf->used) {
231 writeBytes(buf, buf->buffer, sizeof(buf->buffer));
232 if (buf->stream || buf->databytes) {
233 memmove(buf->buffer, buffer + copyLen, count - copyLen);
234 }
235 buf->used = count - copyLen;
236 }
237 }
238
239 /*
240 HEADER
241 magic number ("bplist")
242 file format version (currently "0?")
243
244 OBJECT TABLE
245 variable-sized objects
246
247 Object Formats (marker byte followed by additional info in some cases)
248 null 0000 0000 // null object [v"1?"+ only]
249 bool 0000 1000 // false
250 bool 0000 1001 // true
251 url 0000 1100 string // URL with no base URL, recursive encoding of URL string [v"1?"+ only]
252 url 0000 1101 base string // URL with base URL, recursive encoding of base URL, then recursive encoding of URL string [v"1?"+ only]
253 uuid 0000 1110 // 16-byte UUID [v"1?"+ only]
254 fill 0000 1111 // fill byte
255 int 0001 0nnn ... // # of bytes is 2^nnn, big-endian bytes
256 real 0010 0nnn ... // # of bytes is 2^nnn, big-endian bytes
257 date 0011 0011 ... // 8 byte float follows, big-endian bytes
258 data 0100 nnnn [int] ... // nnnn is number of bytes unless 1111 then int count follows, followed by bytes
259 string 0101 nnnn [int] ... // ASCII string, nnnn is # of chars, else 1111 then int count, then bytes
260 string 0110 nnnn [int] ... // Unicode string, nnnn is # of chars, else 1111 then int count, then big-endian 2-byte uint16_t
261 string 0111 nnnn [int] ... // UTF8 string, nnnn is # of chars, else 1111 then int count, then bytes [v"1?"+ only]
262 uid 1000 nnnn ... // nnnn+1 is # of bytes
263 1001 xxxx // unused
264 array 1010 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows
265 ordset 1011 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows [v"1?"+ only]
266 set 1100 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows [v"1?"+ only]
267 dict 1101 nnnn [int] keyref* objref* // nnnn is count, unless '1111', then int count follows
268 1110 xxxx // unused
269 1111 xxxx // unused
270
271 OFFSET TABLE
272 list of ints, byte size of which is given in trailer
273 -- these are the byte offsets into the file
274 -- number of these is in the trailer
275
276 TRAILER
277 byte size of offset ints in offset table
278 byte size of object refs in arrays and dicts
279 number of offsets in offset table (also is number of objects)
280 element # in offset table which is top level object
281 offset table offset
282
283 */
284
285
286 static CFTypeID stringtype = -1, datatype = -1, numbertype = -1, datetype = -1;
287 static CFTypeID booltype = -1, nulltype = -1, dicttype = -1, arraytype = -1;
288 static CFTypeID uuidtype = -1, urltype = -1, osettype = -1, settype = -1;
289
290 static void initStatics() {
291 if ((CFTypeID)-1 == stringtype) {
292 stringtype = CFStringGetTypeID();
293 }
294 if ((CFTypeID)-1 == datatype) {
295 datatype = CFDataGetTypeID();
296 }
297 if ((CFTypeID)-1 == numbertype) {
298 numbertype = CFNumberGetTypeID();
299 }
300 if ((CFTypeID)-1 == booltype) {
301 booltype = CFBooleanGetTypeID();
302 }
303 if ((CFTypeID)-1 == datetype) {
304 datetype = CFDateGetTypeID();
305 }
306 if ((CFTypeID)-1 == dicttype) {
307 dicttype = CFDictionaryGetTypeID();
308 }
309 if ((CFTypeID)-1 == arraytype) {
310 arraytype = CFArrayGetTypeID();
311 }
312 if ((CFTypeID)-1 == settype) {
313 settype = CFSetGetTypeID();
314 }
315 if ((CFTypeID)-1 == nulltype) {
316 nulltype = CFNullGetTypeID();
317 }
318 if ((CFTypeID)-1 == uuidtype) {
319 uuidtype = CFUUIDGetTypeID();
320 }
321 if ((CFTypeID)-1 == urltype) {
322 urltype = CFURLGetTypeID();
323 }
324 if ((CFTypeID)-1 == osettype) {
325 osettype = -1;
326 }
327 }
328
329 static void _appendInt(__CFBinaryPlistWriteBuffer *buf, uint64_t bigint) {
330 uint8_t marker;
331 uint8_t *bytes;
332 CFIndex nbytes;
333 if (bigint <= (uint64_t)0xff) {
334 nbytes = 1;
335 marker = kCFBinaryPlistMarkerInt | 0;
336 } else if (bigint <= (uint64_t)0xffff) {
337 nbytes = 2;
338 marker = kCFBinaryPlistMarkerInt | 1;
339 } else if (bigint <= (uint64_t)0xffffffff) {
340 nbytes = 4;
341 marker = kCFBinaryPlistMarkerInt | 2;
342 } else {
343 nbytes = 8;
344 marker = kCFBinaryPlistMarkerInt | 3;
345 }
346 bigint = CFSwapInt64HostToBig(bigint);
347 bytes = (uint8_t *)&bigint + sizeof(bigint) - nbytes;
348 bufferWrite(buf, &marker, 1);
349 bufferWrite(buf, bytes, nbytes);
350 }
351
352 static void _appendUID(__CFBinaryPlistWriteBuffer *buf, CFKeyedArchiverUIDRef uid) {
353 uint8_t marker;
354 uint8_t *bytes;
355 CFIndex nbytes;
356 uint64_t bigint = _CFKeyedArchiverUIDGetValue(uid);
357 if (bigint <= (uint64_t)0xff) {
358 nbytes = 1;
359 } else if (bigint <= (uint64_t)0xffff) {
360 nbytes = 2;
361 } else if (bigint <= (uint64_t)0xffffffff) {
362 nbytes = 4;
363 } else {
364 nbytes = 8;
365 }
366 marker = kCFBinaryPlistMarkerUID | (uint8_t)(nbytes - 1);
367 bigint = CFSwapInt64HostToBig(bigint);
368 bytes = (uint8_t *)&bigint + sizeof(bigint) - nbytes;
369 bufferWrite(buf, &marker, 1);
370 bufferWrite(buf, bytes, nbytes);
371 }
372
373 static void _appendString(__CFBinaryPlistWriteBuffer *buf, CFStringRef str) {
374 CFIndex ret, count = CFStringGetLength(str);
375 CFIndex needed, idx2;
376 uint8_t *bytes, buffer[1024];
377 bytes = (count <= 1024) ? buffer : (uint8_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault, count, 0);
378 // presumption, believed to be true, is that ASCII encoding may need
379 // less bytes, but will not need greater, than the # of unichars
380 ret = CFStringGetBytes(str, CFRangeMake(0, count), kCFStringEncodingASCII, 0, false, bytes, count, &needed);
381 if (ret == count) {
382 uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerASCIIString | (needed < 15 ? needed : 0xf));
383 bufferWrite(buf, &marker, 1);
384 if (15 <= needed) {
385 _appendInt(buf, (uint64_t)needed);
386 }
387 bufferWrite(buf, bytes, needed);
388 } else {
389 UniChar *chars;
390 uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerUnicode16String | (count < 15 ? count : 0xf));
391 bufferWrite(buf, &marker, 1);
392 if (15 <= count) {
393 _appendInt(buf, (uint64_t)count);
394 }
395 chars = (UniChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, count * sizeof(UniChar), 0);
396 CFStringGetCharacters(str, CFRangeMake(0, count), chars);
397 for (idx2 = 0; idx2 < count; idx2++) {
398 chars[idx2] = CFSwapInt16HostToBig(chars[idx2]);
399 }
400 bufferWrite(buf, (uint8_t *)chars, count * sizeof(UniChar));
401 CFAllocatorDeallocate(kCFAllocatorSystemDefault, chars);
402 }
403 if (bytes != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, bytes);
404 }
405
406 CF_EXPORT CFNumberType _CFNumberGetType2(CFNumberRef number);
407
408 static void _appendNumber(__CFBinaryPlistWriteBuffer *buf, CFNumberRef num) {
409 uint8_t marker;
410 uint64_t bigint;
411 uint8_t *bytes;
412 CFIndex nbytes;
413 if (CFNumberIsFloatType(num)) {
414 CFSwappedFloat64 swapped64;
415 CFSwappedFloat32 swapped32;
416 if (CFNumberGetByteSize(num) <= (CFIndex)sizeof(float)) {
417 float v;
418 CFNumberGetValue(num, kCFNumberFloat32Type, &v);
419 swapped32 = CFConvertFloat32HostToSwapped(v);
420 bytes = (uint8_t *)&swapped32;
421 nbytes = sizeof(float);
422 marker = kCFBinaryPlistMarkerReal | 2;
423 } else {
424 double v;
425 CFNumberGetValue(num, kCFNumberFloat64Type, &v);
426 swapped64 = CFConvertFloat64HostToSwapped(v);
427 bytes = (uint8_t *)&swapped64;
428 nbytes = sizeof(double);
429 marker = kCFBinaryPlistMarkerReal | 3;
430 }
431 bufferWrite(buf, &marker, 1);
432 bufferWrite(buf, bytes, nbytes);
433 } else {
434 CFNumberType type = _CFNumberGetType2(num);
435 if (kCFNumberSInt128Type == type) {
436 CFSInt128Struct s;
437 CFNumberGetValue(num, kCFNumberSInt128Type, &s);
438 struct {
439 int64_t high;
440 uint64_t low;
441 } storage;
442 storage.high = CFSwapInt64HostToBig(s.high);
443 storage.low = CFSwapInt64HostToBig(s.low);
444 uint8_t *bytes = (uint8_t *)&storage;
445 uint8_t marker = kCFBinaryPlistMarkerInt | 4;
446 CFIndex nbytes = 16;
447 bufferWrite(buf, &marker, 1);
448 bufferWrite(buf, bytes, nbytes);
449 } else {
450 CFNumberGetValue(num, kCFNumberSInt64Type, &bigint);
451 _appendInt(buf, bigint);
452 }
453 }
454 }
455
456 static Boolean _appendObject(__CFBinaryPlistWriteBuffer *buf, CFTypeRef obj, CFDictionaryRef objtable, uint32_t objRefSize) {
457 uint64_t refnum;
458 CFIndex idx2;
459 CFTypeID type = CFGetTypeID(obj);
460 if (stringtype == type) {
461 _appendString(buf, (CFStringRef)obj);
462 } else if (numbertype == type) {
463 _appendNumber(buf, (CFNumberRef)obj);
464 } else if (booltype == type) {
465 uint8_t marker = CFBooleanGetValue((CFBooleanRef)obj) ? kCFBinaryPlistMarkerTrue : kCFBinaryPlistMarkerFalse;
466 bufferWrite(buf, &marker, 1);
467 } else if (datatype == type) {
468 CFIndex count = CFDataGetLength((CFDataRef)obj);
469 uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerData | (count < 15 ? count : 0xf));
470 bufferWrite(buf, &marker, 1);
471 if (15 <= count) {
472 _appendInt(buf, (uint64_t)count);
473 }
474 bufferWrite(buf, CFDataGetBytePtr((CFDataRef)obj), count);
475 } else if (datetype == type) {
476 CFSwappedFloat64 swapped;
477 uint8_t marker = kCFBinaryPlistMarkerDate;
478 bufferWrite(buf, &marker, 1);
479 swapped = CFConvertFloat64HostToSwapped(CFDateGetAbsoluteTime((CFDateRef)obj));
480 bufferWrite(buf, (uint8_t *)&swapped, sizeof(swapped));
481 } else if (dicttype == type) {
482 CFIndex count = CFDictionaryGetCount((CFDictionaryRef)obj);
483 uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerDict | (count < 15 ? count : 0xf));
484 bufferWrite(buf, &marker, 1);
485 if (15 <= count) {
486 _appendInt(buf, (uint64_t)count);
487 }
488 CFPropertyListRef *list, buffer[512];
489 list = (count <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, 2 * count * sizeof(CFTypeRef), __kCFAllocatorGCScannedMemory);
490 CFDictionaryGetKeysAndValues((CFDictionaryRef)obj, list, list + count);
491 for (idx2 = 0; idx2 < 2 * count; idx2++) {
492 CFPropertyListRef value = list[idx2];
493 if (objtable) {
494 uint32_t swapped = 0;
495 uint8_t *source = (uint8_t *)&swapped;
496 refnum = (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable, value);
497 swapped = CFSwapInt32HostToBig((uint32_t)refnum);
498 bufferWrite(buf, source + sizeof(swapped) - objRefSize, objRefSize);
499 } else {
500 Boolean ret = _appendObject(buf, value, objtable, objRefSize);
501 if (!ret) {
502 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
503 return false;
504 }
505 }
506 }
507 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
508 } else if (arraytype == type) {
509 CFIndex count = CFArrayGetCount((CFArrayRef)obj);
510 CFPropertyListRef *list, buffer[256];
511 uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerArray | (count < 15 ? count : 0xf));
512 bufferWrite(buf, &marker, 1);
513 if (15 <= count) {
514 _appendInt(buf, (uint64_t)count);
515 }
516 list = (count <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, count * sizeof(CFTypeRef), __kCFAllocatorGCScannedMemory);
517 CFArrayGetValues((CFArrayRef)obj, CFRangeMake(0, count), list);
518 for (idx2 = 0; idx2 < count; idx2++) {
519 CFPropertyListRef value = list[idx2];
520 if (objtable) {
521 uint32_t swapped = 0;
522 uint8_t *source = (uint8_t *)&swapped;
523 refnum = (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable, value);
524 swapped = CFSwapInt32HostToBig((uint32_t)refnum);
525 bufferWrite(buf, source + sizeof(swapped) - objRefSize, objRefSize);
526 } else {
527 Boolean ret = _appendObject(buf, value, objtable, objRefSize);
528 if (!ret) {
529 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
530 return false;
531 }
532 }
533 }
534 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
535 } else if (_CFKeyedArchiverUIDGetTypeID() == type) {
536 _appendUID(buf, (CFKeyedArchiverUIDRef)obj);
537 } else {
538 return false;
539 }
540 return true;
541 }
542
543 static void _flattenPlist(CFPropertyListRef plist, CFMutableArrayRef objlist, CFMutableDictionaryRef objtable, CFMutableSetRef uniquingset) {
544 uint32_t refnum;
545 CFTypeID type = CFGetTypeID(plist);
546
547 // Do not unique dictionaries or arrays, because: they
548 // are slow to compare, and have poor hash codes.
549 // Uniquing bools is unnecessary.
550 if (stringtype == type || numbertype == type || datetype == type || datatype == type) {
551 CFIndex before = CFSetGetCount(uniquingset);
552 CFSetAddValue(uniquingset, plist);
553 CFIndex after = CFSetGetCount(uniquingset);
554 if (after == before) { // already in set
555 CFPropertyListRef unique = CFSetGetValue(uniquingset, plist);
556 if (unique != plist) {
557 refnum = (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable, unique);
558 CFDictionaryAddValue(objtable, plist, (const void *)(uintptr_t)refnum);
559 }
560 return;
561 }
562 }
563 refnum = CFArrayGetCount(objlist);
564 CFArrayAppendValue(objlist, plist);
565 CFDictionaryAddValue(objtable, plist, (const void *)(uintptr_t)refnum);
566 if (dicttype == type) {
567 CFIndex count = CFDictionaryGetCount((CFDictionaryRef)plist);
568 STACK_BUFFER_DECL(CFPropertyListRef, buffer, count <= 128 ? count * 2 : 1);
569 CFPropertyListRef *list = (count <= 128) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, 2 * count * sizeof(CFTypeRef), __kCFAllocatorGCScannedMemory);
570 CFDictionaryGetKeysAndValues((CFDictionaryRef)plist, list, list + count);
571 for (CFIndex idx = 0; idx < 2 * count; idx++) {
572 _flattenPlist(list[idx], objlist, objtable, uniquingset);
573 }
574 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
575 } else if (arraytype == type) {
576 CFIndex count = CFArrayGetCount((CFArrayRef)plist);
577 STACK_BUFFER_DECL(CFPropertyListRef, buffer, count <= 256 ? count : 1);
578 CFPropertyListRef *list = (count <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, count * sizeof(CFTypeRef), __kCFAllocatorGCScannedMemory);
579 CFArrayGetValues((CFArrayRef)plist, CFRangeMake(0, count), list);
580 for (CFIndex idx = 0; idx < count; idx++) {
581 _flattenPlist(list[idx], objlist, objtable, uniquingset);
582 }
583 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
584 }
585 }
586
587 /* Get the number of bytes required to hold the value in 'count'. Will return a power of 2 value big enough to hold 'count'.
588 */
589 CF_INLINE uint8_t _byteCount(uint64_t count) {
590 uint64_t mask = ~(uint64_t)0;
591 uint8_t size = 0;
592
593 // Find something big enough to hold 'count'
594 while (count & mask) {
595 size++;
596 mask = mask << 8;
597 }
598
599 // Ensure that 'count' is a power of 2
600 // For sizes bigger than 8, just use the required count
601 while ((size != 1 && size != 2 && size != 4 && size != 8) && size <= 8) {
602 size++;
603 }
604
605 return size;
606 }
607
608 // stream can be a CFWriteStreamRef (on supported platforms) or a CFMutableDataRef
609 /* 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. */
610 CFIndex __CFBinaryPlistWrite(CFPropertyListRef plist, CFTypeRef stream, uint64_t estimate, CFOptionFlags options, CFErrorRef *error) {
611 CFMutableDictionaryRef objtable = NULL;
612 CFMutableArrayRef objlist = NULL;
613 CFMutableSetRef uniquingset = NULL;
614 CFBinaryPlistTrailer trailer;
615 uint64_t *offsets, length_so_far;
616 int64_t idx, cnt;
617 __CFBinaryPlistWriteBuffer *buf;
618
619 initStatics();
620
621 const CFDictionaryKeyCallBacks dictKeyCallbacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, 0, 0, 0};
622 objtable = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &dictKeyCallbacks, NULL);
623
624 const CFArrayCallBacks arrayCallbacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, 0, 0};
625 objlist = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &arrayCallbacks);
626
627
628 const CFSetCallBacks setCallbacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, 0, 0, 0};
629 uniquingset = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &setCallbacks);
630
631 #if DEPLOYMENT_TARGET_MACOSX
632 _CFDictionarySetCapacity(objtable, estimate ? estimate : 650);
633 _CFArraySetCapacity(objlist, estimate ? estimate : 650);
634 _CFSetSetCapacity(uniquingset, estimate ? estimate : 1000);
635 #endif
636
637 _flattenPlist(plist, objlist, objtable, uniquingset);
638
639 CFRelease(uniquingset);
640
641 cnt = CFArrayGetCount(objlist);
642 offsets = (uint64_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault, (CFIndex)(cnt * sizeof(*offsets)), 0);
643
644 buf = (__CFBinaryPlistWriteBuffer *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(__CFBinaryPlistWriteBuffer), 0);
645 buf->stream = stream;
646 buf->databytes = NULL;
647 buf->datalen = 0;
648 buf->error = NULL;
649 buf->streamIsData = (CFGetTypeID(stream) == CFDataGetTypeID());
650 buf->written = 0;
651 buf->used = 0;
652 bufferWrite(buf, (uint8_t *)"bplist00", 8); // header
653
654 memset(&trailer, 0, sizeof(trailer));
655 trailer._numObjects = CFSwapInt64HostToBig(cnt);
656 trailer._topObject = 0; // true for this implementation
657 trailer._objectRefSize = _byteCount(cnt);
658 for (idx = 0; idx < cnt; idx++) {
659 offsets[idx] = buf->written + buf->used;
660 CFPropertyListRef obj = CFArrayGetValueAtIndex(objlist, (CFIndex)idx);
661 Boolean success = _appendObject(buf, obj, objtable, trailer._objectRefSize);
662 if (!success) {
663 CFRelease(objtable);
664 CFRelease(objlist);
665 if (error && buf->error) {
666 // caller will release error
667 *error = buf->error;
668 } else if (buf->error) {
669 // caller is not interested in error, release it here
670 CFRelease(buf->error);
671 }
672 CFAllocatorDeallocate(kCFAllocatorSystemDefault, buf);
673 CFAllocatorDeallocate(kCFAllocatorSystemDefault, offsets);
674 return 0;
675 }
676 }
677 CFRelease(objtable);
678 CFRelease(objlist);
679
680 length_so_far = buf->written + buf->used;
681 trailer._offsetTableOffset = CFSwapInt64HostToBig(length_so_far);
682 trailer._offsetIntSize = _byteCount(length_so_far);
683
684 for (idx = 0; idx < cnt; idx++) {
685 uint64_t swapped = CFSwapInt64HostToBig(offsets[idx]);
686 uint8_t *source = (uint8_t *)&swapped;
687 bufferWrite(buf, source + sizeof(*offsets) - trailer._offsetIntSize, trailer._offsetIntSize);
688 }
689 length_so_far += cnt * trailer._offsetIntSize;
690 CFAllocatorDeallocate(kCFAllocatorSystemDefault, offsets);
691
692 bufferWrite(buf, (uint8_t *)&trailer, sizeof(trailer));
693 bufferFlush(buf);
694 length_so_far += sizeof(trailer);
695 if (buf->error) {
696 if (error) {
697 // caller will release error
698 *error = buf->error;
699 } else {
700 CFRelease(buf->error);
701 }
702 CFAllocatorDeallocate(kCFAllocatorSystemDefault, buf);
703 return 0;
704 }
705 CFAllocatorDeallocate(kCFAllocatorSystemDefault, buf);
706 return (CFIndex)length_so_far;
707 }
708
709
710 CFIndex __CFBinaryPlistWriteToStream(CFPropertyListRef plist, CFTypeRef stream) {
711 return __CFBinaryPlistWrite(plist, stream, 0, 0, NULL);
712 }
713
714 // to be removed soon
715 CFIndex __CFBinaryPlistWriteToStreamWithEstimate(CFPropertyListRef plist, CFTypeRef stream, uint64_t estimate) {
716 return __CFBinaryPlistWrite(plist, stream, estimate, 0, NULL);
717 }
718
719 // to be removed soon
720 CFIndex __CFBinaryPlistWriteToStreamWithOptions(CFPropertyListRef plist, CFTypeRef stream, uint64_t estimate, CFOptionFlags options) {
721 return __CFBinaryPlistWrite(plist, stream, estimate, options, NULL);
722 }
723
724
725 #pragma mark -
726 #pragma mark Reading
727
728 #define FAIL_FALSE do { return false; } while (0)
729 #define FAIL_MAXOFFSET do { return UINT64_MAX; } while (0)
730
731 CF_PRIVATE 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);
732
733 /* Grab a valSize-bytes integer out of the buffer pointed at by data and return it.
734 */
735 CF_INLINE uint64_t _getSizedInt(const uint8_t *data, uint8_t valSize) {
736 #if defined(__i386__) || defined(__x86_64__)
737 if (valSize == 1) {
738 return (uint64_t)*data;
739 } else if (valSize == 2) {
740 uint16_t val = *(uint16_t *)data;
741 return (uint64_t)CFSwapInt16BigToHost(val);
742 } else if (valSize == 4) {
743 uint32_t val = *(uint32_t *)data;
744 return (uint64_t)CFSwapInt32BigToHost(val);
745 } else if (valSize == 8) {
746 uint64_t val = *(uint64_t *)data;
747 return CFSwapInt64BigToHost(val);
748 }
749 #endif
750 // Compatability with existing archives, including anything with a non-power-of-2
751 // size and 16-byte values, and architectures that don't support unaligned access
752 uint64_t res = 0;
753 for (CFIndex idx = 0; idx < valSize; idx++) {
754 res = (res << 8) + data[idx];
755 }
756 return res;
757 }
758
759 bool __CFBinaryPlistGetTopLevelInfo(const uint8_t *databytes, uint64_t datalen, uint8_t *marker, uint64_t *offset, CFBinaryPlistTrailer *trailer) {
760 CFBinaryPlistTrailer trail;
761
762 initStatics();
763
764 if (!databytes || datalen < sizeof(trail) + 8 + 1) FAIL_FALSE;
765 // Tiger and earlier will parse "bplist00"
766 // Leopard will parse "bplist00" or "bplist01"
767 // SnowLeopard will parse "bplist0?" where ? is any one character
768 if (0 != memcmp("bplist0", databytes, 7)) {
769 FAIL_FALSE;
770 }
771 memmove(&trail, databytes + datalen - sizeof(trail), sizeof(trail));
772 // In Leopard, the unused bytes in the trailer must be 0 or the parse will fail
773 // This check is not present in Tiger and earlier or after Leopard
774 trail._numObjects = CFSwapInt64BigToHost(trail._numObjects);
775 trail._topObject = CFSwapInt64BigToHost(trail._topObject);
776 trail._offsetTableOffset = CFSwapInt64BigToHost(trail._offsetTableOffset);
777
778 // Don't overflow on the number of objects or offset of the table
779 if (LONG_MAX < trail._numObjects) FAIL_FALSE;
780 if (LONG_MAX < trail._offsetTableOffset) FAIL_FALSE;
781
782 // Must be a minimum of 1 object
783 if (trail._numObjects < 1) FAIL_FALSE;
784
785 // The ref to the top object must be a value in the range of 1 to the total number of objects
786 if (trail._numObjects <= trail._topObject) FAIL_FALSE;
787
788 // The offset table must be after at least 9 bytes of other data ('bplist??' + 1 byte of object table data).
789 if (trail._offsetTableOffset < 9) FAIL_FALSE;
790
791 // The trailer must point to a value before itself in the data.
792 if (datalen - sizeof(trail) <= trail._offsetTableOffset) FAIL_FALSE;
793
794 // Minimum of 1 byte for the size of integers and references in the data
795 if (trail._offsetIntSize < 1) FAIL_FALSE;
796 if (trail._objectRefSize < 1) FAIL_FALSE;
797
798 int32_t err = CF_NO_ERROR;
799
800 // The total size of the offset table (number of objects * size of each int in the table) must not overflow
801 uint64_t offsetIntSize = trail._offsetIntSize;
802 uint64_t offsetTableSize = __check_uint64_mul_unsigned_unsigned(trail._numObjects, offsetIntSize, &err);
803 if (CF_NO_ERROR!= err) FAIL_FALSE;
804
805 // The offset table must have at least 1 entry
806 if (offsetTableSize < 1) FAIL_FALSE;
807
808 // Make sure the size of the offset table and data sections do not overflow
809 uint64_t objectDataSize = trail._offsetTableOffset - 8;
810 uint64_t tmpSum = __check_uint64_add_unsigned_unsigned(8, objectDataSize, &err);
811 tmpSum = __check_uint64_add_unsigned_unsigned(tmpSum, offsetTableSize, &err);
812 tmpSum = __check_uint64_add_unsigned_unsigned(tmpSum, sizeof(trail), &err);
813 if (CF_NO_ERROR != err) FAIL_FALSE;
814
815 // The total size of the data should be equal to the sum of offsetTableOffset + sizeof(trailer)
816 if (datalen != tmpSum) FAIL_FALSE;
817
818 // 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.
819 if (trail._objectRefSize < 8 && (1ULL << (8 * trail._objectRefSize)) <= trail._numObjects) FAIL_FALSE;
820
821 // The integers used for pointers in the offset table must be able to reach as far as the start of the offset table.
822 if (trail._offsetIntSize < 8 && (1ULL << (8 * trail._offsetIntSize)) <= trail._offsetTableOffset) FAIL_FALSE;
823
824
825 (void)check_ptr_add(databytes, 8, &err);
826 if (CF_NO_ERROR != err) FAIL_FALSE;
827 const uint8_t *offsetsFirstByte = check_ptr_add(databytes, trail._offsetTableOffset, &err);
828 if (CF_NO_ERROR != err) FAIL_FALSE;
829 (void)check_ptr_add(offsetsFirstByte, offsetTableSize - 1, &err);
830 if (CF_NO_ERROR != err) FAIL_FALSE;
831
832 const uint8_t *bytesptr = databytes + trail._offsetTableOffset;
833 uint64_t maxOffset = trail._offsetTableOffset - 1;
834 for (CFIndex idx = 0; idx < trail._numObjects; idx++) {
835 uint64_t off = _getSizedInt(bytesptr, trail._offsetIntSize);
836 if (maxOffset < off) FAIL_FALSE;
837 bytesptr += trail._offsetIntSize;
838 }
839
840 bytesptr = databytes + trail._offsetTableOffset + trail._topObject * trail._offsetIntSize;
841 uint64_t off = _getSizedInt(bytesptr, trail._offsetIntSize);
842 if (off < 8 || trail._offsetTableOffset <= off) FAIL_FALSE;
843 if (trailer) *trailer = trail;
844 if (offset) *offset = off;
845 if (marker) *marker = *(databytes + off);
846 return true;
847 }
848
849 CF_INLINE Boolean _plistIsPrimitive(CFPropertyListRef pl) {
850 CFTypeID type = CFGetTypeID(pl);
851 if (dicttype == type || arraytype == type || settype == type || osettype == type) FAIL_FALSE;
852 return true;
853 }
854
855 CF_INLINE bool _readInt(const uint8_t *ptr, const uint8_t *end_byte_ptr, uint64_t *bigint, const uint8_t **newptr) {
856 if (end_byte_ptr < ptr) FAIL_FALSE;
857 uint8_t marker = *ptr++;
858 if ((marker & 0xf0) != kCFBinaryPlistMarkerInt) FAIL_FALSE;
859 uint64_t cnt = 1 << (marker & 0x0f);
860 int32_t err = CF_NO_ERROR;
861 const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1;
862 if (CF_NO_ERROR != err) FAIL_FALSE;
863 if (end_byte_ptr < extent) FAIL_FALSE;
864 // integers are not required to be in the most compact possible representation, but only the last 64 bits are significant currently
865 *bigint = _getSizedInt(ptr, cnt);
866 ptr += cnt;
867 if (newptr) *newptr = ptr;
868 return true;
869 }
870
871 // bytesptr points at a ref
872 CF_INLINE uint64_t _getOffsetOfRefAt(const uint8_t *databytes, const uint8_t *bytesptr, const CFBinaryPlistTrailer *trailer) {
873 // *trailer contents are trusted, even for overflows -- was checked when the trailer was parsed;
874 // this pointer arithmetic and the multiplication was also already done once and checked,
875 // and the offsetTable was already validated.
876 const uint8_t *objectsFirstByte = databytes + 8;
877 const uint8_t *offsetsFirstByte = databytes + trailer->_offsetTableOffset;
878 if (bytesptr < objectsFirstByte || offsetsFirstByte - trailer->_objectRefSize < bytesptr) FAIL_MAXOFFSET;
879
880 uint64_t ref = _getSizedInt(bytesptr, trailer->_objectRefSize);
881 if (trailer->_numObjects <= ref) FAIL_MAXOFFSET;
882
883 bytesptr = databytes + trailer->_offsetTableOffset + ref * trailer->_offsetIntSize;
884 uint64_t off = _getSizedInt(bytesptr, trailer->_offsetIntSize);
885 return off;
886 }
887
888 bool __CFBinaryPlistGetOffsetForValueFromArray2(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFIndex idx, uint64_t *offset, CFMutableDictionaryRef objects) {
889 uint64_t objectsRangeStart = 8, objectsRangeEnd = trailer->_offsetTableOffset - 1;
890 if (startOffset < objectsRangeStart || objectsRangeEnd < startOffset) FAIL_FALSE;
891 const uint8_t *ptr = databytes + startOffset;
892 uint8_t marker = *ptr;
893 if ((marker & 0xf0) != kCFBinaryPlistMarkerArray) FAIL_FALSE;
894 int32_t err = CF_NO_ERROR;
895 ptr = check_ptr_add(ptr, 1, &err);
896 if (CF_NO_ERROR != err) FAIL_FALSE;
897 uint64_t cnt = (marker & 0x0f);
898 if (0xf == cnt) {
899 uint64_t bigint;
900 if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE;
901 if (LONG_MAX < bigint) FAIL_FALSE;
902 cnt = bigint;
903 }
904 if (cnt <= idx) FAIL_FALSE;
905 size_t byte_cnt = check_size_t_mul(cnt, trailer->_objectRefSize, &err);
906 if (CF_NO_ERROR != err) FAIL_FALSE;
907 const uint8_t *extent = check_ptr_add(ptr, byte_cnt, &err) - 1;
908 if (CF_NO_ERROR != err) FAIL_FALSE;
909 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
910 uint64_t off = _getOffsetOfRefAt(databytes, ptr + idx * trailer->_objectRefSize, trailer);
911 if (offset) *offset = off;
912 return true;
913 }
914
915 /* Get the offset for a value in a dictionary in a binary property list.
916 @param databytes A pointer to the start of the binary property list data.
917 @param datalen The length of the data.
918 @param startOffset The offset at which the dictionary starts.
919 @param trailer A pointer to a filled out trailer structure (use __CFBinaryPlistGetTopLevelInfo).
920 @param key A string key in the dictionary that should be searched for.
921 @param koffset Will be filled out with the offset to the key in the data bytes.
922 @param voffset Will be filled out with the offset to the value in the data bytes.
923 @param unused Unused parameter.
924 @param objects Used for caching objects. Should be a valid CFMutableDictionaryRef.
925 @return True if the key was found, false otherwise.
926 */
927 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) {
928
929 // Require a key that is a plist primitive
930 if (!key || !_plistIsPrimitive(key)) FAIL_FALSE;
931
932 // Require that startOffset is in the range of the object table
933 uint64_t objectsRangeStart = 8, objectsRangeEnd = trailer->_offsetTableOffset - 1;
934 if (startOffset < objectsRangeStart || objectsRangeEnd < startOffset) FAIL_FALSE;
935
936 // ptr is the start of the dictionary we are reading
937 const uint8_t *ptr = databytes + startOffset;
938
939 // Check that the data pointer actually points to a dictionary
940 uint8_t marker = *ptr;
941 if ((marker & 0xf0) != kCFBinaryPlistMarkerDict) FAIL_FALSE;
942
943 // Get the number of objects in this dictionary
944 int32_t err = CF_NO_ERROR;
945 ptr = check_ptr_add(ptr, 1, &err);
946 if (CF_NO_ERROR != err) FAIL_FALSE;
947 uint64_t cnt = (marker & 0x0f);
948 if (0xf == cnt) {
949 uint64_t bigint = 0;
950 if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE;
951 if (LONG_MAX < bigint) FAIL_FALSE;
952 cnt = bigint;
953 }
954
955 // Total number of objects (keys + values) is cnt * 2
956 cnt = check_size_t_mul(cnt, 2, &err);
957 if (CF_NO_ERROR != err) FAIL_FALSE;
958 size_t byte_cnt = check_size_t_mul(cnt, trailer->_objectRefSize, &err);
959 if (CF_NO_ERROR != err) FAIL_FALSE;
960
961 // Find the end of the dictionary
962 const uint8_t *extent = check_ptr_add(ptr, byte_cnt, &err) - 1;
963 if (CF_NO_ERROR != err) FAIL_FALSE;
964
965 // Check that we didn't overflow the size of the dictionary
966 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
967
968 // For short keys (15 bytes or less) in ASCII form, we can do a quick comparison check
969 // We get the pointer or copy the buffer here, outside of the loop
970 CFIndex stringKeyLen = -1;
971 if (CFGetTypeID(key) == stringtype) {
972 stringKeyLen = CFStringGetLength((CFStringRef)key);
973 }
974
975 // Find the object in the dictionary with this key
976 cnt = cnt / 2;
977 uint64_t totalKeySize = cnt * trailer->_objectRefSize;
978 uint64_t off;
979 Boolean match = false;
980 CFPropertyListRef keyInData = NULL;
981
982 #define KEY_BUFF_SIZE 16
983 char keyBuffer[KEY_BUFF_SIZE];
984 const char *keyBufferPtr = NULL;
985
986 // 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
987 if (stringKeyLen != -1) {
988 // Since we will only be comparing ASCII strings, we can attempt to get a pointer using MacRoman encoding
989 // (this is cheaper than a copy)
990 if (!(keyBufferPtr = CFStringGetCStringPtr((CFStringRef)key, kCFStringEncodingMacRoman)) && stringKeyLen < KEY_BUFF_SIZE) {
991 CFStringGetCString((CFStringRef)key, keyBuffer, KEY_BUFF_SIZE, kCFStringEncodingMacRoman);
992 // The pointer should now point to our keyBuffer instead of the original string buffer, since we've copied it
993 keyBufferPtr = keyBuffer;
994 }
995 }
996
997 // Perform linear search of the keys
998 for (CFIndex idx = 0; idx < cnt; idx++) {
999 off = _getOffsetOfRefAt(databytes, ptr, trailer);
1000 marker = *(databytes + off);
1001 // 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.
1002 if (keyBufferPtr && (marker & 0xf0) == kCFBinaryPlistMarkerASCIIString) {
1003 CFIndex len = marker & 0x0f;
1004 // move past the marker
1005 const uint8_t *ptr2 = databytes + off;
1006 err = CF_NO_ERROR;
1007 ptr2 = check_ptr_add(ptr2, 1, &err);
1008 if (CF_NO_ERROR != err) FAIL_FALSE;
1009
1010 // 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.
1011 if (0xf == len && stringKeyLen >= 0xf) {
1012 uint64_t bigint = 0;
1013 if (!_readInt(ptr2, databytes + objectsRangeEnd, &bigint, &ptr2)) FAIL_FALSE;
1014 if (LONG_MAX < bigint) FAIL_FALSE;
1015 len = (CFIndex)bigint;
1016 }
1017
1018 if (len == stringKeyLen) {
1019 err = CF_NO_ERROR;
1020 extent = check_ptr_add(ptr2, len, &err);
1021 if (CF_NO_ERROR != err) FAIL_FALSE;
1022
1023 if (databytes + trailer->_offsetTableOffset <= extent) FAIL_FALSE;
1024
1025 // Compare the key to this potential match
1026 if (memcmp(ptr2, keyBufferPtr, stringKeyLen) == 0) {
1027 match = true;
1028 }
1029 }
1030 } else {
1031 // temp object not saved in 'objects', because we don't know what allocator to use
1032 // (what allocator __CFBinaryPlistCreateObjectFiltered() or __CFBinaryPlistCreateObject()
1033 // will eventually be called with which results in that object)
1034 keyInData = NULL;
1035 if (!__CFBinaryPlistCreateObjectFiltered(databytes, datalen, off, trailer, kCFAllocatorSystemDefault, kCFPropertyListImmutable, NULL /*objects*/, NULL, 0, NULL, &keyInData) || !_plistIsPrimitive(keyInData)) {
1036 if (keyInData) CFRelease(keyInData);
1037 FAIL_FALSE;
1038 }
1039
1040 match = CFEqual(key, keyInData);
1041 CFRelease(keyInData);
1042 }
1043
1044 if (match) {
1045 if (koffset) *koffset = off;
1046 if (voffset) *voffset = _getOffsetOfRefAt(databytes, ptr + totalKeySize, trailer);
1047 return true;
1048 }
1049
1050 ptr += trailer->_objectRefSize;
1051 }
1052
1053 FAIL_FALSE;
1054 }
1055
1056 extern CFDictionaryRef __CFDictionaryCreateTransfer(CFAllocatorRef allocator, const void * *klist, const void * *vlist, CFIndex numValues);
1057 extern CFSetRef __CFSetCreateTransfer(CFAllocatorRef allocator, const void * *klist, CFIndex numValues);
1058 extern CFArrayRef __CFArrayCreateTransfer(CFAllocatorRef allocator, const void * *klist, CFIndex numValues);
1059 CF_PRIVATE void __CFPropertyListCreateSplitKeypaths(CFAllocatorRef allocator, CFSetRef currentKeys, CFSetRef *theseKeys, CFSetRef *nextKeys);
1060
1061 CF_PRIVATE 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) {
1062
1063 if (objects) {
1064 *plist = CFDictionaryGetValue(objects, (const void *)(uintptr_t)startOffset);
1065 if (*plist) {
1066 // have to assume that '*plist' was previously created with same allocator that is now being passed in
1067 CFRetain(*plist);
1068 return true;
1069 }
1070 }
1071
1072 // at any one invocation of this function, set should contain the offsets in the "path" down to this object
1073 if (set && CFSetContainsValue(set, (const void *)(uintptr_t)startOffset)) FAIL_FALSE;
1074
1075 // databytes is trusted to be at least datalen bytes long
1076 // *trailer contents are trusted, even for overflows -- was checked when the trailer was parsed
1077 uint64_t objectsRangeStart = 8, objectsRangeEnd = trailer->_offsetTableOffset - 1;
1078 if (startOffset < objectsRangeStart || objectsRangeEnd < startOffset) FAIL_FALSE;
1079
1080 uint64_t off;
1081 CFPropertyListRef *list;
1082
1083 uint8_t marker = *(databytes + startOffset);
1084 switch (marker & 0xf0) {
1085 case kCFBinaryPlistMarkerNull:
1086 switch (marker) {
1087 case kCFBinaryPlistMarkerNull:
1088 *plist = kCFNull;
1089 return true;
1090 case kCFBinaryPlistMarkerFalse:
1091 *plist = !(0) ? CFRetain(kCFBooleanFalse) : kCFBooleanFalse;
1092 return true;
1093 case kCFBinaryPlistMarkerTrue:
1094 *plist = !(0) ? CFRetain(kCFBooleanTrue) : kCFBooleanTrue;
1095 return true;
1096 }
1097 FAIL_FALSE;
1098 case kCFBinaryPlistMarkerInt:
1099 {
1100 const uint8_t *ptr = (databytes + startOffset);
1101 int32_t err = CF_NO_ERROR;
1102 ptr = check_ptr_add(ptr, 1, &err);
1103 if (CF_NO_ERROR != err) FAIL_FALSE;
1104 uint64_t cnt = 1 << (marker & 0x0f);
1105 const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1;
1106 if (CF_NO_ERROR != err) FAIL_FALSE;
1107 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1108 if (16 < cnt) FAIL_FALSE;
1109 // in format version '00', 1, 2, and 4-byte integers have to be interpreted as unsigned,
1110 // whereas 8-byte integers are signed (and 16-byte when available)
1111 // negative 1, 2, 4-byte integers are always emitted as 8 bytes in format '00'
1112 // integers are not required to be in the most compact possible representation, but only the last 64 bits are significant currently
1113 uint64_t bigint = _getSizedInt(ptr, cnt);
1114 if (8 < cnt) {
1115 CFSInt128Struct val;
1116 val.high = 0;
1117 val.low = bigint;
1118 *plist = CFNumberCreate(allocator, kCFNumberSInt128Type, &val);
1119 } else {
1120 *plist = CFNumberCreate(allocator, kCFNumberSInt64Type, &bigint);
1121 }
1122 // these are always immutable
1123 if (objects && *plist) {
1124 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1125 }
1126 return (*plist) ? true : false;
1127 }
1128 case kCFBinaryPlistMarkerReal:
1129 switch (marker & 0x0f) {
1130 case 2: {
1131 const uint8_t *ptr = (databytes + startOffset);
1132 int32_t err = CF_NO_ERROR;
1133 ptr = check_ptr_add(ptr, 1, &err);
1134 if (CF_NO_ERROR != err) FAIL_FALSE;
1135 const uint8_t *extent = check_ptr_add(ptr, 4, &err) - 1;
1136 if (CF_NO_ERROR != err) FAIL_FALSE;
1137 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1138 CFSwappedFloat32 swapped32;
1139 memmove(&swapped32, ptr, 4);
1140 float f = CFConvertFloat32SwappedToHost(swapped32);
1141 *plist = CFNumberCreate(allocator, kCFNumberFloat32Type, &f);
1142 // these are always immutable
1143 if (objects && *plist) {
1144 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1145 }
1146 return (*plist) ? true : false;
1147 }
1148 case 3: {
1149 const uint8_t *ptr = (databytes + startOffset);
1150 int32_t err = CF_NO_ERROR;
1151 ptr = check_ptr_add(ptr, 1, &err);
1152 if (CF_NO_ERROR != err) FAIL_FALSE;
1153 const uint8_t *extent = check_ptr_add(ptr, 8, &err) - 1;
1154 if (CF_NO_ERROR != err) FAIL_FALSE;
1155 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1156 CFSwappedFloat64 swapped64;
1157 memmove(&swapped64, ptr, 8);
1158 double d = CFConvertFloat64SwappedToHost(swapped64);
1159 *plist = CFNumberCreate(allocator, kCFNumberFloat64Type, &d);
1160 // these are always immutable
1161 if (objects && *plist) {
1162 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1163 }
1164 return (*plist) ? true : false;
1165 }
1166 }
1167 FAIL_FALSE;
1168 case kCFBinaryPlistMarkerDate & 0xf0:
1169 switch (marker) {
1170 case kCFBinaryPlistMarkerDate: {
1171 const uint8_t *ptr = (databytes + startOffset);
1172 int32_t err = CF_NO_ERROR;
1173 ptr = check_ptr_add(ptr, 1, &err);
1174 if (CF_NO_ERROR != err) FAIL_FALSE;
1175 const uint8_t *extent = check_ptr_add(ptr, 8, &err) - 1;
1176 if (CF_NO_ERROR != err) FAIL_FALSE;
1177 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1178 CFSwappedFloat64 swapped64;
1179 memmove(&swapped64, ptr, 8);
1180 double d = CFConvertFloat64SwappedToHost(swapped64);
1181 *plist = CFDateCreate(allocator, d);
1182 // these are always immutable
1183 if (objects && *plist) {
1184 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1185 }
1186 return (*plist) ? true : false;
1187 }
1188 }
1189 FAIL_FALSE;
1190 case kCFBinaryPlistMarkerData: {
1191 const uint8_t *ptr = databytes + startOffset;
1192 int32_t err = CF_NO_ERROR;
1193 ptr = check_ptr_add(ptr, 1, &err);
1194 if (CF_NO_ERROR != err) FAIL_FALSE;
1195 CFIndex cnt = marker & 0x0f;
1196 if (0xf == cnt) {
1197 uint64_t bigint = 0;
1198 if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE;
1199 if (LONG_MAX < bigint) FAIL_FALSE;
1200 cnt = (CFIndex)bigint;
1201 }
1202 const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1;
1203 if (CF_NO_ERROR != err) FAIL_FALSE;
1204 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1205 if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) {
1206 *plist = CFDataCreateMutable(allocator, 0);
1207 if (*plist) CFDataAppendBytes((CFMutableDataRef)*plist, ptr, cnt);
1208 } else {
1209 *plist = CFDataCreate(allocator, ptr, cnt);
1210 }
1211 if (objects && *plist && (mutabilityOption != kCFPropertyListMutableContainersAndLeaves)) {
1212 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1213 }
1214 return (*plist) ? true : false;
1215 }
1216 case kCFBinaryPlistMarkerASCIIString: {
1217 const uint8_t *ptr = databytes + startOffset;
1218 int32_t err = CF_NO_ERROR;
1219 ptr = check_ptr_add(ptr, 1, &err);
1220 if (CF_NO_ERROR != err) FAIL_FALSE;
1221 CFIndex cnt = marker & 0x0f;
1222 if (0xf == cnt) {
1223 uint64_t bigint = 0;
1224 if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE;
1225 if (LONG_MAX < bigint) FAIL_FALSE;
1226 cnt = (CFIndex)bigint;
1227 }
1228 const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1;
1229 if (CF_NO_ERROR != err) FAIL_FALSE;
1230 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1231 if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) {
1232 CFStringRef str = CFStringCreateWithBytes(allocator, ptr, cnt, kCFStringEncodingASCII, false);
1233 *plist = str ? CFStringCreateMutableCopy(allocator, 0, str) : NULL;
1234 if (str) CFRelease(str);
1235 } else {
1236 *plist = CFStringCreateWithBytes(allocator, ptr, cnt, kCFStringEncodingASCII, false);
1237 }
1238 if (objects && *plist && (mutabilityOption != kCFPropertyListMutableContainersAndLeaves)) {
1239 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1240 }
1241 return (*plist) ? true : false;
1242 }
1243 case kCFBinaryPlistMarkerUnicode16String: {
1244 const uint8_t *ptr = databytes + startOffset;
1245 int32_t err = CF_NO_ERROR;
1246 ptr = check_ptr_add(ptr, 1, &err);
1247 if (CF_NO_ERROR != err) FAIL_FALSE;
1248 CFIndex cnt = marker & 0x0f;
1249 if (0xf == cnt) {
1250 uint64_t bigint = 0;
1251 if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE;
1252 if (LONG_MAX < bigint) FAIL_FALSE;
1253 cnt = (CFIndex)bigint;
1254 }
1255 const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1;
1256 extent = check_ptr_add(extent, cnt, &err); // 2 bytes per character
1257 if (CF_NO_ERROR != err) FAIL_FALSE;
1258 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1259 size_t byte_cnt = check_size_t_mul(cnt, sizeof(UniChar), &err);
1260 if (CF_NO_ERROR != err) FAIL_FALSE;
1261 UniChar *chars = (UniChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, byte_cnt, 0);
1262 if (!chars) FAIL_FALSE;
1263 memmove(chars, ptr, byte_cnt);
1264 for (CFIndex idx = 0; idx < cnt; idx++) {
1265 chars[idx] = CFSwapInt16BigToHost(chars[idx]);
1266 }
1267 if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) {
1268 CFStringRef str = CFStringCreateWithCharacters(allocator, chars, cnt);
1269 *plist = str ? CFStringCreateMutableCopy(allocator, 0, str) : NULL;
1270 if (str) CFRelease(str);
1271 } else {
1272 *plist = CFStringCreateWithCharacters(allocator, chars, cnt);
1273 }
1274 CFAllocatorDeallocate(kCFAllocatorSystemDefault, chars);
1275 if (objects && *plist && (mutabilityOption != kCFPropertyListMutableContainersAndLeaves)) {
1276 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1277 }
1278 return (*plist) ? true : false;
1279 }
1280 case kCFBinaryPlistMarkerUID: {
1281 const uint8_t *ptr = databytes + startOffset;
1282 int32_t err = CF_NO_ERROR;
1283 ptr = check_ptr_add(ptr, 1, &err);
1284 if (CF_NO_ERROR != err) FAIL_FALSE;
1285 CFIndex cnt = (marker & 0x0f) + 1;
1286 const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1;
1287 if (CF_NO_ERROR != err) FAIL_FALSE;
1288 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1289 // uids are not required to be in the most compact possible representation, but only the last 64 bits are significant currently
1290 uint64_t bigint = _getSizedInt(ptr, cnt);
1291 if (UINT32_MAX < bigint) FAIL_FALSE;
1292 *plist = _CFKeyedArchiverUIDCreate(allocator, (uint32_t)bigint);
1293 // these are always immutable
1294 if (objects && *plist) {
1295 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1296 }
1297 return (*plist) ? true : false;
1298 }
1299 case kCFBinaryPlistMarkerArray:
1300 case kCFBinaryPlistMarkerSet: {
1301 const uint8_t *ptr = databytes + startOffset;
1302 int32_t err = CF_NO_ERROR;
1303 ptr = check_ptr_add(ptr, 1, &err);
1304 if (CF_NO_ERROR != err) FAIL_FALSE;
1305 CFIndex arrayCount = marker & 0x0f;
1306 if (0xf == arrayCount) {
1307 uint64_t bigint = 0;
1308 if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE;
1309 if (LONG_MAX < bigint) FAIL_FALSE;
1310 arrayCount = (CFIndex)bigint;
1311 }
1312 size_t byte_cnt = check_size_t_mul(arrayCount, trailer->_objectRefSize, &err);
1313 if (CF_NO_ERROR != err) FAIL_FALSE;
1314 const uint8_t *extent = check_ptr_add(ptr, byte_cnt, &err) - 1;
1315 if (CF_NO_ERROR != err) FAIL_FALSE;
1316 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1317 byte_cnt = check_size_t_mul(arrayCount, sizeof(CFPropertyListRef), &err);
1318 if (CF_NO_ERROR != err) FAIL_FALSE;
1319 STACK_BUFFER_DECL(CFPropertyListRef, buffer, arrayCount <= 256 ? arrayCount : 1);
1320 list = (arrayCount <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, byte_cnt, __kCFAllocatorGCScannedMemory);
1321 if (!list) FAIL_FALSE;
1322 Boolean madeSet = false;
1323 if (!set && 15 < curDepth) {
1324 set = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, NULL);
1325 madeSet = set ? true : false;
1326 }
1327
1328 if (set) CFSetAddValue(set, (const void *)(uintptr_t)startOffset);
1329 if ((marker & 0xf0) == kCFBinaryPlistMarkerArray && keyPaths) {
1330 // Only get a subset of this array
1331 CFSetRef theseKeys, nextKeys;
1332 __CFPropertyListCreateSplitKeypaths(kCFAllocatorSystemDefault, keyPaths, &theseKeys, &nextKeys);
1333
1334 Boolean success = true;
1335 CFMutableArrayRef array = CFArrayCreateMutable(allocator, CFSetGetCount(theseKeys), &kCFTypeArrayCallBacks);
1336 if (theseKeys) {
1337 CFTypeRef *keys = (CFTypeRef *)malloc(CFSetGetCount(theseKeys) * sizeof(CFTypeRef));
1338 CFSetGetValues(theseKeys, keys);
1339 for (CFIndex i = 0; i < CFSetGetCount(theseKeys); i++) {
1340 CFStringRef key = (CFStringRef)keys[i];
1341 SInt32 intValue = CFStringGetIntValue(key);
1342 if ((intValue == 0 && CFStringCompare(CFSTR("0"), key, 0) != kCFCompareEqualTo) || intValue == INT_MAX || intValue == INT_MIN || intValue < 0) {
1343 // skip, doesn't appear to be a proper integer
1344 } else {
1345 uint64_t valueOffset;
1346 Boolean found = __CFBinaryPlistGetOffsetForValueFromArray2(databytes, datalen, startOffset, trailer, (CFIndex)intValue, &valueOffset, objects);
1347 if (found) {
1348 CFPropertyListRef result;
1349 success = __CFBinaryPlistCreateObjectFiltered(databytes, datalen, valueOffset, trailer, allocator, mutabilityOption, objects, set, curDepth + 1, nextKeys, &result);
1350 if (success) {
1351 CFArrayAppendValue(array, result);
1352 CFRelease(result);
1353 } else {
1354 break;
1355 }
1356 }
1357 }
1358 }
1359
1360 free(keys);
1361 CFRelease(theseKeys);
1362 }
1363 if (nextKeys) CFRelease(nextKeys);
1364
1365 if (success) {
1366 if (!(mutabilityOption == kCFPropertyListMutableContainers || mutabilityOption == kCFPropertyListMutableContainersAndLeaves)) {
1367 // make immutable
1368 *plist = CFArrayCreateCopy(allocator, array);
1369 CFRelease(array);
1370 } else {
1371 *plist = array;
1372 }
1373 } else if (array) {
1374 CFRelease(array);
1375 }
1376 } else {
1377 for (CFIndex idx = 0; idx < arrayCount; idx++) {
1378 CFPropertyListRef pl;
1379 off = _getOffsetOfRefAt(databytes, ptr, trailer);
1380 if (!__CFBinaryPlistCreateObjectFiltered(databytes, datalen, off, trailer, allocator, mutabilityOption, objects, set, curDepth + 1, NULL, &pl)) {
1381 while (idx--) {
1382 CFRelease(list[idx]);
1383 }
1384 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
1385 FAIL_FALSE;
1386 }
1387 __CFAssignWithWriteBarrier((void **)list + idx, (void *)pl);
1388 ptr += trailer->_objectRefSize;
1389 }
1390 if ((marker & 0xf0) == kCFBinaryPlistMarkerArray) {
1391 if (mutabilityOption != kCFPropertyListImmutable) {
1392 *plist = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
1393 CFArrayReplaceValues((CFMutableArrayRef)*plist, CFRangeMake(0, 0), list, arrayCount);
1394 for (CFIndex idx = 0; idx < arrayCount; idx++) {
1395 CFRelease(list[idx]);
1396 }
1397 } else {
1398 if (!kCFUseCollectableAllocator) {
1399 *plist = __CFArrayCreateTransfer(allocator, list, arrayCount);
1400 } else {
1401 *plist = CFArrayCreate(allocator, list, arrayCount, &kCFTypeArrayCallBacks);
1402 for (CFIndex idx = 0; idx < arrayCount; idx++) {
1403 CFRelease(list[idx]);
1404 }
1405 }
1406 }
1407 } else {
1408 if (mutabilityOption != kCFPropertyListImmutable) {
1409 *plist = CFSetCreateMutable(allocator, 0, &kCFTypeSetCallBacks);
1410 for (CFIndex idx = 0; idx < arrayCount; idx++) {
1411 CFSetAddValue((CFMutableSetRef)*plist, list[idx]);
1412 }
1413 for (CFIndex idx = 0; idx < arrayCount; idx++) {
1414 CFRelease(list[idx]);
1415 }
1416 } else {
1417 if (!kCFUseCollectableAllocator) {
1418 *plist = __CFSetCreateTransfer(allocator, list, arrayCount);
1419 } else {
1420 *plist = CFSetCreate(allocator, list, arrayCount, &kCFTypeSetCallBacks);
1421 for (CFIndex idx = 0; idx < arrayCount; idx++) {
1422 CFRelease(list[idx]);
1423 }
1424 }
1425 }
1426 }
1427 }
1428 if (set) CFSetRemoveValue(set, (const void *)(uintptr_t)startOffset);
1429 if (madeSet) {
1430 CFRelease(set);
1431 set = NULL;
1432 }
1433 if (objects && *plist && (mutabilityOption == kCFPropertyListImmutable)) {
1434 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1435 }
1436 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
1437 return (*plist) ? true : false;
1438 }
1439 case kCFBinaryPlistMarkerDict: {
1440 const uint8_t *ptr = databytes + startOffset;
1441 int32_t err = CF_NO_ERROR;
1442 ptr = check_ptr_add(ptr, 1, &err);
1443 if (CF_NO_ERROR != err) FAIL_FALSE;
1444 CFIndex dictionaryCount = marker & 0x0f;
1445 if (0xf == dictionaryCount) {
1446 uint64_t bigint = 0;
1447 if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE;
1448 if (LONG_MAX < bigint) FAIL_FALSE;
1449 dictionaryCount = (CFIndex)bigint;
1450 }
1451 dictionaryCount = check_size_t_mul(dictionaryCount, 2, &err);
1452 if (CF_NO_ERROR != err) FAIL_FALSE;
1453 size_t byte_cnt = check_size_t_mul(dictionaryCount, trailer->_objectRefSize, &err);
1454 if (CF_NO_ERROR != err) FAIL_FALSE;
1455 const uint8_t *extent = check_ptr_add(ptr, byte_cnt, &err) - 1;
1456 if (CF_NO_ERROR != err) FAIL_FALSE;
1457 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1458 byte_cnt = check_size_t_mul(dictionaryCount, sizeof(CFPropertyListRef), &err);
1459 if (CF_NO_ERROR != err) FAIL_FALSE;
1460 STACK_BUFFER_DECL(CFPropertyListRef, buffer, dictionaryCount <= 256 ? dictionaryCount : 1);
1461 list = (dictionaryCount <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, byte_cnt, __kCFAllocatorGCScannedMemory);
1462 if (!list) FAIL_FALSE;
1463 Boolean madeSet = false;
1464 if (!set && 15 < curDepth) {
1465 set = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, NULL);
1466 madeSet = set ? true : false;
1467 }
1468
1469 if (set) CFSetAddValue(set, (const void *)(uintptr_t)startOffset);
1470 if (keyPaths) {
1471 // Only get a subset of this dictionary
1472 CFSetRef theseKeys, nextKeys;
1473 __CFPropertyListCreateSplitKeypaths(kCFAllocatorSystemDefault, keyPaths, &theseKeys, &nextKeys);
1474
1475 Boolean success = true;
1476 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(allocator, CFSetGetCount(theseKeys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1477 if (theseKeys) {
1478 CFTypeRef *keys = (CFTypeRef *)malloc(CFSetGetCount(theseKeys) * sizeof(CFTypeRef));
1479 CFSetGetValues(theseKeys, keys);
1480 for (CFIndex i = 0; i < CFSetGetCount(theseKeys); i++) {
1481 CFStringRef key = (CFStringRef)keys[i];
1482 uint64_t keyOffset, valueOffset;
1483 Boolean found = __CFBinaryPlistGetOffsetForValueFromDictionary3(databytes, datalen, startOffset, trailer, key, &keyOffset, &valueOffset, false, objects);
1484 if (found) {
1485 CFPropertyListRef result;
1486 success = __CFBinaryPlistCreateObjectFiltered(databytes, datalen, valueOffset, trailer, allocator, mutabilityOption, objects, set, curDepth + 1, nextKeys, &result);
1487 if (success) {
1488 CFDictionarySetValue(dict, key, result);
1489 CFRelease(result);
1490 } else {
1491 break;
1492 }
1493 }
1494 }
1495
1496 free(keys);
1497 CFRelease(theseKeys);
1498 }
1499 if (nextKeys) CFRelease(nextKeys);
1500
1501 if (success) {
1502 if (!(mutabilityOption == kCFPropertyListMutableContainers || mutabilityOption == kCFPropertyListMutableContainersAndLeaves)) {
1503 // make immutable
1504 *plist = CFDictionaryCreateCopy(allocator, dict);
1505 CFRelease(dict);
1506 } else {
1507 *plist = dict;
1508 }
1509 } else if (dict) {
1510 CFRelease(dict);
1511 }
1512 } else {
1513 for (CFIndex idx = 0; idx < dictionaryCount; idx++) {
1514 CFPropertyListRef pl = NULL;
1515 off = _getOffsetOfRefAt(databytes, ptr, trailer);
1516 if (!__CFBinaryPlistCreateObjectFiltered(databytes, datalen, off, trailer, allocator, mutabilityOption, objects, set, curDepth + 1, NULL, &pl) || (idx < dictionaryCount / 2 && !_plistIsPrimitive(pl))) {
1517 if (pl && !(0)) CFRelease(pl);
1518 while (idx--) {
1519 CFRelease(list[idx]);
1520 }
1521 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
1522 FAIL_FALSE;
1523 }
1524 __CFAssignWithWriteBarrier((void **)list + idx, (void *)pl);
1525 ptr += trailer->_objectRefSize;
1526 }
1527 if (mutabilityOption != kCFPropertyListImmutable) {
1528 *plist = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1529 for (CFIndex idx = 0; idx < dictionaryCount / 2; idx++) {
1530 CFDictionaryAddValue((CFMutableDictionaryRef)*plist, list[idx], list[idx + dictionaryCount / 2]);
1531 }
1532 for (CFIndex idx = 0; idx < dictionaryCount; idx++) {
1533 CFRelease(list[idx]);
1534 }
1535 } else {
1536 if (!kCFUseCollectableAllocator) {
1537 *plist = __CFDictionaryCreateTransfer(allocator, list, list + dictionaryCount / 2, dictionaryCount / 2);
1538 } else {
1539 *plist = CFDictionaryCreate(allocator, list, list + dictionaryCount / 2, dictionaryCount / 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1540 for (CFIndex idx = 0; idx < dictionaryCount; idx++) {
1541 CFRelease(list[idx]);
1542 }
1543 }
1544 }
1545 }
1546 if (set) CFSetRemoveValue(set, (const void *)(uintptr_t)startOffset);
1547 if (madeSet) {
1548 CFRelease(set);
1549 set = NULL;
1550 }
1551 if (objects && *plist && (mutabilityOption == kCFPropertyListImmutable)) {
1552 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1553 }
1554 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
1555 return (*plist) ? true : false;
1556 }
1557 }
1558 FAIL_FALSE;
1559 }
1560
1561 bool __CFBinaryPlistCreateObject(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFAllocatorRef allocator, CFOptionFlags mutabilityOption, CFMutableDictionaryRef objects, CFPropertyListRef *plist) {
1562 // for compatibility with Foundation's use, need to leave this here
1563 return __CFBinaryPlistCreateObjectFiltered(databytes, datalen, startOffset, trailer, allocator, mutabilityOption, objects, NULL, 0, NULL, plist);
1564 }
1565
1566 CF_PRIVATE bool __CFTryParseBinaryPlist(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags option, CFPropertyListRef *plist, CFStringRef *errorString) {
1567 uint8_t marker;
1568 CFBinaryPlistTrailer trailer;
1569 uint64_t offset;
1570 const uint8_t *databytes = CFDataGetBytePtr(data);
1571 uint64_t datalen = CFDataGetLength(data);
1572
1573 if (8 <= datalen && __CFBinaryPlistGetTopLevelInfo(databytes, datalen, &marker, &offset, &trailer)) {
1574 // FALSE: We know for binary plist parsing that the result objects will be retained
1575 // by their containing collections as the parsing proceeds, so we do not need
1576 // to use retaining callbacks for the objects map in this case. WHY: the file might
1577 // be malformed and contain hash-equal keys for the same dictionary (for example)
1578 // and the later key will cause the previous one to be released when we set the second
1579 // in the dictionary.
1580 CFMutableDictionaryRef objects = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
1581 _CFDictionarySetCapacity(objects, trailer._numObjects);
1582 CFPropertyListRef pl = NULL;
1583 bool result = true;
1584 if (__CFBinaryPlistCreateObjectFiltered(databytes, datalen, offset, &trailer, allocator, option, objects, NULL, 0, NULL, &pl)) {
1585 if (plist) *plist = pl;
1586 #if 0
1587 // code to check the 1.5 version code against any binary plist successfully parsed above
1588 extern size_t __CFBinaryPlistWrite15(CFPropertyListRef plist, CFMutableDataRef data, CFErrorRef *error);
1589 extern CFPropertyListRef __CFBinaryPlistCreate15(const uint8_t *databytes, uint64_t datalen, CFErrorRef *error);
1590
1591 CFMutableDataRef mdata = CFDataCreateMutable(0, 0);
1592 size_t s = __CFBinaryPlistWrite15(pl, mdata, NULL);
1593 //double ratio = (double)s / (double)datalen;
1594 //if (ratio < 0.75 || ratio > 4.0) CFLog(4, CFSTR("@@@ note: Binary plist of %ld bytes is %ld bytes (%f) in version 1.5"), datalen, s, ratio);
1595 if (s != CFDataGetLength(mdata)) CFLog(3, CFSTR("### error: returned length not equal to data length (%ld != %ld)"), s, CFDataGetLength(mdata));
1596 CFPropertyListRef pl2 = __CFBinaryPlistCreate15((const uint8_t *)CFDataGetBytePtr(mdata), CFDataGetLength(mdata), NULL);
1597 if (!CFEqual(pl, pl2)) CFLog(3, CFSTR("*** error: plists before and after are not equal\n--------\n%@\n--------\n%@\n--------"), pl, pl2);
1598 #endif
1599 } else {
1600 if (plist) *plist = NULL;
1601 if (errorString) *errorString = (CFStringRef)CFRetain(CFSTR("binary data is corrupt"));
1602 result = false;
1603 }
1604 CFRelease(objects);
1605 return result;
1606 }
1607 FAIL_FALSE;
1608 }
1609