xref: /xnu-8020.121.3/libkern/libkern/Block_private.h (revision fdd8201d7b966f0c3ea610489d29bd841d358941)
1 /*
2  * Block_private.h
3  *
4  * SPI for Blocks
5  *
6  * Copyright (c) 2008-2010 Apple Inc. All rights reserved.
7  *
8  * @APPLE_LLVM_LICENSE_HEADER@
9  *
10  */
11 
12 #ifndef _BLOCK_PRIVATE_H_
13 #define _BLOCK_PRIVATE_H_
14 
15 #include <Availability.h>
16 #include <AvailabilityMacros.h>
17 #ifndef KERNEL
18 #include <TargetConditionals.h>
19 #endif
20 
21 #include <stdbool.h>
22 #include <stdint.h>
23 #ifdef KERNEL
24 #include <sys/systm.h>
25 #else
26 #include <stdio.h>
27 #endif
28 
29 
30 #ifdef KERNEL
31 #include <libkern/Block.h>
32 struct Block_byref;
33 #else
34 #include <Block.h>
35 #endif
36 
37 #if __has_include(<ptrauth.h>)
38 #include <ptrauth.h>
39 #endif
40 
41 #if __has_feature(ptrauth_calls) && __cplusplus < 201103L
42 
43 // C ptrauth or old C++ ptrauth
44 
45 #define _Block_set_function_pointer(field, value)                       \
46     ((value)                                                            \
47      ? ((field) =                                                       \
48 	(__typeof__(field))                                             \
49 	ptrauth_auth_and_resign((void*)(value),                         \
50 	                        ptrauth_key_function_pointer, 0,        \
51 	                        ptrauth_key_block_function, &(field)))  \
52      : ((field) = 0))
53 
54 #define _Block_get_function_pointer(field)                              \
55     ((field)                                                            \
56      ? (__typeof__(field))                                              \
57        ptrauth_auth_function((void*)(field),                            \
58 	                     ptrauth_key_block_function, &(field))      \
59      : (__typeof__(field))0)
60 
61 #else
62 
63 // C++11 ptrauth or no ptrauth
64 
65 #define _Block_set_function_pointer(field, value)       \
66     (field) = (value)
67 
68 #define _Block_get_function_pointer(field)      \
69     (field)
70 
71 #endif
72 
73 
74 #if __has_feature(ptrauth_calls) && __cplusplus >= 201103L
75 
76 // StorageSignedFunctionPointer<Key, Fn> stores a function pointer of type
77 // Fn but signed with the given ptrauth key and with the address of its
78 // storage as extra data.
79 // Function pointers inside block objects are signed this way.
80 template <typename Fn, ptrauth_key Key>
81 class StorageSignedFunctionPointer {
82 	uintptr_t bits;
83 
84 public:
85 
86 // Authenticate function pointer fn as a C function pointer.
87 // Re-sign it with our key and the storage address as extra data.
88 // DOES NOT actually write to our storage.
89 	uintptr_t
prepareWrite(Fn fn)90 	prepareWrite(Fn fn) const
91 	{
92 		if (fn == nullptr) {
93 			return 0;
94 		} else {
95 			return (uintptr_t)
96 			       ptrauth_auth_and_resign(fn, ptrauth_key_function_pointer, ptrauth_function_pointer_type_discriminator(Fn),
97 			           Key, &bits);
98 		}
99 	}
100 
101 // Authenticate otherBits at otherStorage.
102 // Re-sign it with our storage address.
103 // DOES NOT actually write to our storage.
104 	uintptr_t
prepareWrite(const StorageSignedFunctionPointer & other)105 	prepareWrite(const StorageSignedFunctionPointer& other) const
106 	{
107 		if (other.bits == 0) {
108 			return 0;
109 		} else {
110 			return (uintptr_t)
111 			       ptrauth_auth_and_resign((void*)other.bits, Key, &other.bits,
112 			           Key, &bits);
113 		}
114 	}
115 
116 // Authenticate ptr as if it were stored at our storage address.
117 // Re-sign it as a C function pointer.
118 // DOES NOT actually read from our storage.
119 	Fn
completeReadFn(uintptr_t ptr)120 	completeReadFn(uintptr_t ptr) const
121 	{
122 		if (ptr == 0) {
123 			return nullptr;
124 		} else {
125 			return (Fn)ptrauth_auth_function((void *)ptr, Key, &bits);
126 		}
127 	}
128 
129 // Authenticate ptr as if it were at our storage address.
130 // Return it as a dereferenceable pointer.
131 // DOES NOT actually read from our storage.
132 	void*
completeReadRaw(uintptr_t ptr)133 	completeReadRaw(uintptr_t ptr) const
134 	{
135 		if (ptr == 0) {
136 			return nullptr;
137 		} else {
138 			return ptrauth_auth_data((void*)ptr, Key, &bits);
139 		}
140 	}
141 
StorageSignedFunctionPointer()142 	StorageSignedFunctionPointer()
143 	{
144 	}
145 
StorageSignedFunctionPointer(Fn value)146 	StorageSignedFunctionPointer(Fn value)
147 		: bits(prepareWrite(value))
148 	{
149 	}
150 
StorageSignedFunctionPointer(const StorageSignedFunctionPointer & value)151 	StorageSignedFunctionPointer(const StorageSignedFunctionPointer& value)
152 		: bits(prepareWrite(value))
153 	{
154 	}
155 
156 	StorageSignedFunctionPointer&
157 	operator =(Fn rhs)
158 	{
159 		bits = prepareWrite(rhs);
160 		return *this;
161 	}
162 
163 	StorageSignedFunctionPointer&
164 	operator =(const StorageSignedFunctionPointer& rhs)
165 	{
166 		bits = prepareWrite(rhs);
167 		return *this;
168 	}
169 
Fn()170 	operator Fn() const {
171 		return completeReadFn(bits);
172 	}
173 
174 	explicit
175 	operator void*() const
176 	{
177 		return completeReadRaw(bits);
178 	}
179 
180 	explicit
181 	operator bool() const
182 	{
183 		return completeReadRaw(bits) != nullptr;
184 	}
185 };
186 
187 using BlockCopyFunction = StorageSignedFunctionPointer
188     <void (*)(void *, const void *),
189     ptrauth_key_block_function>;
190 
191 using BlockDisposeFunction = StorageSignedFunctionPointer
192     <void (*)(const void *),
193     ptrauth_key_block_function>;
194 
195 using BlockInvokeFunction = StorageSignedFunctionPointer
196     <void (*)(void *, ...),
197     ptrauth_key_block_function>;
198 
199 using BlockByrefKeepFunction = StorageSignedFunctionPointer
200     <void (*)(struct Block_byref *, struct Block_byref *),
201     ptrauth_key_block_function>;
202 
203 using BlockByrefDestroyFunction = StorageSignedFunctionPointer
204     <void (*)(struct Block_byref *),
205     ptrauth_key_block_function>;
206 
207 // c++11 and ptrauth_calls
208 #elif !__has_feature(ptrauth_calls)
209 // not ptrauth_calls
210 
211 typedef void (*BlockCopyFunction)(void *, const void *);
212 typedef void (*BlockDisposeFunction)(const void *);
213 typedef void (*BlockInvokeFunction)(void *, ...);
214 typedef void (*BlockByrefKeepFunction)(struct Block_byref*, struct Block_byref*);
215 typedef void (*BlockByrefDestroyFunction)(struct Block_byref *);
216 
217 #else
218 // ptrauth_calls but not c++11
219 
220 typedef uintptr_t BlockCopyFunction;
221 typedef uintptr_t BlockDisposeFunction;
222 typedef uintptr_t BlockInvokeFunction;
223 typedef uintptr_t BlockByrefKeepFunction;
224 typedef uintptr_t BlockByrefDestroyFunction;
225 
226 #endif
227 
228 #if __has_feature(ptrauth_calls)
229 #define _Block_get_relative_function_pointer(field, type)           \
230 	((type)ptrauth_sign_unauthenticated(                              \
231 	                (void *)((uintptr_t)(intptr_t)(field) + (uintptr_t)&(field)), \
232 	                ptrauth_key_function_pointer, 0))
233 #else
234 #define _Block_get_relative_function_pointer(field, type)       \
235 	((type)((uintptr_t)(intptr_t)(field) + (uintptr_t)&(field)))
236 #endif
237 
238 #define _Block_descriptor_ptrauth_discriminator 0xC0BB
239 
240 // Values for Block_layout->flags to describe block objects
241 enum {
242 	BLOCK_DEALLOCATING =      (0x0001),// runtime
243 	BLOCK_REFCOUNT_MASK =     (0xfffe),// runtime
244 	BLOCK_INLINE_LAYOUT_STRING = (1 << 21), // compiler
245 	BLOCK_SMALL_DESCRIPTOR =  (1 << 22), // compiler
246 	BLOCK_IS_NOESCAPE =       (1 << 23), // compiler
247 	BLOCK_NEEDS_FREE =        (1 << 24),// runtime
248 	BLOCK_HAS_COPY_DISPOSE =  (1 << 25),// compiler
249 	BLOCK_HAS_CTOR =          (1 << 26),// compiler: helpers have C++ code
250 	BLOCK_IS_GC =             (1 << 27),// runtime
251 	BLOCK_IS_GLOBAL =         (1 << 28),// compiler
252 	BLOCK_USE_STRET =         (1 << 29),// compiler: undefined if !BLOCK_HAS_SIGNATURE
253 	BLOCK_HAS_SIGNATURE  =    (1 << 30),// compiler
254 	BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31) // compiler
255 };
256 
257 #define BLOCK_DESCRIPTOR_1 1
258 struct Block_descriptor_1 {
259 	uintptr_t reserved;
260 	uintptr_t size;
261 };
262 
263 #define BLOCK_DESCRIPTOR_2 1
264 struct Block_descriptor_2 {
265 	// requires BLOCK_HAS_COPY_DISPOSE
266 	BlockCopyFunction copy;
267 	BlockDisposeFunction dispose;
268 };
269 
270 #define BLOCK_DESCRIPTOR_3 1
271 struct Block_descriptor_3 {
272 	// requires BLOCK_HAS_SIGNATURE
273 	const char *signature;
274 	const char *layout; // contents depend on BLOCK_HAS_EXTENDED_LAYOUT
275 };
276 
277 struct Block_descriptor_small {
278 	uint32_t size;
279 
280 	int32_t signature;
281 	int32_t layout;
282 
283 	/* copy & dispose are optional, only access them if
284 	 *        Block_layout->flags & BLOCK_HAS_COPY_DIPOSE */
285 	int32_t copy;
286 	int32_t dispose;
287 };
288 
289 
290 struct Block_layout {
291 	void *isa;
292 	volatile int32_t flags; // contains ref count
293 	int32_t reserved;
294 	BlockInvokeFunction invoke;
295 	struct Block_descriptor_1 *descriptor;
296 	// imported variables
297 };
298 
299 
300 // Values for Block_byref->flags to describe __block variables
301 enum {
302 	// Byref refcount must use the same bits as Block_layout's refcount.
303 	// BLOCK_DEALLOCATING =      (0x0001),  // runtime
304 	// BLOCK_REFCOUNT_MASK =     (0xfffe),  // runtime
305 
306 	BLOCK_BYREF_LAYOUT_MASK =       (0xf << 28),// compiler
307 	BLOCK_BYREF_LAYOUT_EXTENDED =   (1 << 28),// compiler
308 	BLOCK_BYREF_LAYOUT_NON_OBJECT = (2 << 28), // compiler
309 	BLOCK_BYREF_LAYOUT_STRONG =     (3 << 28),// compiler
310 	BLOCK_BYREF_LAYOUT_WEAK =       (4 << 28),// compiler
311 	BLOCK_BYREF_LAYOUT_UNRETAINED = (5 << 28), // compiler
312 
313 	BLOCK_BYREF_IS_GC =             (1 << 27),// runtime
314 
315 	BLOCK_BYREF_HAS_COPY_DISPOSE =  (1 << 25),// compiler
316 	BLOCK_BYREF_NEEDS_FREE =        (1 << 24),// runtime
317 };
318 
319 struct Block_byref {
320 	void *isa;
321 	struct Block_byref *forwarding;
322 	volatile int32_t flags; // contains ref count
323 	uint32_t size;
324 };
325 
326 struct Block_byref_2 {
327 	// requires BLOCK_BYREF_HAS_COPY_DISPOSE
328 	BlockByrefKeepFunction byref_keep;
329 	BlockByrefDestroyFunction byref_destroy;
330 };
331 
332 struct Block_byref_3 {
333 	// requires BLOCK_BYREF_LAYOUT_EXTENDED
334 	const char *layout;
335 };
336 
337 
338 // Extended layout encoding.
339 
340 // Values for Block_descriptor_3->layout with BLOCK_HAS_EXTENDED_LAYOUT
341 // and for Block_byref_3->layout with BLOCK_BYREF_LAYOUT_EXTENDED
342 
343 // If the layout field is less than 0x1000, then it is a compact encoding
344 // of the form 0xXYZ: X strong pointers, then Y byref pointers,
345 // then Z weak pointers.
346 
347 // If the layout field is 0x1000 or greater, it points to a
348 // string of layout bytes. Each byte is of the form 0xPN.
349 // Operator P is from the list below. Value N is a parameter for the operator.
350 // Byte 0x00 terminates the layout; remaining block data is non-pointer bytes.
351 
352 enum {
353 	BLOCK_LAYOUT_ESCAPE = 0, // N=0 halt, rest is non-pointer. N!=0 reserved.
354 	BLOCK_LAYOUT_NON_OBJECT_BYTES = 1, // N bytes non-objects
355 	BLOCK_LAYOUT_NON_OBJECT_WORDS = 2, // N words non-objects
356 	BLOCK_LAYOUT_STRONG           = 3,// N words strong pointers
357 	BLOCK_LAYOUT_BYREF            = 4,// N words byref pointers
358 	BLOCK_LAYOUT_WEAK             = 5,// N words weak pointers
359 	BLOCK_LAYOUT_UNRETAINED       = 6,// N words unretained pointers
360 	BLOCK_LAYOUT_UNKNOWN_WORDS_7  = 7,// N words, reserved
361 	BLOCK_LAYOUT_UNKNOWN_WORDS_8  = 8,// N words, reserved
362 	BLOCK_LAYOUT_UNKNOWN_WORDS_9  = 9,// N words, reserved
363 	BLOCK_LAYOUT_UNKNOWN_WORDS_A  = 0xA,// N words, reserved
364 	BLOCK_LAYOUT_UNUSED_B         = 0xB,// unspecified, reserved
365 	BLOCK_LAYOUT_UNUSED_C         = 0xC,// unspecified, reserved
366 	BLOCK_LAYOUT_UNUSED_D         = 0xD,// unspecified, reserved
367 	BLOCK_LAYOUT_UNUSED_E         = 0xE,// unspecified, reserved
368 	BLOCK_LAYOUT_UNUSED_F         = 0xF,// unspecified, reserved
369 };
370 
371 
372 // Runtime support functions used by compiler when generating copy/dispose helpers
373 
374 // Values for _Block_object_assign() and _Block_object_dispose() parameters
375 enum {
376 	// see function implementation for a more complete description of these fields and combinations
377 	BLOCK_FIELD_IS_OBJECT   =  3,// id, NSObject, __attribute__((NSObject)), block, ...
378 	BLOCK_FIELD_IS_BLOCK    =  7,// a block variable
379 	BLOCK_FIELD_IS_BYREF    =  8,// the on stack structure holding the __block variable
380 	BLOCK_FIELD_IS_WEAK     = 16,// declared __weak, only used in byref copy helpers
381 	BLOCK_BYREF_CALLER      = 128,// called from __block (byref) copy/dispose support routines.
382 };
383 
384 enum {
385 	BLOCK_ALL_COPY_DISPOSE_FLAGS =
386 	    BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_BLOCK | BLOCK_FIELD_IS_BYREF |
387 	    BLOCK_FIELD_IS_WEAK | BLOCK_BYREF_CALLER
388 };
389 
390 
391 // Function pointer accessors
392 
393 static inline __typeof__(void (*)(void *, ...))
_Block_get_invoke_fn(struct Block_layout * block)394 _Block_get_invoke_fn(struct Block_layout *block)
395 {
396 	return (void (*)(void *, ...))_Block_get_function_pointer(block->invoke);
397 }
398 
399 static inline void
_Block_set_invoke_fn(struct Block_layout * block,void (* fn)(void *,...))400 _Block_set_invoke_fn(struct Block_layout *block, void (*fn)(void *, ...))
401 {
402 	_Block_set_function_pointer(block->invoke, fn);
403 }
404 
405 static inline void *
_Block_get_descriptor(struct Block_layout * aBlock)406 _Block_get_descriptor(struct Block_layout *aBlock)
407 {
408 #if __has_feature(ptrauth_signed_block_descriptors)
409 	uintptr_t disc = ptrauth_blend_discriminator(
410 		&aBlock->descriptor, _Block_descriptor_ptrauth_discriminator);
411 	return ptrauth_auth_data(aBlock->descriptor, ptrauth_key_asda, disc);
412 #else
413 	return aBlock->descriptor;
414 #endif
415 }
416 
417 static inline __typeof__(void (*)(void *, const void *))
_Block_get_copy_fn(struct Block_descriptor_2 * desc)418 _Block_get_copy_fn(struct Block_descriptor_2 *desc)
419 {
420 	return (void (*)(void *, const void *))_Block_get_function_pointer(desc->copy);
421 }
422 
423 static inline void
_Block_set_copy_fn(struct Block_descriptor_2 * desc,void (* fn)(void *,const void *))424 _Block_set_copy_fn(struct Block_descriptor_2 *desc,
425     void (*fn)(void *, const void *))
426 {
427 	_Block_set_function_pointer(desc->copy, fn);
428 }
429 
430 
431 static inline __typeof__(void (*)(const void *))
_Block_get_dispose_fn(struct Block_descriptor_2 * desc)432 _Block_get_dispose_fn(struct Block_descriptor_2 *desc)
433 {
434 	return (void (*)(const void *))_Block_get_function_pointer(desc->dispose);
435 }
436 
437 static inline void
_Block_set_dispose_fn(struct Block_descriptor_2 * desc,void (* fn)(const void *))438 _Block_set_dispose_fn(struct Block_descriptor_2 *desc,
439     void (*fn)(const void *))
440 {
441 	_Block_set_function_pointer(desc->dispose, fn);
442 }
443 
444 #pragma clang diagnostic push
445 #pragma clang diagnostic ignored "-Wcast-align"
446 
447 static inline __typeof__(void (*)(void *, const void *))
_Block_get_copy_function(struct Block_layout * aBlock)448 _Block_get_copy_function(struct Block_layout *aBlock)
449 {
450 	if (!(aBlock->flags & BLOCK_HAS_COPY_DISPOSE)) {
451 		return NULL;
452 	}
453 
454 	void *desc = _Block_get_descriptor(aBlock);
455 	if (aBlock->flags & BLOCK_SMALL_DESCRIPTOR) {
456 		struct Block_descriptor_small *bds =
457 		    (struct Block_descriptor_small *)desc;
458 		return _Block_get_relative_function_pointer(
459 			bds->copy, void (*)(void *, const void *));
460 	}
461 
462 	struct Block_descriptor_2 *bd2 =
463 	    (struct Block_descriptor_2 *)((unsigned char *)desc +
464 	    sizeof(struct Block_descriptor_1));
465 	return _Block_get_copy_fn(bd2);
466 }
467 
468 static inline __typeof__(void (*)(const void *))
_Block_get_dispose_function(struct Block_layout * aBlock)469 _Block_get_dispose_function(struct Block_layout *aBlock)
470 {
471 	if (!(aBlock->flags & BLOCK_HAS_COPY_DISPOSE)) {
472 		return NULL;
473 	}
474 
475 	void *desc = _Block_get_descriptor(aBlock);
476 	if (aBlock->flags & BLOCK_SMALL_DESCRIPTOR) {
477 		struct Block_descriptor_small *bds =
478 		    (struct Block_descriptor_small *)desc;
479 		return _Block_get_relative_function_pointer(
480 			bds->dispose, void (*)(const void *));
481 	}
482 
483 	struct Block_descriptor_2 *bd2 =
484 	    (struct Block_descriptor_2 *)((unsigned char *)desc +
485 	    sizeof(struct Block_descriptor_1));
486 	return _Block_get_dispose_fn(bd2);
487 }
488 
489 #pragma clang diagnostic pop
490 
491 // Other support functions
492 
493 
494 // runtime entry to get total size of a closure
495 BLOCK_EXPORT size_t Block_size(void *aBlock);
496 
497 // indicates whether block was compiled with compiler that sets the ABI related metadata bits
498 BLOCK_EXPORT bool _Block_has_signature(void *aBlock)
499 __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
500 
501 // returns TRUE if return value of block is on the stack, FALSE otherwise
502 BLOCK_EXPORT bool _Block_use_stret(void *aBlock)
503 __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
504 
505 // Returns a string describing the block's parameter and return types.
506 // The encoding scheme is the same as Objective-C @encode.
507 // Returns NULL for blocks compiled with some compilers.
508 BLOCK_EXPORT const char * _Block_signature(void *aBlock)
509 __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
510 
511 // Returns a string describing the block's GC layout.
512 // This uses the GC skip/scan encoding.
513 // May return NULL.
514 BLOCK_EXPORT const char * _Block_layout(void *aBlock)
515 __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
516 
517 // Returns a string describing the block's layout.
518 // This uses the "extended layout" form described above.
519 // May return NULL.
520 BLOCK_EXPORT const char * _Block_extended_layout(void *aBlock)
521 __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_7_0);
522 
523 // Callable only from the ARR weak subsystem while in exclusion zone
524 BLOCK_EXPORT bool _Block_tryRetain(const void *aBlock)
525 __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
526 
527 // Callable only from the ARR weak subsystem while in exclusion zone
528 BLOCK_EXPORT bool _Block_isDeallocating(const void *aBlock)
529 __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
530 
531 
532 // the raw data space for runtime classes for blocks
533 // class+meta used for stack, malloc, and collectable based blocks
534 BLOCK_EXPORT void * _NSConcreteMallocBlock[32]
535 __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
536 BLOCK_EXPORT void * _NSConcreteAutoBlock[32]
537 __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
538 BLOCK_EXPORT void * _NSConcreteFinalizingBlock[32]
539 __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
540 BLOCK_EXPORT void * _NSConcreteWeakBlockVariable[32]
541 __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
542 // declared in Block.h
543 // BLOCK_EXPORT void * _NSConcreteGlobalBlock[32];
544 // BLOCK_EXPORT void * _NSConcreteStackBlock[32];
545 
546 
547 #if !KERNEL
548 struct Block_callbacks_RR {
549 	size_t  size;               // size == sizeof(struct Block_callbacks_RR)
550 	void  (*retain)(const void *);
551 	void  (*release)(const void *);
552 	void  (*destructInstance)(const void *);
553 };
554 typedef struct Block_callbacks_RR Block_callbacks_RR;
555 
556 BLOCK_EXPORT void _Block_use_RR2(const Block_callbacks_RR *callbacks);
557 #endif // !KERNEL
558 
559 #endif
560