xref: /trueos/lib/libdispatch/src/voucher.c (revision bf5f91cb28c5878845eb00fbf329c042f6c643c9)
1 /*
2  * Copyright (c) 2013 Apple Inc. All rights reserved.
3  *
4  * @APPLE_APACHE_LICENSE_HEADER_START@
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * @APPLE_APACHE_LICENSE_HEADER_END@
19  */
20 
21 #include "internal.h"
22 
23 #if VOUCHER_USE_MACH_VOUCHER
24 
25 #include <mach/mach_voucher.h>
26 
27 // <rdar://16363550>
28 #ifndef VM_MEMORY_GENEALOGY
29 #define VM_MEMORY_GENEALOGY 78
30 #endif
31 
32 #ifndef VOUCHER_ATM_COLLECT_THRESHOLD
33 #define VOUCHER_ATM_COLLECT_THRESHOLD 1
34 #endif
35 #define VATM_COLLECT_THRESHOLD_VALUE(t) (((t) - 1) * 2)
36 static volatile long _voucher_atm_collect_level;
37 static long _voucher_atm_collect_threshold =
38 		VATM_COLLECT_THRESHOLD_VALUE(VOUCHER_ATM_COLLECT_THRESHOLD);
39 static unsigned long _voucher_atm_subid_bits;
40 
41 typedef struct _voucher_atm_s *_voucher_atm_t;
42 
43 static void _voucher_activity_atfork_child(void);
44 static inline mach_voucher_t _voucher_get_atm_mach_voucher(voucher_t voucher);
45 static inline mach_voucher_t _voucher_activity_get_atm_mach_voucher(
46 		_voucher_activity_t act);
47 static inline _voucher_activity_t _voucher_activity_get(voucher_t voucher);
48 static _voucher_activity_t _voucher_activity_copy_from_mach_voucher(
49 		mach_voucher_t kv, voucher_activity_id_t va_id);
50 static inline _voucher_activity_t _voucher_activity_retain(
51 		_voucher_activity_t act);
52 static inline void _voucher_activity_release(_voucher_activity_t act);
53 
54 #pragma mark -
55 #pragma mark voucher_t
56 
57 #if USE_OBJC
58 OS_OBJECT_OBJC_CLASS_DECL(voucher);
59 #define VOUCHER_CLASS OS_OBJECT_OBJC_CLASS(voucher)
60 #else
61 const _os_object_class_s _voucher_class = {
62 	._os_obj_xref_dispose = (void(*)(_os_object_t))_voucher_xref_dispose,
63 	._os_obj_dispose = (void(*)(_os_object_t))_voucher_dispose,
64 };
65 #define VOUCHER_CLASS &_voucher_class
66 #endif // USE_OBJC
67 
68 static const voucher_activity_trace_id_t _voucher_activity_trace_id_release =
69 		(voucher_activity_trace_id_t)voucher_activity_tracepoint_type_release <<
70 		_voucher_activity_trace_id_type_shift;
71 static const unsigned int _voucher_max_activities = 16;
72 
73 DISPATCH_ALWAYS_INLINE
74 static inline void
_voucher_recipes_init(mach_voucher_attr_recipe_data_t * recipes,mach_voucher_attr_content_size_t bits_size)75 _voucher_recipes_init(mach_voucher_attr_recipe_data_t *recipes,
76 		mach_voucher_attr_content_size_t bits_size)
77 {
78 	static const mach_voucher_attr_recipe_data_t base_recipe = {
79 		.key = MACH_VOUCHER_ATTR_KEY_ALL,
80 		.command = MACH_VOUCHER_ATTR_COPY,
81 	};
82 	_voucher_recipes_base(recipes) = base_recipe;
83 	static const mach_voucher_attr_recipe_data_t atm_recipe = {
84 		.key = MACH_VOUCHER_ATTR_KEY_ATM,
85 		.command = MACH_VOUCHER_ATTR_COPY,
86 	};
87 	_voucher_recipes_atm(recipes) = atm_recipe;
88 	static const mach_voucher_attr_recipe_data_t bits_recipe = {
89 		.key = MACH_VOUCHER_ATTR_KEY_USER_DATA,
90 		.command = MACH_VOUCHER_ATTR_USER_DATA_STORE,
91 	};
92 	_voucher_recipes_bits(recipes) = bits_recipe;
93 	if (!bits_size) return;
94 	_voucher_recipes_bits(recipes).content_size = bits_size;
95 	*_voucher_recipes_magic(recipes) = _voucher_magic_v1;
96 }
97 
98 static inline voucher_t
_voucher_alloc(unsigned int activities,pthread_priority_t priority,mach_voucher_attr_recipe_size_t extra)99 _voucher_alloc(unsigned int activities, pthread_priority_t priority,
100 		mach_voucher_attr_recipe_size_t extra)
101 {
102 	if (activities > _voucher_max_activities) {
103 		activities = _voucher_max_activities;
104 	}
105 	voucher_t voucher;
106 	size_t voucher_size, recipes_size;
107 	mach_voucher_attr_content_size_t bits_size;
108 	recipes_size = (priority||activities||extra) ? _voucher_recipes_size() : 0;
109 	bits_size = recipes_size ? _voucher_bits_size(activities) : 0;
110 	voucher_size = sizeof(voucher_s) + recipes_size + bits_size + extra;
111 	voucher = (voucher_t)_os_object_alloc_realized(VOUCHER_CLASS, voucher_size);
112 #if VOUCHER_ENABLE_RECIPE_OBJECTS
113 	voucher->v_recipe_extra_size = extra;
114 	voucher->v_recipe_extra_offset = voucher_size - extra;
115 #else
116 	dispatch_assert(!extra);
117 #endif
118 	voucher->v_has_priority = priority ? 1 : 0;
119 	voucher->v_activities = activities;
120 	if (!recipes_size) return voucher;
121 	_voucher_recipes_init(voucher->v_recipes, bits_size);
122 	*_voucher_priority(voucher) = (_voucher_priority_t)priority;
123 	_dispatch_voucher_debug("alloc", voucher);
124 	return voucher;
125 }
126 
127 #if VOUCHER_ENABLE_RECIPE_OBJECTS
128 voucher_t
voucher_create(voucher_recipe_t recipe)129 voucher_create(voucher_recipe_t recipe)
130 {
131 	// TODO: capture current activities or current kvoucher ?
132 	mach_voucher_attr_recipe_size_t extra = recipe ? recipe->vr_size : 0;
133 	voucher_t voucher = _voucher_alloc(0, 0, extra);
134 	if (extra) {
135 		memcpy(_voucher_extra_recipes(voucher), recipe->vr_data, extra);
136 	}
137 	return voucher;
138 }
139 #endif
140 
141 voucher_t
voucher_adopt(voucher_t voucher)142 voucher_adopt(voucher_t voucher)
143 {
144 	return _voucher_adopt(voucher);
145 }
146 
147 voucher_t
voucher_copy(void)148 voucher_copy(void)
149 {
150 	return _voucher_copy();
151 }
152 
153 voucher_t
voucher_copy_without_importance(void)154 voucher_copy_without_importance(void)
155 {
156 	return _voucher_copy_without_importance();
157 }
158 
159 void
_voucher_thread_cleanup(void * voucher)160 _voucher_thread_cleanup(void *voucher)
161 {
162 	_voucher_swap(voucher, NULL);
163 }
164 
165 DISPATCH_CACHELINE_ALIGN
166 static TAILQ_HEAD(, voucher_s) _vouchers[VL_HASH_SIZE];
167 #define _vouchers(kv) (&_vouchers[VL_HASH((kv))])
168 static os_lock_handoff_s _vouchers_lock = OS_LOCK_HANDOFF_INIT;
169 #define _vouchers_lock_lock() os_lock_lock(&_vouchers_lock)
170 #define _vouchers_lock_unlock() os_lock_unlock(&_vouchers_lock)
171 
172 static voucher_t
_voucher_find_and_retain(mach_voucher_t kv)173 _voucher_find_and_retain(mach_voucher_t kv)
174 {
175 	voucher_t v;
176 	if (!kv) return NULL;
177 	_vouchers_lock_lock();
178 	TAILQ_FOREACH(v, _vouchers(kv), v_list) {
179 		if (v->v_ipc_kvoucher == kv) {
180 			int xref_cnt = dispatch_atomic_inc2o(v, os_obj_xref_cnt, relaxed);
181 			_dispatch_voucher_debug("retain  -> %d", v, xref_cnt + 1);
182 			if (slowpath(xref_cnt < 0)) {
183 				_dispatch_voucher_debug("overrelease", v);
184 				DISPATCH_CRASH("Voucher overrelease");
185 			}
186 			if (xref_cnt == 0) {
187 				// resurrection: raced with _voucher_remove
188 				(void)dispatch_atomic_inc2o(v, os_obj_ref_cnt, relaxed);
189 			}
190 			break;
191 		}
192 	}
193 	_vouchers_lock_unlock();
194 	return v;
195 }
196 
197 static void
_voucher_insert(voucher_t v)198 _voucher_insert(voucher_t v)
199 {
200 	mach_voucher_t kv = v->v_ipc_kvoucher;
201 	if (!kv) return;
202 	_vouchers_lock_lock();
203 	if (slowpath(_TAILQ_IS_ENQUEUED(v, v_list))) {
204 		_dispatch_voucher_debug("corruption", v);
205 		DISPATCH_CRASH("Voucher corruption");
206 	}
207 	TAILQ_INSERT_TAIL(_vouchers(kv), v, v_list);
208 	_vouchers_lock_unlock();
209 }
210 
211 static void
_voucher_remove(voucher_t v)212 _voucher_remove(voucher_t v)
213 {
214 	mach_voucher_t kv = v->v_ipc_kvoucher;
215 	if (!_TAILQ_IS_ENQUEUED(v, v_list)) return;
216 	_vouchers_lock_lock();
217 	if (slowpath(!kv)) {
218 		_dispatch_voucher_debug("corruption", v);
219 		DISPATCH_CRASH("Voucher corruption");
220 	}
221 	// check for resurrection race with _voucher_find_and_retain
222 	if (dispatch_atomic_load2o(v, os_obj_xref_cnt, seq_cst) < 0 &&
223 			_TAILQ_IS_ENQUEUED(v, v_list)) {
224 		TAILQ_REMOVE(_vouchers(kv), v, v_list);
225 		_TAILQ_MARK_NOT_ENQUEUED(v, v_list);
226 		v->v_list.tqe_next = (void*)~0ull;
227 	}
228 	_vouchers_lock_unlock();
229 }
230 
231 void
_voucher_dealloc_mach_voucher(mach_voucher_t kv)232 _voucher_dealloc_mach_voucher(mach_voucher_t kv)
233 {
234 	_dispatch_kvoucher_debug("dealloc", kv);
235 	_dispatch_voucher_debug_machport(kv);
236 	kern_return_t kr = mach_voucher_deallocate(kv);
237 	DISPATCH_VERIFY_MIG(kr);
238 	(void)dispatch_assume_zero(kr);
239 }
240 
241 static inline kern_return_t
_voucher_create_mach_voucher(const mach_voucher_attr_recipe_data_t * recipes,size_t recipes_size,mach_voucher_t * kvp)242 _voucher_create_mach_voucher(const mach_voucher_attr_recipe_data_t *recipes,
243 		size_t recipes_size, mach_voucher_t *kvp)
244 {
245 	kern_return_t kr;
246 	mach_port_t mhp = _dispatch_get_mach_host_port();
247 	mach_voucher_t kv = MACH_VOUCHER_NULL;
248 	mach_voucher_attr_raw_recipe_array_t kvr;
249 	mach_voucher_attr_recipe_size_t kvr_size;
250 	kvr = (mach_voucher_attr_raw_recipe_array_t)recipes;
251 	kvr_size = (mach_voucher_attr_recipe_size_t)recipes_size;
252 	kr = host_create_mach_voucher(mhp, kvr, kvr_size, &kv);
253 	DISPATCH_VERIFY_MIG(kr);
254 	if (!kr) {
255 		_dispatch_kvoucher_debug("create", kv);
256 		_dispatch_voucher_debug_machport(kv);
257 	}
258 	*kvp = kv;
259 	return kr;
260 }
261 
262 #if __has_include(<bank/bank_types.h>) && !defined(VOUCHER_USE_ATTR_BANK)
263 #include <bank/bank_types.h>
264 #define VOUCHER_USE_ATTR_BANK 1
265 mach_voucher_t _voucher_default_task_mach_voucher;
266 #endif
267 
268 void
_voucher_task_mach_voucher_init(void * ctxt DISPATCH_UNUSED)269 _voucher_task_mach_voucher_init(void* ctxt DISPATCH_UNUSED)
270 {
271 #if VOUCHER_USE_ATTR_BANK
272 	kern_return_t kr;
273 	mach_voucher_t kv;
274 	static const mach_voucher_attr_recipe_data_t task_create_recipe = {
275 		.key = MACH_VOUCHER_ATTR_KEY_BANK,
276 		.command = MACH_VOUCHER_ATTR_BANK_CREATE,
277 	};
278 	kr = _voucher_create_mach_voucher(&task_create_recipe,
279 			sizeof(task_create_recipe), &kv);
280 	if (dispatch_assume_zero(kr)) {
281 		DISPATCH_CLIENT_CRASH("Could not create task mach voucher");
282 	}
283 	_voucher_default_task_mach_voucher = kv;
284 	_voucher_task_mach_voucher = kv;
285 #endif
286 }
287 
288 void
voucher_replace_default_voucher(void)289 voucher_replace_default_voucher(void)
290 {
291 #if VOUCHER_USE_ATTR_BANK
292 	(void)_voucher_get_task_mach_voucher(); // initalize task mach voucher
293 	mach_voucher_t kv, tkv = MACH_VOUCHER_NULL;
294 	voucher_t v = _voucher_get();
295 	if (v && v->v_kvoucher) {
296 		kern_return_t kr;
297 		kv = v->v_ipc_kvoucher ? v->v_ipc_kvoucher : v->v_kvoucher;
298 		const mach_voucher_attr_recipe_data_t task_copy_recipe = {
299 			.key = MACH_VOUCHER_ATTR_KEY_BANK,
300 			.command = MACH_VOUCHER_ATTR_COPY,
301 			.previous_voucher = kv,
302 		};
303 		kr = _voucher_create_mach_voucher(&task_copy_recipe,
304 				sizeof(task_copy_recipe), &tkv);
305 		if (dispatch_assume_zero(kr)) {
306 			tkv = MACH_VOUCHER_NULL;
307 		}
308 	}
309 	if (!tkv) tkv = _voucher_default_task_mach_voucher;
310 	kv = dispatch_atomic_xchg(&_voucher_task_mach_voucher, tkv, relaxed);
311 	if (kv && kv != _voucher_default_task_mach_voucher) {
312 		_voucher_dealloc_mach_voucher(kv);
313 	}
314 	_dispatch_voucher_debug("kvoucher[0x%08x] replace default voucher", v, tkv);
315 #endif
316 }
317 
318 static inline mach_voucher_t
_voucher_get_atm_mach_voucher(voucher_t voucher)319 _voucher_get_atm_mach_voucher(voucher_t voucher)
320 {
321 	_voucher_activity_t act = _voucher_activity_get(voucher);
322 	return _voucher_activity_get_atm_mach_voucher(act);
323 }
324 
325 mach_voucher_t
_voucher_get_mach_voucher(voucher_t voucher)326 _voucher_get_mach_voucher(voucher_t voucher)
327 {
328 	if (!voucher) return MACH_VOUCHER_NULL;
329 	if (voucher->v_ipc_kvoucher) return voucher->v_ipc_kvoucher;
330 	mach_voucher_t kvb = voucher->v_kvoucher;
331 	if (!kvb) kvb = _voucher_get_task_mach_voucher();
332 	if (!voucher->v_has_priority && !voucher->v_activities &&
333 			!_voucher_extra_size(voucher)) {
334 		return kvb;
335 	}
336 	kern_return_t kr;
337 	mach_voucher_t kv, kvo;
338 	_voucher_base_recipe(voucher).previous_voucher = kvb;
339 	_voucher_atm_recipe(voucher).previous_voucher =
340 			_voucher_get_atm_mach_voucher(voucher);
341 	kr = _voucher_create_mach_voucher(voucher->v_recipes,
342 			_voucher_recipes_size() + _voucher_extra_size(voucher) +
343 			_voucher_bits_recipe(voucher).content_size, &kv);
344 	if (dispatch_assume_zero(kr) || !kv){
345 		return MACH_VOUCHER_NULL;
346 	}
347 	if (!dispatch_atomic_cmpxchgv2o(voucher, v_ipc_kvoucher, MACH_VOUCHER_NULL,
348 			kv, &kvo, relaxed)) {
349 		_voucher_dealloc_mach_voucher(kv);
350 		kv = kvo;
351 	} else {
352 		if (kv == voucher->v_kvoucher) {
353 			// if v_kvoucher == v_ipc_kvoucher we keep only one reference
354 			_voucher_dealloc_mach_voucher(kv);
355 		}
356 		_voucher_insert(voucher);
357 		_dispatch_voucher_debug("kvoucher[0x%08x] create", voucher, kv);
358 	}
359 	return kv;
360 }
361 
362 mach_voucher_t
_voucher_create_mach_voucher_with_priority(voucher_t voucher,pthread_priority_t priority)363 _voucher_create_mach_voucher_with_priority(voucher_t voucher,
364 		pthread_priority_t priority)
365 {
366 	if (priority == _voucher_get_priority(voucher)) {
367 		return MACH_VOUCHER_NULL; // caller will use _voucher_get_mach_voucher
368 	}
369 	kern_return_t kr;
370 	mach_voucher_t kv, kvb = voucher ? voucher->v_kvoucher : MACH_VOUCHER_NULL;
371 	if (!kvb) kvb = _voucher_get_task_mach_voucher();
372 	mach_voucher_attr_recipe_data_t *recipes;
373 	size_t recipes_size = _voucher_recipes_size();
374 	if (voucher && (voucher->v_has_priority || voucher->v_activities ||
375 			_voucher_extra_size(voucher))) {
376 		recipes_size += _voucher_bits_recipe(voucher).content_size +
377 				_voucher_extra_size(voucher);
378 		recipes = alloca(recipes_size);
379 		memcpy(recipes, voucher->v_recipes, recipes_size);
380 		_voucher_recipes_atm(recipes).previous_voucher =
381 				_voucher_get_atm_mach_voucher(voucher);
382 	} else {
383 		mach_voucher_attr_content_size_t bits_size = _voucher_bits_size(0);
384 		recipes_size += bits_size;
385 		recipes = alloca(recipes_size);
386 		_voucher_recipes_init(recipes, bits_size);
387 	}
388 	_voucher_recipes_base(recipes).previous_voucher = kvb;
389 	*_voucher_recipes_priority(recipes) = (_voucher_priority_t)priority;
390 	kr = _voucher_create_mach_voucher(recipes, recipes_size, &kv);
391 	if (dispatch_assume_zero(kr) || !kv){
392 		return MACH_VOUCHER_NULL;
393 	}
394 	_dispatch_kvoucher_debug("create with priority from voucher[%p]", kv,
395 			voucher);
396 	return kv;
397 }
398 
399 static voucher_t
_voucher_create_with_mach_voucher(mach_voucher_t kv)400 _voucher_create_with_mach_voucher(mach_voucher_t kv)
401 {
402 	if (!kv) return NULL;
403 	kern_return_t kr;
404 	mach_voucher_t rkv;
405 	mach_voucher_attr_recipe_t vr;
406 	size_t vr_size;
407 	mach_voucher_attr_recipe_size_t kvr_size = 0;
408 	const mach_voucher_attr_recipe_data_t redeem_recipe[] = {
409 		[0] = {
410 			.key = MACH_VOUCHER_ATTR_KEY_ALL,
411 			.command = MACH_VOUCHER_ATTR_COPY,
412 			.previous_voucher = kv,
413 		},
414 #if VOUCHER_USE_ATTR_BANK
415 		[1] = {
416 			.key = MACH_VOUCHER_ATTR_KEY_BANK,
417 			.command = MACH_VOUCHER_ATTR_REDEEM,
418 		},
419 #endif
420 	};
421 	kr = _voucher_create_mach_voucher(redeem_recipe, sizeof(redeem_recipe),
422 			&rkv);
423 	if (!dispatch_assume_zero(kr)) {
424 		_voucher_dealloc_mach_voucher(kv);
425 	} else {
426 		_dispatch_voucher_debug_machport(kv);
427 		rkv = kv;
428 	}
429 	voucher_t v = _voucher_find_and_retain(rkv);
430 	if (v) {
431 		_dispatch_voucher_debug("kvoucher[0x%08x] find with 0x%08x", v, rkv,kv);
432 		_voucher_dealloc_mach_voucher(rkv);
433 		return v;
434 	}
435 	vr_size = sizeof(*vr) + _voucher_bits_size(_voucher_max_activities);
436 	vr = alloca(vr_size);
437 	if (rkv) {
438 		kvr_size = (mach_voucher_attr_recipe_size_t)vr_size;
439 		kr = mach_voucher_extract_attr_recipe(rkv,
440 				MACH_VOUCHER_ATTR_KEY_USER_DATA, (void*)vr, &kvr_size);
441 		DISPATCH_VERIFY_MIG(kr);
442 		if (dispatch_assume_zero(kr)) kvr_size = 0;
443 	}
444 	mach_voucher_attr_content_size_t content_size = vr->content_size;
445 	uint8_t *content = vr->content;
446 	bool valid = false, has_priority = false;
447 	unsigned int activities = 0;
448 	if (kvr_size >= sizeof(*vr) + sizeof(_voucher_magic_t)) {
449 		valid = (*(_voucher_magic_t*)content == _voucher_magic_v1);
450 		content += sizeof(_voucher_magic_t);
451 		content_size -= sizeof(_voucher_magic_t);
452 	}
453 	if (valid) {
454 		has_priority = (content_size >= sizeof(_voucher_priority_t));
455 		activities = has_priority ? (content_size - sizeof(_voucher_priority_t))
456 				/ sizeof(voucher_activity_id_t) : 0;
457 	}
458 	pthread_priority_t priority = 0;
459 	if (has_priority) {
460 		priority = (pthread_priority_t)*(_voucher_priority_t*)content;
461 		content += sizeof(_voucher_priority_t);
462 		content_size -= sizeof(_voucher_priority_t);
463 	}
464 	voucher_activity_id_t va_id = 0, va_base_id = 0;
465 	_voucher_activity_t act = NULL;
466 	if (activities) {
467 		va_id = *(voucher_activity_id_t*)content;
468 		act = _voucher_activity_copy_from_mach_voucher(rkv, va_id);
469 		if (!act && _voucher_activity_default) {
470 			activities++;
471 			// default to _voucher_activity_default base activity
472 			va_base_id = _voucher_activity_default->va_id;
473 		} else if (act && act->va_id != va_id) {
474 			activities++;
475 			va_base_id = act->va_id;
476 		}
477 	}
478 	v = _voucher_alloc(activities, priority, 0);
479 	v->v_activity = act;
480 	voucher_activity_id_t *activity_ids = _voucher_activity_ids(v);
481 	if (activities && va_base_id) {
482 		*activity_ids++ = va_base_id;
483 		activities--;
484 	}
485 	if (activities) {
486 		memcpy(activity_ids, content, content_size);
487 	}
488 	v->v_ipc_kvoucher = v->v_kvoucher = rkv;
489 	_voucher_insert(v);
490 	_dispatch_voucher_debug("kvoucher[0x%08x] create with 0x%08x", v, rkv, kv);
491 	return v;
492 }
493 
494 voucher_t
_voucher_create_with_priority_and_mach_voucher(voucher_t ov,pthread_priority_t priority,mach_voucher_t kv)495 _voucher_create_with_priority_and_mach_voucher(voucher_t ov,
496 		pthread_priority_t priority, mach_voucher_t kv)
497 {
498 	if (priority == _voucher_get_priority(ov)) {
499 		if (kv) _voucher_dealloc_mach_voucher(kv);
500 		return ov ? _voucher_retain(ov) : NULL;
501 	}
502 	voucher_t v = _voucher_find_and_retain(kv);
503 	if (v) {
504 		_dispatch_voucher_debug("kvoucher[0x%08x] find", v, kv);
505 		_voucher_dealloc_mach_voucher(kv);
506 		return v;
507 	}
508 	unsigned int activities = ov ? ov->v_activities : 0;
509 	mach_voucher_attr_recipe_size_t extra = ov ? _voucher_extra_size(ov) : 0;
510 	v = _voucher_alloc(activities, priority, extra);
511 	if (extra) {
512 		memcpy(_voucher_extra_recipes(v), _voucher_extra_recipes(ov), extra);
513 	}
514 	if (activities) {
515 		if (ov->v_activity) {
516 			v->v_activity = _voucher_activity_retain(ov->v_activity);
517 		}
518 		memcpy(_voucher_activity_ids(v), _voucher_activity_ids(ov),
519 				activities * sizeof(voucher_activity_id_t));
520 	}
521 	if (kv) {
522 		v->v_ipc_kvoucher = v->v_kvoucher = kv;
523 		_voucher_insert(v);
524 		_dispatch_voucher_debug("kvoucher[0x%08x] create with priority from "
525 				"voucher[%p]", v, kv, ov);
526 		_dispatch_voucher_debug_machport(kv);
527 	} else if (ov && ov->v_kvoucher) {
528 		voucher_t kvb = ov->v_kvbase ? ov->v_kvbase : ov;
529 		v->v_kvbase = _voucher_retain(kvb);
530 		v->v_kvoucher = kvb->v_kvoucher;
531 	}
532 	return v;
533 }
534 
535 voucher_t
_voucher_create_without_importance(voucher_t ov)536 _voucher_create_without_importance(voucher_t ov)
537 {
538 	// Nothing to do unless the old voucher has a kernel voucher. If it
539 	// doesn't, it can't have any importance, now or in the future.
540 	if (!ov) return NULL;
541 	// TODO: 17487167: track presence of importance attribute
542 	if (!ov->v_kvoucher) return _voucher_retain(ov);
543 	kern_return_t kr;
544 	mach_voucher_t kv, okv;
545 	// Copy kernel voucher, removing importance.
546 	okv = ov->v_ipc_kvoucher ? ov->v_ipc_kvoucher : ov->v_kvoucher;
547 	const mach_voucher_attr_recipe_data_t importance_remove_recipe[] = {
548 		[0] = {
549 			.key = MACH_VOUCHER_ATTR_KEY_ALL,
550 			.command = MACH_VOUCHER_ATTR_COPY,
551 			.previous_voucher = okv,
552 		},
553 		[1] = {
554 			.key = MACH_VOUCHER_ATTR_KEY_IMPORTANCE,
555 			.command = MACH_VOUCHER_ATTR_REMOVE,
556 		},
557 	};
558 	kr = _voucher_create_mach_voucher(importance_remove_recipe,
559 			sizeof(importance_remove_recipe), &kv);
560 	if (dispatch_assume_zero(kr) || !kv){
561 		if (ov->v_ipc_kvoucher) return NULL;
562 		kv = MACH_VOUCHER_NULL;
563 	}
564 	if (kv == okv) {
565 		_voucher_dealloc_mach_voucher(kv);
566 		return _voucher_retain(ov);
567 	}
568 	voucher_t v = _voucher_find_and_retain(kv);
569 	if (v && ov->v_ipc_kvoucher) {
570 		_dispatch_voucher_debug("kvoucher[0x%08x] find without importance "
571 				"from voucher[%p]", v, kv, ov);
572 		_voucher_dealloc_mach_voucher(kv);
573 		return v;
574 	}
575 	voucher_t kvbase = v;
576 	// Copy userspace contents
577 	unsigned int activities = ov->v_activities;
578 	pthread_priority_t priority = _voucher_get_priority(ov);
579 	mach_voucher_attr_recipe_size_t extra = _voucher_extra_size(ov);
580 	v = _voucher_alloc(activities, priority, extra);
581 	if (extra) {
582 		memcpy(_voucher_extra_recipes(v), _voucher_extra_recipes(ov), extra);
583 	}
584 	if (activities) {
585 		if (ov->v_activity) {
586 			v->v_activity = _voucher_activity_retain(ov->v_activity);
587 		}
588 		memcpy(_voucher_activity_ids(v), _voucher_activity_ids(ov),
589 				activities * sizeof(voucher_activity_id_t));
590 	}
591 	v->v_kvoucher = kv;
592 	if (ov->v_ipc_kvoucher) {
593 		v->v_ipc_kvoucher = kv;
594 		_voucher_insert(v);
595 	} else if (kvbase) {
596 		v->v_kvbase = kvbase;
597 		_voucher_dealloc_mach_voucher(kv); // borrow base reference
598 	}
599 	if (!kvbase) {
600 		_dispatch_voucher_debug("kvoucher[0x%08x] create without importance "
601 				"from voucher[%p]", v, kv, ov);
602 	}
603 	return v;
604 }
605 
606 voucher_t
voucher_create_with_mach_msg(mach_msg_header_t * msg)607 voucher_create_with_mach_msg(mach_msg_header_t *msg)
608 {
609 	voucher_t v = _voucher_create_with_mach_voucher(_voucher_mach_msg_get(msg));
610 	_voucher_activity_trace_msg(v, msg, receive);
611 	return v;
612 }
613 
614 #ifndef MACH_VOUCHER_IMPORTANCE_ATTR_DROP_EXTERNAL
615 #define MACH_VOUCHER_IMPORTANCE_ATTR_DROP_EXTERNAL 2
616 #endif
617 
618 void
voucher_decrement_importance_count4CF(voucher_t v)619 voucher_decrement_importance_count4CF(voucher_t v)
620 {
621 	if (!v || !v->v_kvoucher) return;
622 	// TODO: 17487167: track presence of importance attribute
623 	kern_return_t kr;
624 	mach_voucher_t kv = v->v_ipc_kvoucher ? v->v_ipc_kvoucher : v->v_kvoucher;
625 	uint32_t dec = 1;
626 	mach_voucher_attr_content_t kvc_in = (mach_voucher_attr_content_t)&dec;
627 	mach_voucher_attr_content_size_t kvc_in_size = sizeof(dec);
628 	mach_voucher_attr_content_t kvc_out = NULL;
629 	mach_voucher_attr_content_size_t kvc_out_size = 0;
630 #if DISPATCH_DEBUG
631 	uint32_t count = UINT32_MAX;
632 	kvc_out = (mach_voucher_attr_content_t)&count;
633 	kvc_out_size = sizeof(count);
634 #endif
635 	kr = mach_voucher_attr_command(kv, MACH_VOUCHER_ATTR_KEY_IMPORTANCE,
636 			MACH_VOUCHER_IMPORTANCE_ATTR_DROP_EXTERNAL, kvc_in, kvc_in_size,
637 			kvc_out, &kvc_out_size);
638 	DISPATCH_VERIFY_MIG(kr);
639 #if DISPATCH_DEBUG
640 	_dispatch_voucher_debug("kvoucher[0x%08x] decrement importance count to %u:"
641 			" %s - 0x%x", v, kv, count, mach_error_string(kr), kr);
642 #endif
643 	if (kr != KERN_INVALID_ARGUMENT &&
644 			dispatch_assume_zero(kr) == KERN_FAILURE) {
645 		// TODO: 17487167: skip KERN_INVALID_ARGUMENT check
646 		DISPATCH_CLIENT_CRASH("Voucher importance count underflow");
647 	}
648 }
649 
650 #if VOUCHER_ENABLE_GET_MACH_VOUCHER
651 mach_voucher_t
voucher_get_mach_voucher(voucher_t voucher)652 voucher_get_mach_voucher(voucher_t voucher)
653 {
654 	return _voucher_get_mach_voucher(voucher);
655 }
656 #endif
657 
658 void
_voucher_xref_dispose(voucher_t voucher)659 _voucher_xref_dispose(voucher_t voucher)
660 {
661 	_dispatch_voucher_debug("xref_dispose", voucher);
662 	_voucher_remove(voucher);
663 	return _os_object_release_internal_inline((_os_object_t)voucher);
664 }
665 
666 void
_voucher_dispose(voucher_t voucher)667 _voucher_dispose(voucher_t voucher)
668 {
669 	_dispatch_voucher_debug("dispose", voucher);
670 	if (slowpath(_TAILQ_IS_ENQUEUED(voucher, v_list))) {
671 		_dispatch_voucher_debug("corruption", voucher);
672 		DISPATCH_CRASH("Voucher corruption");
673 	}
674 	voucher->v_list.tqe_next = DISPATCH_OBJECT_LISTLESS;
675 	if (voucher->v_ipc_kvoucher) {
676 		if (voucher->v_ipc_kvoucher != voucher->v_kvoucher) {
677 			_voucher_dealloc_mach_voucher(voucher->v_ipc_kvoucher);
678 		}
679 		voucher->v_ipc_kvoucher = MACH_VOUCHER_NULL;
680 	}
681 	if (voucher->v_kvoucher) {
682 		if (!voucher->v_kvbase) {
683 			_voucher_dealloc_mach_voucher(voucher->v_kvoucher);
684 		}
685 		voucher->v_kvoucher = MACH_VOUCHER_NULL;
686 	}
687 	if (voucher->v_kvbase) {
688 		_voucher_release(voucher->v_kvbase);
689 		voucher->v_kvbase = NULL;
690 	}
691 	if (voucher->v_activity) {
692 		_voucher_activity_release(voucher->v_activity);
693 		voucher->v_activity = NULL;
694 	}
695 	voucher->v_has_priority= 0;
696 	voucher->v_activities = 0;
697 #if VOUCHER_ENABLE_RECIPE_OBJECTS
698 	voucher->v_recipe_extra_size = 0;
699 	voucher->v_recipe_extra_offset = 0;
700 #endif
701 	return _os_object_dealloc((_os_object_t)voucher);
702 }
703 
704 void
_voucher_atfork_child(void)705 _voucher_atfork_child(void)
706 {
707 	_voucher_activity_atfork_child();
708 	_dispatch_thread_setspecific(dispatch_voucher_key, NULL);
709 	_voucher_task_mach_voucher_pred = 0;
710 	_voucher_task_mach_voucher = MACH_VOUCHER_NULL;
711 
712 	// TODO: voucher/activity inheritance on fork ?
713 }
714 
715 #pragma mark -
716 #pragma mark _voucher_init
717 
718 boolean_t
voucher_mach_msg_set(mach_msg_header_t * msg)719 voucher_mach_msg_set(mach_msg_header_t *msg)
720 {
721 	voucher_t v = _voucher_get();
722 	bool clear_voucher = _voucher_mach_msg_set(msg, v);
723 	if (clear_voucher) _voucher_activity_trace_msg(v, msg, send);
724 	return clear_voucher;
725 }
726 
727 void
voucher_mach_msg_clear(mach_msg_header_t * msg)728 voucher_mach_msg_clear(mach_msg_header_t *msg)
729 {
730 	(void)_voucher_mach_msg_clear(msg, false);
731 }
732 
733 voucher_mach_msg_state_t
voucher_mach_msg_adopt(mach_msg_header_t * msg)734 voucher_mach_msg_adopt(mach_msg_header_t *msg)
735 {
736 	mach_voucher_t kv = _voucher_mach_msg_get(msg);
737 	if (!kv) return VOUCHER_MACH_MSG_STATE_UNCHANGED;
738 	voucher_t v = _voucher_create_with_mach_voucher(kv);
739 	_voucher_activity_trace_msg(v, msg, receive);
740 	return (voucher_mach_msg_state_t)_voucher_adopt(v);
741 }
742 
743 void
voucher_mach_msg_revert(voucher_mach_msg_state_t state)744 voucher_mach_msg_revert(voucher_mach_msg_state_t state)
745 {
746 	if (state == VOUCHER_MACH_MSG_STATE_UNCHANGED) return;
747 	_voucher_replace((voucher_t)state);
748 }
749 
750 #if DISPATCH_USE_LIBKERNEL_VOUCHER_INIT
751 #include <_libkernel_init.h>
752 
753 static const struct _libkernel_voucher_functions _voucher_libkernel_functions =
754 {
755 	.version = 1,
756 	.voucher_mach_msg_set = voucher_mach_msg_set,
757 	.voucher_mach_msg_clear = voucher_mach_msg_clear,
758 	.voucher_mach_msg_adopt = voucher_mach_msg_adopt,
759 	.voucher_mach_msg_revert = voucher_mach_msg_revert,
760 };
761 
762 static void
_voucher_libkernel_init(void)763 _voucher_libkernel_init(void)
764 {
765 	kern_return_t kr = __libkernel_voucher_init(&_voucher_libkernel_functions);
766 	dispatch_assert(!kr);
767 }
768 #else
769 #define _voucher_libkernel_init()
770 #endif
771 
772 void
_voucher_init(void)773 _voucher_init(void)
774 {
775 	_voucher_libkernel_init();
776 	char *e, *end;
777 	unsigned int i;
778 	for (i = 0; i < VL_HASH_SIZE; i++) {
779 		TAILQ_INIT(&_vouchers[i]);
780 	}
781 	voucher_activity_mode_t mode;
782 	mode = DISPATCH_DEBUG ? voucher_activity_mode_debug
783 			: voucher_activity_mode_release;
784 	e = getenv("OS_ACTIVITY_MODE");
785 	if (e) {
786 		if (strcmp(e, "release") == 0) {
787 			mode = voucher_activity_mode_release;
788 		} else if (strcmp(e, "debug") == 0) {
789 			mode = voucher_activity_mode_debug;
790 		} else if (strcmp(e, "stream") == 0) {
791 			mode = voucher_activity_mode_stream;
792 		} else if (strcmp(e, "disable") == 0) {
793 			mode = voucher_activity_mode_disable;
794 		}
795 	}
796 	_voucher_activity_mode = mode;
797 	if (_voucher_activity_disabled()) return;
798 
799 	e = getenv("LIBDISPATCH_ACTIVITY_ATM_SUBID_BITS");
800 	if (e) {
801 		unsigned long v = strtoul(e, &end, 0);
802 		if (v && !*end) {
803 			_voucher_atm_subid_bits = v;
804 		}
805 	}
806 	e = getenv("LIBDISPATCH_ACTIVITY_ATM_COLLECT_THRESHOLD");
807 	if (e) {
808 		unsigned long v = strtoul(e, &end, 0);
809 		if (v && v < LONG_MAX/2 && !*end) {
810 			_voucher_atm_collect_threshold =
811 					VATM_COLLECT_THRESHOLD_VALUE((long)v);
812 		}
813 	}
814 	// default task activity
815 	bool default_task_activity = DISPATCH_DEBUG;
816 	e = getenv("LIBDISPATCH_DEFAULT_TASK_ACTIVITY");
817 	if (e) default_task_activity = atoi(e);
818 	if (default_task_activity) {
819 		(void)voucher_activity_start(_voucher_activity_trace_id_release, 0);
820 	}
821 }
822 
823 #pragma mark -
824 #pragma mark _voucher_activity_lock_s
825 
826 DISPATCH_ALWAYS_INLINE
827 static inline void
_voucher_activity_lock_init(_voucher_activity_lock_s * lock)828 _voucher_activity_lock_init(_voucher_activity_lock_s *lock) {
829     static const os_lock_handoff_s _os_lock_handoff_init = OS_LOCK_HANDOFF_INIT;
830     *lock = _os_lock_handoff_init;
831 }
832 
833 DISPATCH_ALWAYS_INLINE
834 static inline void
_voucher_activity_lock_lock(_voucher_activity_lock_s * lock)835 _voucher_activity_lock_lock(_voucher_activity_lock_s *lock) {
836     return os_lock_lock(lock);
837 }
838 
839 DISPATCH_ALWAYS_INLINE
840 static inline void
_voucher_activity_lock_unlock(_voucher_activity_lock_s * lock)841 _voucher_activity_lock_unlock(_voucher_activity_lock_s *lock) {
842     return os_lock_unlock(lock);
843 }
844 
845 #pragma mark -
846 #pragma mark _voucher_activity_heap
847 
848 #if __has_extension(c_static_assert)
849 _Static_assert(sizeof(struct _voucher_activity_tracepoint_s) == 64,
850 		"Tracepoint too large");
851 _Static_assert(sizeof(struct _voucher_activity_buffer_header_s) <=
852 		sizeof(struct _voucher_activity_tracepoint_s),
853 		"Buffer header too large");
854 _Static_assert(offsetof(struct _voucher_activity_s, va_flags2) ==
855 		sizeof(struct _voucher_activity_tracepoint_s),
856 		"Extended activity object misaligned");
857 #if __LP64__
858 _Static_assert(sizeof(struct _voucher_activity_s) ==
859 		3 * sizeof(struct _voucher_activity_tracepoint_s),
860 		"Activity object too large");
861 _Static_assert(offsetof(struct _voucher_activity_s, va_flags3) ==
862 		2 * sizeof(struct _voucher_activity_tracepoint_s),
863 		"Extended activity object misaligned");
864 _Static_assert(offsetof(struct _voucher_atm_s, vatm_activities_lock) % 64 == 0,
865 		"Bad ATM padding");
866 _Static_assert(sizeof(struct _voucher_atm_s) <= 128,
867 		"ATM too large");
868 #else
869 _Static_assert(sizeof(struct _voucher_activity_s) ==
870 		2 * sizeof(struct _voucher_activity_tracepoint_s),
871 		"Activity object too large");
872 _Static_assert(sizeof(struct _voucher_atm_s) <= 64,
873 		"ATM too large");
874 #endif
875 _Static_assert(sizeof(_voucher_activity_buffer_t) ==
876 		sizeof(struct {char x[_voucher_activity_buffer_size];}),
877 		"Buffer too large");
878 _Static_assert(sizeof(struct _voucher_activity_metadata_s) <=
879 		sizeof(struct _voucher_activity_metadata_opaque_s),
880 		"Metadata too large");
881 _Static_assert(sizeof(_voucher_activity_bitmap_t) % 64 == 0,
882 		"Bad metadata bitmap size");
883 _Static_assert(offsetof(struct _voucher_activity_metadata_s,
884 		vam_atm_mbox_bitmap) % 64 == 0,
885 		"Bad metadata padding");
886 _Static_assert(offsetof(struct _voucher_activity_metadata_s,
887 		vam_base_atm_subid) % 64 == 0,
888 		"Bad metadata padding");
889 _Static_assert(offsetof(struct _voucher_activity_metadata_s, vam_base_atm_lock)
890 		% 32 == 0,
891 		"Bad metadata padding");
892 _Static_assert(offsetof(struct _voucher_activity_metadata_s, vam_atms) % 64 ==0,
893 		"Bad metadata padding");
894 _Static_assert(sizeof(_voucher_activity_bitmap_t) * 8 *
895 		sizeof(atm_mailbox_offset_t) <=
896 		sizeof(((_voucher_activity_metadata_t)NULL)->vam_kernel_metadata),
897 		"Bad kernel metadata bitmap");
898 _Static_assert(sizeof(atm_mailbox_offset_t) == 2 * sizeof(atm_subaid32_t),
899 		"Bad kernel ATM mailbox sizes");
900 #endif
901 
902 static const size_t _voucher_atm_mailboxes =
903 		sizeof(((_voucher_activity_metadata_t)NULL)->vam_kernel_metadata) /
904 		sizeof(atm_mailbox_offset_t);
905 
906 #define va_buffers_lock(va) (&(va)->va_buffers_lock)
907 #define vatm_activities_lock(vatm) (&(vatm)->vatm_activities_lock)
908 #define vatm_activities(vatm) (&(vatm)->vatm_activities)
909 #define vatm_used_activities(vatm) (&(vatm)->vatm_used_activities)
910 #define vam_base_atm_lock() (&_voucher_activity_heap->vam_base_atm_lock)
911 #define vam_nested_atm_lock() (&_voucher_activity_heap->vam_nested_atm_lock)
912 #define vam_atms_lock() (&_voucher_activity_heap->vam_atms_lock)
913 #define vam_activities_lock() (&_voucher_activity_heap->vam_activities_lock)
914 #define vam_atms(hash) (&_voucher_activity_heap->vam_atms[hash])
915 #define vam_activities(hash) (&_voucher_activity_heap->vam_activities[hash])
916 #define vam_buffer_bitmap() (_voucher_activity_heap->vam_buffer_bitmap)
917 #define vam_atm_mbox_bitmap() (_voucher_activity_heap->vam_atm_mbox_bitmap)
918 #define vam_pressure_locked_bitmap() \
919 		(_voucher_activity_heap->vam_pressure_locked_bitmap)
920 #define vam_buffer(i) ((void*)((char*)_voucher_activity_heap + \
921 		(i) * _voucher_activity_buffer_size))
922 
923 static _voucher_activity_t _voucher_activity_create_with_atm(
924 		_voucher_atm_t vatm, voucher_activity_id_t va_id,
925 		voucher_activity_trace_id_t trace_id, uint64_t location,
926 		_voucher_activity_buffer_header_t buffer);
927 static _voucher_atm_t _voucher_atm_create(mach_voucher_t kv, atm_aid_t atm_id);
928 static voucher_activity_id_t _voucher_atm_nested_atm_id_make(void);
929 
930 DISPATCH_ALWAYS_INLINE
931 static inline uint32_t
_voucher_default_activity_buffer_limit()932 _voucher_default_activity_buffer_limit()
933 {
934 	switch (_voucher_activity_mode) {
935 	case voucher_activity_mode_debug:
936 	case voucher_activity_mode_stream:
937 		// High-profile modes: Default activity can use 1/32nd of the heap
938 		// (twice as much as non-default activities)
939 		return MAX(_voucher_activity_buffers_per_heap / 32, 3) - 1;
940 	}
941 #if TARGET_OS_EMBEDDED
942 	// Low-profile modes: Default activity can use a total of 3 buffers.
943 	return 2;
944 #else
945 	// Low-profile modes: Default activity can use a total of 8 buffers.
946 	return 7;
947 #endif
948 }
949 
950 DISPATCH_ALWAYS_INLINE
951 static inline uint32_t
_voucher_activity_buffer_limit()952 _voucher_activity_buffer_limit()
953 {
954 	switch (_voucher_activity_mode) {
955 	case voucher_activity_mode_debug:
956 	case voucher_activity_mode_stream:
957 		// High-profile modes: 64 activities, each of which can use 1/64th
958 		// of the entire heap.
959 		return MAX(_voucher_activity_buffers_per_heap / 64, 2) - 1;
960 	}
961 #if TARGET_OS_EMBEDDED
962 	// Low-profile modes: Each activity can use a total of 2 buffers.
963 	return 1;
964 #else
965 	// Low-profile modes: Each activity can use a total of 4 buffers.
966 	return 3;
967 #endif
968 }
969 
970 // The two functions above return the number of *additional* buffers activities
971 // may allocate, hence the gymnastics with - 1.
972 
973 DISPATCH_ALWAYS_INLINE
974 static inline uint32_t
_voucher_heap_buffer_limit()975 _voucher_heap_buffer_limit()
976 {
977 	switch (_voucher_activity_mode) {
978 	case voucher_activity_mode_debug:
979 	case voucher_activity_mode_stream:
980 		// High-profile modes: Use it all.
981 		return _voucher_activity_buffers_per_heap;
982 	}
983 #if TARGET_OS_EMBEDDED
984 	// Low-profile modes: 3 activities, each of which can use 2 buffers;
985 	// plus the default activity, which can use 3; plus 3 buffers of overhead.
986 	return 12;
987 #else
988 	// Low-profile modes: 13 activities, each of which can use 4 buffers;
989 	// plus the default activity, which can use 8; plus 3 buffers of overhead.
990 	return 64;
991 #endif
992 }
993 
994 #define NO_BITS_WERE_UNSET (UINT_MAX)
995 
996 DISPATCH_ALWAYS_INLINE
997 static inline size_t
_voucher_activity_bitmap_set_first_unset_bit_upto(_voucher_activity_bitmap_t volatile bitmap,unsigned int max_index)998 _voucher_activity_bitmap_set_first_unset_bit_upto(
999 		_voucher_activity_bitmap_t volatile bitmap,
1000 		unsigned int max_index)
1001 {
1002 	dispatch_assert(max_index != 0);
1003 	unsigned int index = NO_BITS_WERE_UNSET, max_map, max_bit, i;
1004 	max_map = max_index / _voucher_activity_bits_per_bitmap_base_t;
1005 	max_map = MIN(max_map, _voucher_activity_bitmaps_per_heap - 1);
1006 	max_bit = max_index % _voucher_activity_bits_per_bitmap_base_t;
1007 	for (i = 0; i < max_map; i++) {
1008 		index = dispatch_atomic_set_first_bit(&bitmap[i], UINT_MAX);
1009 		if (fastpath(index < NO_BITS_WERE_UNSET)) {
1010 			return index + i * _voucher_activity_bits_per_bitmap_base_t;
1011 		}
1012 	}
1013 	index = dispatch_atomic_set_first_bit(&bitmap[i], max_bit);
1014 	if (fastpath(index < NO_BITS_WERE_UNSET)) {
1015 		return index + i * _voucher_activity_bits_per_bitmap_base_t;
1016 	}
1017 	return index;
1018 }
1019 
1020 DISPATCH_ALWAYS_INLINE
1021 static inline size_t
_voucher_activity_bitmap_set_first_unset_bit(_voucher_activity_bitmap_t volatile bitmap)1022 _voucher_activity_bitmap_set_first_unset_bit(
1023 		_voucher_activity_bitmap_t volatile bitmap)
1024 {
1025 	return _voucher_activity_bitmap_set_first_unset_bit_upto(bitmap, UINT_MAX);
1026 }
1027 
1028 
1029 DISPATCH_ALWAYS_INLINE
1030 static inline void
_voucher_activity_bitmap_clear_bit(_voucher_activity_bitmap_t volatile bitmap,size_t index)1031 _voucher_activity_bitmap_clear_bit(
1032 		_voucher_activity_bitmap_t volatile bitmap, size_t index)
1033 {
1034 	size_t i = index / _voucher_activity_bits_per_bitmap_base_t;
1035 	_voucher_activity_bitmap_base_t mask = ((typeof(mask))1) <<
1036 			(index % _voucher_activity_bits_per_bitmap_base_t);
1037 	if (slowpath((bitmap[i] & mask) == 0)) {
1038 		DISPATCH_CRASH("Corruption: failed to clear bit exclusively");
1039 	}
1040 	(void)dispatch_atomic_and(&bitmap[i], ~mask, release);
1041 }
1042 
1043 _voucher_activity_metadata_t _voucher_activity_heap;
1044 static dispatch_once_t _voucher_activity_heap_pred;
1045 
1046 static void
_voucher_activity_heap_init(void * ctxt DISPATCH_UNUSED)1047 _voucher_activity_heap_init(void *ctxt DISPATCH_UNUSED)
1048 {
1049 	if (_voucher_activity_disabled()) return;
1050 	kern_return_t kr;
1051 	mach_vm_size_t vm_size = _voucher_activity_buffer_size *
1052 			_voucher_activity_buffers_per_heap;
1053 	mach_vm_address_t vm_addr = vm_page_size;
1054 	while (slowpath(kr = mach_vm_map(mach_task_self(), &vm_addr, vm_size,
1055 			0, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_GENEALOGY),
1056 			MEMORY_OBJECT_NULL, 0, FALSE, VM_PROT_DEFAULT, VM_PROT_ALL,
1057 			VM_INHERIT_NONE))) {
1058 		if (kr != KERN_NO_SPACE) {
1059 			(void)dispatch_assume_zero(kr);
1060 			_voucher_activity_mode = voucher_activity_mode_disable;
1061 			return;
1062 		}
1063 		_dispatch_temporary_resource_shortage();
1064 		vm_addr = vm_page_size;
1065 	}
1066 	_voucher_activity_metadata_t heap;
1067 	task_trace_memory_info_data_t trace_memory_info = {
1068 		.user_memory_address = vm_addr,
1069 		.buffer_size = vm_size,
1070 		.mailbox_array_size = sizeof(heap->vam_kernel_metadata),
1071 	};
1072 	kr = task_set_info(mach_task_self(), TASK_TRACE_MEMORY_INFO,
1073 			(task_info_t)&trace_memory_info, TASK_TRACE_MEMORY_INFO_COUNT);
1074 	DISPATCH_VERIFY_MIG(kr);
1075 	if (kr) {
1076 		if (kr != KERN_NOT_SUPPORTED) (void)dispatch_assume_zero(kr);
1077 		kr = mach_vm_deallocate(mach_task_self(), vm_addr, vm_size);
1078 		(void)dispatch_assume_zero(kr);
1079 		_voucher_activity_mode = voucher_activity_mode_disable;
1080 		return;
1081 	}
1082 	heap = (void*)vm_addr;
1083 	heap->vam_self_metadata.vasm_baseaddr = (void*)vm_addr;
1084 	heap->vam_buffer_bitmap[0] = 0xf; // first four buffers are reserved
1085 	uint32_t i;
1086 	for (i = 0; i < _voucher_activity_hash_size; i++) {
1087 		TAILQ_INIT(&heap->vam_activities[i]);
1088 		TAILQ_INIT(&heap->vam_atms[i]);
1089 	}
1090 	uint32_t subid_max = VATM_SUBID_MAX;
1091 	if (_voucher_atm_subid_bits &&
1092 			_voucher_atm_subid_bits < VATM_SUBID_MAXBITS) {
1093 		subid_max = MIN(VATM_SUBID_BITS2MAX(_voucher_atm_subid_bits),
1094 				VATM_SUBID_MAX);
1095 	}
1096 	heap->vam_base_atm_subid_max = subid_max;
1097 	_voucher_activity_lock_init(&heap->vam_base_atm_lock);
1098 	_voucher_activity_lock_init(&heap->vam_nested_atm_lock);
1099 	_voucher_activity_lock_init(&heap->vam_atms_lock);
1100 	_voucher_activity_lock_init(&heap->vam_activities_lock);
1101 	_voucher_activity_heap = heap;
1102 
1103 	_voucher_atm_t vatm = _voucher_atm_create(0, 0);
1104 	dispatch_assert(vatm->vatm_kvoucher);
1105 	heap->vam_default_activity_atm = vatm;
1106 	_voucher_activity_buffer_header_t buffer = vam_buffer(3); // reserved index
1107 	// consumes vatm reference:
1108 	_voucher_activity_t va = _voucher_activity_create_with_atm(vatm,
1109 			VATM_ACTID(vatm, _voucher_default_activity_subid), 0, 0, buffer);
1110 	dispatch_assert(va);
1111 	va->va_buffer_limit = _voucher_default_activity_buffer_limit();
1112 	_voucher_activity_default = va;
1113 	heap->vam_base_atm = _voucher_atm_create(0, 0);
1114 	heap->vam_nested_atm_id = _voucher_atm_nested_atm_id_make();
1115 }
1116 
1117 static void
_voucher_activity_atfork_child(void)1118 _voucher_activity_atfork_child(void)
1119 {
1120 	_voucher_activity_heap_pred = 0;
1121 	_voucher_activity_heap = NULL; // activity heap is VM_INHERIT_NONE
1122 	_voucher_activity_default = NULL;
1123 }
1124 
1125 void*
voucher_activity_get_metadata_buffer(size_t * length)1126 voucher_activity_get_metadata_buffer(size_t *length)
1127 {
1128 	dispatch_once_f(&_voucher_activity_heap_pred, NULL,
1129 			_voucher_activity_heap_init);
1130 	if (_voucher_activity_disabled()) {
1131 		*length = 0;
1132 		return NULL;
1133 	}
1134 	*length = sizeof(_voucher_activity_heap->vam_client_metadata);
1135 	return _voucher_activity_heap->vam_client_metadata;
1136 }
1137 
1138 DISPATCH_ALWAYS_INLINE
1139 static inline _voucher_activity_buffer_header_t
_voucher_activity_heap_buffer_alloc(void)1140 _voucher_activity_heap_buffer_alloc(void)
1141 {
1142 	_voucher_activity_buffer_header_t buffer = NULL;
1143 	size_t index;
1144 	index = _voucher_activity_bitmap_set_first_unset_bit_upto(
1145 			vam_buffer_bitmap(), _voucher_heap_buffer_limit() - 1);
1146 	if (index < NO_BITS_WERE_UNSET) {
1147 		buffer = vam_buffer(index);
1148 	}
1149 #if DISPATCH_DEBUG && DISPATCH_VOUCHER_ACTIVITY_DEBUG
1150 	_dispatch_debug("activity heap alloc %zd (%p)", index, buffer);
1151 #endif
1152 	return buffer;
1153 }
1154 
1155 DISPATCH_ALWAYS_INLINE
1156 static inline void
_voucher_activity_heap_buffer_free(_voucher_activity_buffer_header_t buffer)1157 _voucher_activity_heap_buffer_free(_voucher_activity_buffer_header_t buffer)
1158 {
1159 	buffer->vabh_flags = _voucher_activity_trace_flag_buffer_empty;
1160 	size_t index = (size_t)((char*)buffer - (char*)_voucher_activity_heap) /
1161 			_voucher_activity_buffer_size;
1162 #if DISPATCH_DEBUG && DISPATCH_VOUCHER_ACTIVITY_DEBUG
1163 	_dispatch_debug("activity heap free %zd (%p)", index, buffer);
1164 #endif
1165 	_voucher_activity_bitmap_clear_bit(vam_buffer_bitmap(), index);
1166 }
1167 
1168 #define _voucher_activity_heap_can_madvise() \
1169 		(PAGE_SIZE == _voucher_activity_buffer_size) // <rdar://17445544>
1170 
1171 DISPATCH_ALWAYS_INLINE
1172 static inline void
_voucher_activity_heap_madvise(size_t bitmap_num,unsigned int start,unsigned int len)1173 _voucher_activity_heap_madvise(size_t bitmap_num, unsigned int start,
1174 		unsigned int len)
1175 {
1176 	size_t base = bitmap_num * _voucher_activity_bits_per_bitmap_base_t;
1177 #if DISPATCH_DEBUG
1178 #if DISPATCH_VOUCHER_ACTIVITY_DEBUG
1179 	_dispatch_debug("activity heap madvise %zd (%p) -> %zd (%p)", base + start,
1180 			vam_buffer(base + start), base + start + len,
1181 			vam_buffer(base + start + len));
1182 #endif
1183 	dispatch_assert(!(len * _voucher_activity_buffer_size % vm_page_size));
1184 	const uint64_t pattern = 0xFACEFACEFACEFACE;
1185 	_voucher_activity_buffer_header_t buffer = vam_buffer(base + start);
1186 	for (unsigned int i = 0; i < len; i++, buffer++) {
1187 		memset_pattern8((char*)buffer + sizeof(buffer->vabh_flags), &pattern,
1188 				_voucher_activity_buffer_size - sizeof(buffer->vabh_flags));
1189 	}
1190 #endif
1191 	(void)dispatch_assume_zero(madvise(vam_buffer(base + start),
1192 			len * _voucher_activity_buffer_size, MADV_FREE));
1193 }
1194 
1195 DISPATCH_ALWAYS_INLINE
1196 static inline void
_voucher_activity_heap_madvise_contiguous(size_t bitmap_num,_voucher_activity_bitmap_base_t bits)1197 _voucher_activity_heap_madvise_contiguous(size_t bitmap_num,
1198 		_voucher_activity_bitmap_base_t bits)
1199 {
1200 	// TODO: x86 has fast ctz; arm has fast clz; haswell has fast ctz
1201 	dispatch_assert(_voucher_activity_heap_can_madvise());
1202 	if (bits == 0) {
1203 		return;
1204 	} else if (~bits == 0) {
1205 		_voucher_activity_heap_madvise(bitmap_num, 0,
1206 				_voucher_activity_bits_per_bitmap_base_t);
1207 	} else while (bits != 0) {
1208 		unsigned int start = (typeof(start))__builtin_ctzl(bits), len;
1209 		typeof(bits) inverse = ~bits >> start;
1210 		if (inverse) {
1211 			len = (typeof(len))__builtin_ctzl(inverse);
1212 		} else {
1213 			len = _voucher_activity_bits_per_bitmap_base_t - start;
1214 		}
1215 		typeof(bits) mask = ((((typeof(bits))1) << len) - 1) << start;
1216 		bits &= ~mask;
1217 		_voucher_activity_heap_madvise(bitmap_num, start, len);
1218 	}
1219 }
1220 
1221 void
_voucher_activity_heap_pressure_warn(void)1222 _voucher_activity_heap_pressure_warn(void)
1223 {
1224 	if (!_voucher_activity_heap_can_madvise() || !_voucher_activity_heap) {
1225 		return;
1226 	}
1227 	volatile _voucher_activity_bitmap_base_t *bitmap, *pressure_locked_bitmap;
1228 	bitmap = vam_buffer_bitmap();
1229 	pressure_locked_bitmap = vam_pressure_locked_bitmap();
1230 
1231 	// number of bitmaps needed to map the current buffer limit =
1232 	// ceil(buffer limit / bits per bitmap)
1233 	size_t nbuffers = _voucher_heap_buffer_limit();
1234 	size_t nbitmaps_quot = nbuffers / _voucher_activity_bits_per_bitmap_base_t;
1235 	size_t nbitmaps_rem = nbuffers % _voucher_activity_bits_per_bitmap_base_t;
1236 	size_t nbitmaps = nbitmaps_quot + ((nbitmaps_rem == 0) ? 0 : 1);
1237 
1238 	for (size_t i = 0; i < nbitmaps; i++) {
1239 		_voucher_activity_bitmap_base_t got_bits;
1240 		got_bits = dispatch_atomic_or_orig(&bitmap[i], ~((typeof(bitmap[i]))0),
1241 				relaxed);
1242 		got_bits = ~got_bits; // Now 1 means 'acquired this one, madvise it'
1243 		_voucher_activity_heap_madvise_contiguous(i, got_bits);
1244 		pressure_locked_bitmap[i] |= got_bits;
1245 	}
1246 }
1247 
1248 void
_voucher_activity_heap_pressure_normal(void)1249 _voucher_activity_heap_pressure_normal(void)
1250 {
1251 	if (!_voucher_activity_heap_can_madvise() || !_voucher_activity_heap) {
1252 		return;
1253 	}
1254 	volatile _voucher_activity_bitmap_base_t *bitmap, *pressure_locked_bitmap;
1255 	bitmap = vam_buffer_bitmap();
1256 	pressure_locked_bitmap = vam_pressure_locked_bitmap();
1257 	for (size_t i = 0; i < _voucher_activity_bitmaps_per_heap; i++) {
1258 		_voucher_activity_bitmap_base_t free_bits = pressure_locked_bitmap[i];
1259 		pressure_locked_bitmap[i] = 0;
1260 		if (free_bits != 0) {
1261 			(void)dispatch_atomic_and(&bitmap[i], ~free_bits, release);
1262 		}
1263 	}
1264 }
1265 
1266 DISPATCH_ALWAYS_INLINE
1267 static inline void
_voucher_activity_buffer_init(_voucher_activity_t act,_voucher_activity_buffer_header_t buffer,bool reuse)1268 _voucher_activity_buffer_init(_voucher_activity_t act,
1269 		_voucher_activity_buffer_header_t buffer, bool reuse)
1270 {
1271 	if (!reuse) {
1272 		buffer->vabh_flags = _voucher_activity_trace_flag_buffer_header;
1273 		buffer->vabh_activity_id = act->va_id;
1274 	}
1275 	buffer->vabh_timestamp = _voucher_activity_timestamp();
1276 	buffer->vabh_next_tracepoint_idx = 1;
1277 	buffer->vabh_sequence_no = dispatch_atomic_inc2o(act, va_max_sequence_no,
1278 			relaxed);
1279 }
1280 
1281 static _voucher_activity_buffer_header_t
_voucher_activity_buffer_alloc_slow(_voucher_activity_t act,_voucher_activity_buffer_header_t current)1282 _voucher_activity_buffer_alloc_slow(_voucher_activity_t act,
1283 		_voucher_activity_buffer_header_t current)
1284 {
1285 	_voucher_activity_buffer_header_t buffer;
1286 	_voucher_activity_lock_lock(va_buffers_lock(act)); // TODO: revisit locking
1287 	buffer = act->va_current_buffer;
1288 	if (buffer != current) goto out;
1289 	buffer = TAILQ_FIRST(&act->va_buffers);
1290 	if (buffer) {
1291 		_voucher_activity_buffer_init(act, buffer, true);
1292 		if (buffer != TAILQ_LAST(&act->va_buffers,
1293 				_voucher_activity_buffer_list_s)) {
1294 			TAILQ_REMOVE(&act->va_buffers, buffer, vabh_list);
1295 			TAILQ_INSERT_TAIL(&act->va_buffers, buffer, vabh_list);
1296 		}
1297 	}
1298 	if (!dispatch_atomic_cmpxchgv2o(act, va_current_buffer, current, buffer,
1299 			&current, release)) {
1300 		if (buffer) {
1301 			TAILQ_REMOVE(&act->va_buffers, buffer, vabh_list);
1302 			_voucher_activity_heap_buffer_free(buffer);
1303 		}
1304 		buffer = current;
1305 	}
1306 out:
1307 	_voucher_activity_lock_unlock(va_buffers_lock(act));
1308 	_dispatch_voucher_activity_debug("buffer reuse %p", act, buffer);
1309 	return buffer;
1310 }
1311 
1312 static _voucher_activity_buffer_header_t
_voucher_activity_buffer_alloc(_voucher_activity_t act,_voucher_activity_buffer_header_t current)1313 _voucher_activity_buffer_alloc(_voucher_activity_t act,
1314 		_voucher_activity_buffer_header_t current)
1315 {
1316 	_voucher_activity_buffer_header_t buffer = NULL;
1317 	if (act->va_max_sequence_no < act->va_buffer_limit) {
1318 		buffer = _voucher_activity_heap_buffer_alloc();
1319 	}
1320 	if (!buffer) return _voucher_activity_buffer_alloc_slow(act, current);
1321 	_voucher_activity_buffer_init(act, buffer, false);
1322 	if (dispatch_atomic_cmpxchgv2o(act, va_current_buffer, current, buffer,
1323 			&current, release)) {
1324 		_voucher_activity_lock_lock(va_buffers_lock(act));
1325 		TAILQ_INSERT_TAIL(&act->va_buffers, buffer, vabh_list);
1326 		_voucher_activity_lock_unlock(va_buffers_lock(act));
1327 	} else {
1328 		_voucher_activity_heap_buffer_free(buffer);
1329 		buffer = current;
1330 	}
1331 	_dispatch_voucher_activity_debug("buffer alloc %p", act, buffer);
1332 	return buffer;
1333 }
1334 
1335 #pragma mark -
1336 #pragma mark _voucher_activity_t
1337 
1338 #define _voucher_activity_ordered_insert(_act, head, field) do { \
1339 		typeof(_act) _vai; \
1340 		TAILQ_FOREACH(_vai, (head), field) { \
1341 			if (_act->va_id < _vai->va_id) break; \
1342 		} \
1343 		if (_vai) { \
1344 			TAILQ_INSERT_BEFORE(_vai, _act, field); \
1345 		} else { \
1346 			TAILQ_INSERT_TAIL((head), _act, field); \
1347 		} } while (0);
1348 
1349 static void _voucher_activity_dispose(_voucher_activity_t act);
1350 static _voucher_activity_t _voucher_atm_activity_mark_used(
1351 		_voucher_activity_t act);
1352 static void _voucher_atm_activity_mark_unused(_voucher_activity_t act);
1353 static _voucher_atm_t _voucher_atm_copy(atm_aid_t atm_id);
1354 static inline void _voucher_atm_release(_voucher_atm_t vatm);
1355 static void _voucher_atm_activity_insert(_voucher_atm_t vatm,
1356 		_voucher_activity_t act);
1357 static void _voucher_atm_activity_remove(_voucher_activity_t act);
1358 static atm_aid_t _voucher_mach_voucher_get_atm_id(mach_voucher_t kv);
1359 
1360 DISPATCH_ALWAYS_INLINE
1361 static inline bool
_voucher_activity_copy(_voucher_activity_t act)1362 _voucher_activity_copy(_voucher_activity_t act)
1363 {
1364 	int use_cnt = dispatch_atomic_inc2o(act, va_use_count, relaxed);
1365 	_dispatch_voucher_activity_debug("retain  -> %d", act, use_cnt + 1);
1366 	if (slowpath(use_cnt < 0)) {
1367 		_dispatch_voucher_activity_debug("overrelease", act);
1368 		DISPATCH_CRASH("Activity overrelease");
1369 	}
1370 	return (use_cnt == 0);
1371 }
1372 
1373 DISPATCH_ALWAYS_INLINE
1374 static inline _voucher_activity_t
_voucher_activity_retain(_voucher_activity_t act)1375 _voucher_activity_retain(_voucher_activity_t act)
1376 {
1377 	if (_voucher_activity_copy(act)) {
1378 		_dispatch_voucher_activity_debug("invalid resurrection", act);
1379 		DISPATCH_CRASH("Invalid activity resurrection");
1380 	}
1381 	return act;
1382 }
1383 
1384 DISPATCH_ALWAYS_INLINE
1385 static inline void
_voucher_activity_release(_voucher_activity_t act)1386 _voucher_activity_release(_voucher_activity_t act)
1387 {
1388 	int use_cnt = dispatch_atomic_dec2o(act, va_use_count, relaxed);
1389 	_dispatch_voucher_activity_debug("release -> %d", act, use_cnt + 1);
1390 	if (fastpath(use_cnt >= 0)) {
1391 		return;
1392 	}
1393 	if (slowpath(use_cnt < -1)) {
1394 		_dispatch_voucher_activity_debug("overrelease", act);
1395 		DISPATCH_CRASH("Activity overrelease");
1396 	}
1397 	return _voucher_atm_activity_mark_unused(act);
1398 }
1399 
1400 DISPATCH_ALWAYS_INLINE
1401 static inline _voucher_activity_t
_voucher_activity_atm_retain(_voucher_activity_t act)1402 _voucher_activity_atm_retain(_voucher_activity_t act)
1403 {
1404 	int refcnt = dispatch_atomic_inc2o(act, va_refcnt, relaxed);
1405 	_dispatch_voucher_activity_debug("atm retain  -> %d", act, refcnt + 1);
1406 	if (slowpath(refcnt <= 0)) {
1407 		_dispatch_voucher_activity_debug("atm resurrection", act);
1408 		DISPATCH_CRASH("Activity ATM resurrection");
1409 	}
1410 	return act;
1411 }
1412 
1413 DISPATCH_ALWAYS_INLINE
1414 static inline void
_voucher_activity_atm_release(_voucher_activity_t act)1415 _voucher_activity_atm_release(_voucher_activity_t act)
1416 {
1417 	int refcnt = dispatch_atomic_dec2o(act, va_refcnt, relaxed);
1418 	_dispatch_voucher_activity_debug("atm release -> %d", act, refcnt + 1);
1419 	if (fastpath(refcnt >= 0)) {
1420 		return;
1421 	}
1422 	if (slowpath(refcnt < -1)) {
1423 		_dispatch_voucher_activity_debug("atm overrelease", act);
1424 		DISPATCH_CRASH("Activity ATM overrelease");
1425 	}
1426 	return _voucher_activity_dispose(act);
1427 }
1428 
1429 static inline _voucher_activity_t
_voucher_activity_get(voucher_t v)1430 _voucher_activity_get(voucher_t v)
1431 {
1432 	_voucher_activity_t act;
1433 	act = v && v->v_activity ? v->v_activity : _voucher_activity_default;
1434 	return act;
1435 }
1436 
1437 static _voucher_activity_t
_voucher_activity_find(voucher_activity_id_t va_id,uint32_t hash)1438 _voucher_activity_find(voucher_activity_id_t va_id, uint32_t hash)
1439 {
1440 	// assumes vam_activities_lock held
1441 	_voucher_activity_t act;
1442 	TAILQ_FOREACH(act, vam_activities(hash), va_list){
1443 		if (act->va_id == va_id) break;
1444 	}
1445 	return act;
1446 }
1447 
1448 static _voucher_activity_t
_voucher_activity_copy_from_id(voucher_activity_id_t va_id)1449 _voucher_activity_copy_from_id(voucher_activity_id_t va_id)
1450 {
1451 	bool resurrect = false;
1452 	uint32_t hash = VACTID_HASH(va_id);
1453 	_voucher_activity_lock_lock(vam_activities_lock());
1454 	_voucher_activity_t act = _voucher_activity_find(va_id, hash);
1455 	if (act) {
1456 		resurrect = _voucher_activity_copy(act);
1457 		_dispatch_voucher_activity_debug("copy from id 0x%llx", act, va_id);
1458 	}
1459 	_voucher_activity_lock_unlock(vam_activities_lock());
1460 	if (resurrect) return _voucher_atm_activity_mark_used(act);
1461 	return act;
1462 }
1463 
1464 static _voucher_activity_t
_voucher_activity_try_insert(_voucher_activity_t act_new)1465 _voucher_activity_try_insert(_voucher_activity_t act_new)
1466 {
1467 	bool resurrect = false;
1468 	voucher_activity_id_t va_id = act_new->va_id;
1469 	uint32_t hash = VACTID_HASH(va_id);
1470 	_voucher_activity_lock_lock(vam_activities_lock());
1471 	_voucher_activity_t act = _voucher_activity_find(va_id, hash);
1472 	if (act) {
1473 		resurrect = _voucher_activity_copy(act);
1474 		_dispatch_voucher_activity_debug("try insert: failed (%p)", act,act_new);
1475 	} else {
1476 		if (slowpath(_TAILQ_IS_ENQUEUED(act_new, va_list))) {
1477 			_dispatch_voucher_activity_debug("corruption", act_new);
1478 			DISPATCH_CRASH("Activity corruption");
1479 		}
1480 		TAILQ_INSERT_TAIL(vam_activities(hash), act_new, va_list);
1481 		_dispatch_voucher_activity_debug("try insert: succeeded", act_new);
1482 	}
1483 	_voucher_activity_lock_unlock(vam_activities_lock());
1484 	if (resurrect) return _voucher_atm_activity_mark_used(act);
1485 	return act;
1486 }
1487 
1488 static bool
_voucher_activity_try_remove(_voucher_activity_t act)1489 _voucher_activity_try_remove(_voucher_activity_t act)
1490 {
1491 	bool r;
1492 	voucher_activity_id_t va_id = act->va_id;
1493 	uint32_t hash = VACTID_HASH(va_id);
1494 	_voucher_activity_lock_lock(vam_activities_lock());
1495 	if (slowpath(!va_id)) {
1496 		_dispatch_voucher_activity_debug("corruption", act);
1497 		DISPATCH_CRASH("Activity corruption");
1498 	}
1499 	if ((r = (dispatch_atomic_load2o(act, va_use_count, seq_cst) < 0 &&
1500 			_TAILQ_IS_ENQUEUED(act, va_list)))) {
1501 		TAILQ_REMOVE(vam_activities(hash), act, va_list);
1502 		_TAILQ_MARK_NOT_ENQUEUED(act, va_list);
1503 		act->va_list.tqe_next = (void*)~0ull;
1504 	}
1505 	_dispatch_voucher_activity_debug("try remove: %s", act, r ? "succeeded" :
1506 			"failed");
1507 	_voucher_activity_lock_unlock(vam_activities_lock());
1508 	return r;
1509 }
1510 
1511 static _voucher_activity_t
_voucher_activity_create_with_atm(_voucher_atm_t vatm,voucher_activity_id_t va_id,voucher_activity_trace_id_t trace_id,uint64_t location,_voucher_activity_buffer_header_t buffer)1512 _voucher_activity_create_with_atm(_voucher_atm_t vatm,
1513 		voucher_activity_id_t va_id, voucher_activity_trace_id_t trace_id,
1514 		uint64_t location, _voucher_activity_buffer_header_t buffer)
1515 {
1516 	if (!buffer) buffer = _voucher_activity_heap_buffer_alloc();
1517 	if (!buffer) {
1518 		_dispatch_voucher_atm_debug("no buffer", vatm);
1519 		_voucher_atm_release(vatm); // consume vatm reference
1520 		return NULL;
1521 	}
1522 	if (!trace_id) trace_id = _voucher_activity_trace_id_release;
1523 	_voucher_activity_tracepoint_t vat = (_voucher_activity_tracepoint_t)buffer;
1524 	_voucher_activity_tracepoint_init_with_id(vat, trace_id, ~1ull);
1525 	_voucher_activity_t act = (_voucher_activity_t)buffer;
1526 	act->va_flags = _voucher_activity_trace_flag_buffer_header |
1527 			_voucher_activity_trace_flag_activity |
1528 			_voucher_activity_trace_flag_start |
1529 			_voucher_activity_trace_flag_wide_first;
1530 	act->vabh_next_tracepoint_idx = sizeof(*act)/sizeof(*vat);
1531 	act->va_max_sequence_no = 0;
1532 	act->va_id = va_id ? va_id : VATM_ACTID(vatm, 0);
1533 	act->va_use_count = 0;
1534 	act->va_buffer_limit = _voucher_activity_buffer_limit();
1535 	TAILQ_INIT(&act->va_buffers);
1536 	act->va_flags2 = _voucher_activity_trace_flag_activity |
1537 			_voucher_activity_trace_flag_wide_second;
1538 #if __LP64__
1539 	act->va_flags3 = act->va_flags2;
1540 #endif
1541 	act->va_refcnt = 0;
1542 	act->va_location = location;
1543 	act->va_current_buffer = buffer;
1544 	act->va_atm = vatm; // transfer vatm reference
1545 	_voucher_activity_lock_init(va_buffers_lock(act));
1546 	_TAILQ_MARK_NOT_ENQUEUED(act, va_list);
1547 	_TAILQ_MARK_NOT_ENQUEUED(act, va_atm_list);
1548 	_TAILQ_MARK_NOT_ENQUEUED(act, va_atm_used_list);
1549 	_voucher_activity_t actx = _voucher_activity_try_insert(act);
1550 	if (actx) {
1551 		_voucher_activity_dispose(act);
1552 		act = actx;
1553 	} else {
1554 		_voucher_atm_activity_insert(vatm, act);
1555 	}
1556 	_dispatch_voucher_activity_debug("create", act);
1557 	return act;
1558 }
1559 
1560 static void
_voucher_activity_dispose(_voucher_activity_t act)1561 _voucher_activity_dispose(_voucher_activity_t act)
1562 {
1563 	_dispatch_voucher_activity_debug("dispose", act);
1564 	_voucher_atm_release(act->va_atm);
1565 	if (slowpath(_TAILQ_IS_ENQUEUED(act, va_list))) {
1566 		_dispatch_voucher_activity_debug("corruption", act);
1567 		DISPATCH_CRASH("Activity corruption");
1568 	}
1569 	act->va_list.tqe_next = DISPATCH_OBJECT_LISTLESS;
1570 	dispatch_assert(!_TAILQ_IS_ENQUEUED(act, va_atm_list));
1571 	dispatch_assert(!_TAILQ_IS_ENQUEUED(act, va_atm_used_list));
1572 	_voucher_activity_buffer_header_t buffer, tmp;
1573 	TAILQ_FOREACH_SAFE(buffer, &act->va_buffers, vabh_list, tmp) {
1574 		_dispatch_voucher_activity_debug("buffer free %p", act, buffer);
1575 		TAILQ_REMOVE(&act->va_buffers, buffer, vabh_list);
1576 		_voucher_activity_heap_buffer_free(buffer);
1577 	}
1578 	buffer = (_voucher_activity_buffer_header_t)act;
1579 	_voucher_activity_heap_buffer_free(buffer);
1580 }
1581 
1582 static void
_voucher_activity_collect(_voucher_activity_t act)1583 _voucher_activity_collect(_voucher_activity_t act)
1584 {
1585 	_dispatch_voucher_activity_debug("collect", act);
1586 	if (_voucher_activity_try_remove(act)) {
1587 		_voucher_atm_activity_remove(act);
1588 	}
1589 }
1590 
1591 static _voucher_activity_t
_voucher_activity_copy_from_mach_voucher(mach_voucher_t kv,voucher_activity_id_t va_id)1592 _voucher_activity_copy_from_mach_voucher(mach_voucher_t kv,
1593 		voucher_activity_id_t va_id)
1594 {
1595 	dispatch_once_f(&_voucher_activity_heap_pred, NULL,
1596 			_voucher_activity_heap_init);
1597 	if (_voucher_activity_disabled()) return NULL;
1598 	_voucher_activity_t act = NULL;
1599 	if (dispatch_assume(va_id)) {
1600 		if ((act = _voucher_activity_copy_from_id(va_id))) return act;
1601 	}
1602 	atm_aid_t atm_id = _voucher_mach_voucher_get_atm_id(kv);
1603 	if (!dispatch_assume(atm_id)) return NULL;
1604 	_voucher_activity_buffer_header_t buffer;
1605 	buffer = _voucher_activity_heap_buffer_alloc();
1606 	if (!buffer) return NULL;
1607 	_dispatch_kvoucher_debug("atm copy/create from <%lld>", kv, atm_id);
1608 	_voucher_atm_t vatm = _voucher_atm_copy(atm_id);
1609 	if (!vatm) vatm = _voucher_atm_create(kv, atm_id);
1610 	if (!vatm) {
1611 		_voucher_activity_heap_buffer_free(buffer);
1612 		return NULL;
1613 	}
1614 	if (VACTID_BASEID(va_id) != VATMID2ACTID(atm_id)) va_id = 0;
1615 	// consumes vatm reference:
1616 	act = _voucher_activity_create_with_atm(vatm, va_id, 0, 0, buffer);
1617 	_dispatch_voucher_activity_debug("copy from kvoucher[0x%08x]", act, kv);
1618 	return act;
1619 }
1620 
1621 #pragma mark -
1622 #pragma mark _voucher_atm_mailbox
1623 
1624 DISPATCH_ALWAYS_INLINE
1625 static inline atm_mailbox_offset_t
_voucher_atm_mailbox_alloc(void)1626 _voucher_atm_mailbox_alloc(void)
1627 {
1628 	atm_mailbox_offset_t mailbox_offset = MAILBOX_OFFSET_UNSET;
1629 	size_t index;
1630 	index = _voucher_activity_bitmap_set_first_unset_bit(vam_atm_mbox_bitmap());
1631 	if (index < NO_BITS_WERE_UNSET) {
1632 		mailbox_offset = index * sizeof(atm_mailbox_offset_t);
1633 #if DISPATCH_DEBUG && DISPATCH_VOUCHER_ACTIVITY_DEBUG
1634 		_dispatch_debug("mailbox alloc %zd (%lld)", index, mailbox_offset);
1635 #endif
1636 	}
1637 	return mailbox_offset;
1638 }
1639 
1640 DISPATCH_ALWAYS_INLINE
1641 static inline void
_voucher_atm_mailbox_free(atm_mailbox_offset_t mailbox_offset)1642 _voucher_atm_mailbox_free(atm_mailbox_offset_t mailbox_offset)
1643 {
1644 	if (mailbox_offset == MAILBOX_OFFSET_UNSET) return;
1645 	size_t index = (size_t)mailbox_offset / sizeof(atm_mailbox_offset_t);
1646 #if DISPATCH_DEBUG && DISPATCH_VOUCHER_ACTIVITY_DEBUG
1647 	_dispatch_debug("mailbox free %zd (%lld)", index, mailbox_offset);
1648 #endif
1649 	_voucher_activity_bitmap_clear_bit(vam_atm_mbox_bitmap(), index);
1650 }
1651 
1652 DISPATCH_ALWAYS_INLINE
1653 static inline bool
_voucher_atm_mailbox_set(atm_mailbox_offset_t mailbox_offset,atm_subaid32_t subaid,bool max_present)1654 _voucher_atm_mailbox_set(atm_mailbox_offset_t mailbox_offset,
1655 		atm_subaid32_t subaid, bool max_present)
1656 {
1657 	if (mailbox_offset == MAILBOX_OFFSET_UNSET) return false;
1658 	char *mailbox_base = (char*)_voucher_activity_heap->vam_kernel_metadata;
1659 	atm_subaid32_t *mailbox = (atm_subaid32_t*)(mailbox_base + mailbox_offset);
1660 	if (max_present) mailbox++; // second atm_subaid32_t in atm_mailbox_offset_t
1661 	if (*mailbox == subaid) return false;
1662 	*mailbox = subaid;
1663 	return true;
1664 }
1665 
1666 #pragma mark -
1667 #pragma mark _voucher_atm_t
1668 
1669 static bool _voucher_atm_try_remove(_voucher_atm_t vatm);
1670 static void _voucher_atm_dispose(_voucher_atm_t vatm, bool unregister);
1671 static inline void _voucher_atm_collect_if_needed(bool updated);
1672 
1673 DISPATCH_ALWAYS_INLINE
1674 static inline _voucher_atm_t
_voucher_atm_retain(_voucher_atm_t vatm)1675 _voucher_atm_retain(_voucher_atm_t vatm)
1676 {
1677 	// assumes vam_atms_lock or vam_base_atm_lock held
1678 	int refcnt = dispatch_atomic_inc2o(vatm, vatm_refcnt, relaxed);
1679 	_dispatch_voucher_atm_debug("retain  -> %d", vatm, refcnt + 1);
1680 	if (slowpath(refcnt < 0)) {
1681 		_dispatch_voucher_atm_debug("overrelease", vatm);
1682 		DISPATCH_CRASH("ATM overrelease");
1683 	}
1684 	return vatm;
1685 }
1686 
1687 DISPATCH_ALWAYS_INLINE
1688 static inline void
_voucher_atm_release(_voucher_atm_t vatm)1689 _voucher_atm_release(_voucher_atm_t vatm)
1690 {
1691 	int refcnt = dispatch_atomic_dec2o(vatm, vatm_refcnt, relaxed);
1692 	_dispatch_voucher_atm_debug("release -> %d", vatm, refcnt + 1);
1693 	if (fastpath(refcnt >= 0)) {
1694 		return;
1695 	}
1696 	if (slowpath(refcnt < -1)) {
1697 		_dispatch_voucher_atm_debug("overrelease", vatm);
1698 		DISPATCH_CRASH("ATM overrelease");
1699 	}
1700 	if (_voucher_atm_try_remove(vatm)) {
1701 		_voucher_atm_dispose(vatm, true);
1702 	}
1703 }
1704 
1705 static _voucher_atm_t
_voucher_atm_find(atm_aid_t atm_id,uint32_t hash)1706 _voucher_atm_find(atm_aid_t atm_id, uint32_t hash)
1707 {
1708 	// assumes vam_atms_lock held
1709 	_voucher_atm_t vatm;
1710 	TAILQ_FOREACH(vatm, vam_atms(hash), vatm_list){
1711 		if (vatm->vatm_id == atm_id) break;
1712 	}
1713 	return vatm;
1714 }
1715 
1716 static _voucher_atm_t
_voucher_atm_copy(atm_aid_t atm_id)1717 _voucher_atm_copy(atm_aid_t atm_id)
1718 {
1719 	uint32_t hash = VATMID_HASH(atm_id);
1720 	_voucher_activity_lock_lock(vam_atms_lock());
1721 	_voucher_atm_t vatm = _voucher_atm_find(atm_id, hash);
1722 	if (vatm) {
1723 		_voucher_atm_retain(vatm);
1724 		_dispatch_voucher_atm_debug("copy", vatm);
1725 	}
1726 	_voucher_activity_lock_unlock(vam_atms_lock());
1727 	return vatm;
1728 }
1729 
1730 static _voucher_atm_t
_voucher_atm_try_insert(_voucher_atm_t vatm_new)1731 _voucher_atm_try_insert(_voucher_atm_t vatm_new)
1732 {
1733 	atm_aid_t atm_id = vatm_new->vatm_id;
1734 	uint32_t hash = VATMID_HASH(atm_id);
1735 	_voucher_activity_lock_lock(vam_atms_lock());
1736 	_voucher_atm_t vatm = _voucher_atm_find(atm_id, hash);
1737 	if (vatm) {
1738 		_voucher_atm_retain(vatm);
1739 		_dispatch_voucher_atm_debug("try insert: failed (%p)", vatm, vatm_new);
1740 	} else {
1741 		if (slowpath(_TAILQ_IS_ENQUEUED(vatm_new, vatm_list))) {
1742 			_dispatch_voucher_atm_debug("corruption", vatm_new);
1743 			DISPATCH_CRASH("ATM corruption");
1744 		}
1745 		TAILQ_INSERT_TAIL(vam_atms(hash), vatm_new, vatm_list);
1746 		_dispatch_voucher_atm_debug("try insert: succeeded", vatm_new);
1747 	}
1748 	_voucher_activity_lock_unlock(vam_atms_lock());
1749 	return vatm;
1750 }
1751 
1752 static bool
_voucher_atm_try_remove(_voucher_atm_t vatm)1753 _voucher_atm_try_remove(_voucher_atm_t vatm)
1754 {
1755 	bool r;
1756 	atm_aid_t atm_id = vatm->vatm_id;
1757 	uint32_t hash = VATMID_HASH(atm_id);
1758 	_voucher_activity_lock_lock(vam_atms_lock());
1759 	if (slowpath(!atm_id)) {
1760 		_dispatch_voucher_atm_debug("corruption", vatm);
1761 		DISPATCH_CRASH("ATM corruption");
1762 	}
1763 	if ((r = (dispatch_atomic_load2o(vatm, vatm_refcnt, seq_cst) < 0 &&
1764 			_TAILQ_IS_ENQUEUED(vatm, vatm_list)))) {
1765 		TAILQ_REMOVE(vam_atms(hash), vatm, vatm_list);
1766 		_TAILQ_MARK_NOT_ENQUEUED(vatm, vatm_list);
1767 		vatm->vatm_list.tqe_next = (void*)~0ull;
1768 	}
1769 	_dispatch_voucher_atm_debug("try remove: %s", vatm, r ? "succeeded" :
1770 			"failed");
1771 	_voucher_activity_lock_unlock(vam_atms_lock());
1772 	return r;
1773 }
1774 
1775 static bool
_voucher_atm_update_mailbox(_voucher_atm_t vatm)1776 _voucher_atm_update_mailbox(_voucher_atm_t vatm)
1777 {
1778 	// Update kernel mailbox with largest allocated subaid for this atm_id
1779 	// assumes atm_activities_lock held
1780 	_voucher_activity_t act = TAILQ_LAST(vatm_activities(vatm),
1781 			_voucher_atm_activities_s);
1782 	atm_subaid32_t subaid = act ? VACTID_SUBID(act->va_id) : 0;
1783 	bool r = _voucher_atm_mailbox_set(vatm->vatm_mailbox_offset, subaid, true);
1784 	if (r) {
1785 		_dispatch_voucher_atm_debug("update max-present subaid 0x%x", vatm,
1786 				subaid);
1787 	}
1788 	return r;
1789 }
1790 
1791 static bool
_voucher_atm_update_used_mailbox(_voucher_atm_t vatm)1792 _voucher_atm_update_used_mailbox(_voucher_atm_t vatm)
1793 {
1794 	// Update kernel mailbox with smallest in-use subaid for this atm_id
1795 	// assumes atm_activities_lock held
1796 	_voucher_activity_t act = TAILQ_FIRST(vatm_used_activities(vatm));
1797 	atm_subaid32_t subaid = act ? VACTID_SUBID(act->va_id) : ATM_SUBAID32_MAX;
1798 	bool r = _voucher_atm_mailbox_set(vatm->vatm_mailbox_offset, subaid, false);
1799 	if (r) {
1800 		_dispatch_voucher_atm_debug("update min-used subaid 0x%x", vatm,
1801 				subaid);
1802 	}
1803 	return r;
1804 }
1805 
1806 static void
_voucher_atm_activity_insert(_voucher_atm_t vatm,_voucher_activity_t act)1807 _voucher_atm_activity_insert(_voucher_atm_t vatm, _voucher_activity_t act)
1808 {
1809 	_voucher_activity_lock_lock(vatm_activities_lock(vatm));
1810 	if (!_TAILQ_IS_ENQUEUED(act, va_atm_list)) {
1811 		_voucher_activity_ordered_insert(act, vatm_activities(vatm),
1812 				va_atm_list);
1813 		_voucher_atm_update_mailbox(vatm);
1814 	}
1815 	if (!_TAILQ_IS_ENQUEUED(act, va_atm_used_list)) {
1816 		_voucher_activity_ordered_insert(act, vatm_used_activities(vatm),
1817 				va_atm_used_list);
1818 		_voucher_atm_update_used_mailbox(vatm);
1819 	}
1820 	_dispatch_voucher_activity_debug("atm insert", act);
1821 	_voucher_activity_lock_unlock(vatm_activities_lock(vatm));
1822 }
1823 
1824 static void
_voucher_atm_activity_remove(_voucher_activity_t act)1825 _voucher_atm_activity_remove(_voucher_activity_t act)
1826 {
1827 	_voucher_atm_t vatm = act->va_atm;
1828 	_voucher_activity_lock_lock(vatm_activities_lock(vatm));
1829 	_dispatch_voucher_activity_debug("atm remove", act);
1830 	if (_TAILQ_IS_ENQUEUED(act, va_atm_used_list)) {
1831 		TAILQ_REMOVE(vatm_activities(vatm), act, va_atm_used_list);
1832 		_TAILQ_MARK_NOT_ENQUEUED(act, va_atm_used_list);
1833 		_voucher_atm_update_used_mailbox(vatm);
1834 	}
1835 	if (_TAILQ_IS_ENQUEUED(act, va_atm_list)) {
1836 		TAILQ_REMOVE(vatm_activities(vatm), act, va_atm_list);
1837 		_TAILQ_MARK_NOT_ENQUEUED(act, va_atm_list);
1838 		_voucher_atm_update_mailbox(vatm);
1839 		// Balance initial creation refcnt. Caller must hold additional
1840 		// reference to ensure this does not release vatm before the unlock,
1841 		// see _voucher_atm_activity_collect
1842 		_voucher_activity_atm_release(act);
1843 	}
1844 	_voucher_activity_lock_unlock(vatm_activities_lock(vatm));
1845 }
1846 
1847 static _voucher_activity_t
_voucher_atm_activity_mark_used(_voucher_activity_t act)1848 _voucher_atm_activity_mark_used(_voucher_activity_t act)
1849 {
1850 	_voucher_atm_t vatm = act->va_atm;
1851 	_voucher_activity_lock_lock(vatm_activities_lock(vatm));
1852 	if (!_TAILQ_IS_ENQUEUED(act, va_atm_used_list)) {
1853 		_voucher_activity_ordered_insert(act, vatm_used_activities(vatm),
1854 				va_atm_used_list);
1855 		_voucher_atm_update_used_mailbox(vatm);
1856 		_dispatch_voucher_activity_debug("mark used", act);
1857 	}
1858 	_voucher_activity_lock_unlock(vatm_activities_lock(vatm));
1859 	return act;
1860 }
1861 
1862 static void
_voucher_atm_activity_mark_unused(_voucher_activity_t act)1863 _voucher_atm_activity_mark_unused(_voucher_activity_t act)
1864 {
1865 	bool atm_collect = false, updated = false;
1866 	_voucher_atm_t vatm = act->va_atm;
1867 	_voucher_activity_lock_lock(vatm_activities_lock(vatm));
1868 	if (_TAILQ_IS_ENQUEUED(act, va_atm_used_list)) {
1869 		_dispatch_voucher_activity_debug("mark unused", act);
1870 		TAILQ_REMOVE(&vatm->vatm_used_activities, act, va_atm_used_list);
1871 		_TAILQ_MARK_NOT_ENQUEUED(act, va_atm_used_list);
1872 		atm_collect = true;
1873 		_voucher_atm_retain(vatm);
1874 		updated = _voucher_atm_update_used_mailbox(vatm);
1875 	}
1876 	_voucher_activity_lock_unlock(vatm_activities_lock(vatm));
1877 	if (atm_collect) {
1878 		_voucher_atm_release(vatm);
1879 		_voucher_atm_collect_if_needed(updated);
1880 	}
1881 }
1882 
1883 static void
_voucher_atm_activity_collect(_voucher_atm_t vatm,atm_subaid32_t min_subaid)1884 _voucher_atm_activity_collect(_voucher_atm_t vatm, atm_subaid32_t min_subaid)
1885 {
1886 	_dispatch_voucher_atm_debug("collect min subaid 0x%x", vatm, min_subaid);
1887 	voucher_activity_id_t min_va_id = VATM_ACTID(vatm, min_subaid);
1888 	_voucher_activity_t act;
1889 	do {
1890 		_voucher_activity_lock_lock(vatm_activities_lock(vatm));
1891 		TAILQ_FOREACH(act, vatm_activities(vatm), va_atm_list) {
1892 			if (act->va_id >= min_va_id) {
1893 				act = NULL;
1894 				break;
1895 			}
1896 			if (!_TAILQ_IS_ENQUEUED(act, va_atm_used_list)) {
1897 				_voucher_activity_atm_retain(act);
1898 				break;
1899 			}
1900 		}
1901 		_voucher_activity_lock_unlock(vatm_activities_lock(vatm));
1902 		if (act) {
1903 			_voucher_activity_collect(act);
1904 			_voucher_activity_atm_release(act);
1905 		}
1906 	} while (act);
1907 }
1908 
1909 DISPATCH_NOINLINE
1910 static void
_voucher_atm_collect(void)1911 _voucher_atm_collect(void)
1912 {
1913 	_voucher_atm_t vatms[_voucher_atm_mailboxes], vatm;
1914 	atm_aid_t aids[_voucher_atm_mailboxes];
1915 	mach_atm_subaid_t subaids[_voucher_atm_mailboxes];
1916 	uint32_t i, a = 0, s;
1917 
1918 	_voucher_activity_lock_lock(vam_atms_lock());
1919 	for (i = 0; i < _voucher_activity_hash_size; i++) {
1920 		TAILQ_FOREACH(vatm, vam_atms(i), vatm_list){
1921 			if (vatm == _voucher_activity_heap->vam_default_activity_atm ||
1922 					vatm->vatm_mailbox_offset == MAILBOX_OFFSET_UNSET) continue;
1923 			_dispatch_voucher_atm_debug("find min subaid", vatm);
1924 			vatms[a] = _voucher_atm_retain(vatm);
1925 			aids[a] = vatm->vatm_id;
1926 			if (++a == _voucher_atm_mailboxes) goto out;
1927 		}
1928 	}
1929 out:
1930 	_voucher_activity_lock_unlock(vam_atms_lock());
1931 	if (!a) return;
1932 	kern_return_t kr;
1933 	mach_voucher_t kv = vatms[0]->vatm_kvoucher;
1934 	mach_voucher_attr_content_t kvc_in = (mach_voucher_attr_content_t)&aids;
1935 	mach_voucher_attr_content_size_t kvc_in_size = sizeof(atm_aid_t) * a;
1936 	mach_voucher_attr_content_t kvc_out = (mach_voucher_attr_content_t)&subaids;
1937 	mach_voucher_attr_content_size_t kvc_out_size = sizeof(mach_atm_subaid_t)*a;
1938 	kr = mach_voucher_attr_command(kv, MACH_VOUCHER_ATTR_KEY_ATM,
1939 			ATM_FIND_MIN_SUB_AID, kvc_in, kvc_in_size, kvc_out, &kvc_out_size);
1940 	DISPATCH_VERIFY_MIG(kr);
1941 	(void)dispatch_assume_zero(kr);
1942 	s = kvc_out_size / sizeof(mach_atm_subaid_t);
1943 #if DISPATCH_DEBUG && DISPATCH_VOUCHER_ACTIVITY_DEBUG
1944 	_dispatch_debug("found min subaids (%u out of %u)", s, a);
1945 #endif
1946 	for (i = 0; i < a; i++) {
1947 		if (i < s) _voucher_atm_activity_collect(vatms[i],
1948 				(atm_subaid32_t)subaids[i]);
1949 		_voucher_atm_release(vatms[i]);
1950 	}
1951 }
1952 
1953 static inline void
_voucher_atm_collect_if_needed(bool updated)1954 _voucher_atm_collect_if_needed(bool updated)
1955 {
1956 	long level;
1957 	if (updated) {
1958 		level = dispatch_atomic_add(&_voucher_atm_collect_level, 2ul, relaxed);
1959 	} else {
1960 		level = _voucher_atm_collect_level;
1961 		if (!level) return;
1962 	}
1963 	if (level & 1 || level <= _voucher_atm_collect_threshold) return;
1964 	if (!dispatch_atomic_cmpxchg(&_voucher_atm_collect_level, level, level + 1,
1965 			acquire)) return;
1966 #if DISPATCH_DEBUG && DISPATCH_VOUCHER_ACTIVITY_DEBUG
1967 	_dispatch_debug("atm collect: reached level %ld", level/2);
1968 #endif
1969 	if (slowpath(level < 0)) {
1970 		DISPATCH_CRASH("ATM collection level corruption");
1971 	}
1972 	_voucher_atm_collect();
1973 	dispatch_atomic_sub(&_voucher_atm_collect_level, level + 1, release);
1974 }
1975 
1976 DISPATCH_NOINLINE
1977 static void
_voucher_atm_fault(mach_voucher_attr_command_t kvc_cmd)1978 _voucher_atm_fault(mach_voucher_attr_command_t kvc_cmd)
1979 {
1980 	_voucher_activity_t act = _voucher_activity_get(_voucher_get());
1981 	mach_voucher_t kv = _voucher_activity_get_atm_mach_voucher(act);
1982 	if (!kv) return;
1983 
1984 	kern_return_t kr;
1985 	mach_atm_subaid_t subaid = VACTID_SUBID(act->va_id);
1986 	mach_voucher_attr_content_t kvc_in = (mach_voucher_attr_content_t)&subaid;
1987 	mach_voucher_attr_content_size_t kvc_in_size = sizeof(mach_atm_subaid_t);
1988 	mach_voucher_attr_content_t kvc_out = (mach_voucher_attr_content_t)&subaid;
1989 	mach_voucher_attr_content_size_t kvc_out_size = sizeof(mach_atm_subaid_t);
1990 	kr = mach_voucher_attr_command(kv, MACH_VOUCHER_ATTR_KEY_ATM,
1991 			kvc_cmd, kvc_in, kvc_in_size, kvc_out, &kvc_out_size);
1992 	DISPATCH_VERIFY_MIG(kr);
1993 	(void)dispatch_assume_zero(kr);
1994 }
1995 
1996 static atm_aid_t
_voucher_mach_voucher_get_atm_id(mach_voucher_t kv)1997 _voucher_mach_voucher_get_atm_id(mach_voucher_t kv)
1998 {
1999 	kern_return_t kr;
2000 	atm_aid_t atm_id = 0;
2001 	mach_voucher_attr_content_t kvc = (mach_voucher_attr_content_t)&atm_id;
2002     mach_voucher_attr_content_size_t kvc_size = sizeof(atm_id);
2003 	kr = mach_voucher_extract_attr_content(kv, MACH_VOUCHER_ATTR_KEY_ATM, kvc,
2004 			&kvc_size);
2005 	DISPATCH_VERIFY_MIG(kr);
2006 	(void)dispatch_assume_zero(kr);
2007     return atm_id;
2008 }
2009 
2010 static mach_voucher_t
_voucher_atm_mach_voucher_create(atm_aid_t * atm_id_ptr)2011 _voucher_atm_mach_voucher_create(atm_aid_t *atm_id_ptr)
2012 {
2013 	kern_return_t kr;
2014 	mach_voucher_t kv;
2015 	static const mach_voucher_attr_recipe_data_t atm_create_recipe = {
2016 		.key = MACH_VOUCHER_ATTR_KEY_ATM,
2017 		.command = MACH_VOUCHER_ATTR_ATM_CREATE,
2018 	};
2019 	kr = _voucher_create_mach_voucher(&atm_create_recipe,
2020 			sizeof(atm_create_recipe), &kv);
2021 	if (dispatch_assume_zero(kr)) {
2022 		DISPATCH_CLIENT_CRASH("Could not create ATM mach voucher");
2023 	}
2024 	atm_aid_t atm_id = _voucher_mach_voucher_get_atm_id(kv);
2025 	if (!dispatch_assume(atm_id)) {
2026 		DISPATCH_CLIENT_CRASH("Could not extract ATM ID");
2027 	}
2028 	_dispatch_kvoucher_debug("atm create <%lld>", kv, atm_id);
2029 	*atm_id_ptr = atm_id;
2030 	return kv;
2031 }
2032 
2033 static void
_voucher_atm_mailbox_mach_voucher_register(_voucher_atm_t vatm,mach_voucher_t kv)2034 _voucher_atm_mailbox_mach_voucher_register(_voucher_atm_t vatm,
2035 		mach_voucher_t kv)
2036 {
2037 	_dispatch_voucher_atm_debug("mailbox register %lld with kvoucher[0x%08x]",
2038 			vatm, vatm->vatm_mailbox_offset, kv);
2039 	kern_return_t kr;
2040 	mach_voucher_t akv;
2041 	atm_mailbox_offset_t offset = vatm->vatm_mailbox_offset;
2042 	mach_voucher_attr_recipe_t vr;
2043 	size_t vr_size;
2044 	static const mach_voucher_attr_recipe_data_t atm_register_recipe = {
2045 		.key = MACH_VOUCHER_ATTR_KEY_ATM,
2046 		.command = MACH_VOUCHER_ATTR_ATM_REGISTER,
2047 		.content_size = sizeof(offset),
2048 	};
2049 	vr_size = sizeof(atm_register_recipe) + atm_register_recipe.content_size;
2050 	vr = alloca(vr_size);
2051 	*vr = atm_register_recipe;
2052 	vr->previous_voucher = kv;
2053 	memcpy(&vr->content, &offset, sizeof(offset));
2054 	kr = _voucher_create_mach_voucher(vr, vr_size, &akv);
2055 	if (dispatch_assume_zero(kr)) {
2056 		DISPATCH_CLIENT_CRASH("Could not register ATM ID");
2057 	}
2058 	if (!vatm->vatm_kvoucher) {
2059 		vatm->vatm_kvoucher = akv;
2060 	} else {
2061 #if !RDAR_17510224
2062 		if (akv != vatm->vatm_kvoucher) {
2063 			DISPATCH_CRASH("Unexpected mach voucher returned by ATM ID "
2064 					"registration");
2065 		}
2066 		_voucher_dealloc_mach_voucher(akv);
2067 #else
2068 		DISPATCH_CRASH("Registered invalid ATM object");
2069 #endif
2070 	}
2071 	_dispatch_voucher_atm_debug("mailbox registered %lld", vatm,
2072 			vatm->vatm_mailbox_offset);
2073 }
2074 
2075 static void
_voucher_atm_mailbox_register(_voucher_atm_t vatm)2076 _voucher_atm_mailbox_register(_voucher_atm_t vatm)
2077 {
2078 	mach_voucher_t kv = vatm->vatm_kvoucher;
2079 	if (!kv) return;
2080 #if !RDAR_17510224
2081 	_voucher_atm_mailbox_mach_voucher_register(vatm, kv);
2082 #else // RDAR_17510224
2083 	_dispatch_voucher_atm_debug("mailbox register %lld", vatm,
2084 			vatm->vatm_mailbox_offset);
2085 	kern_return_t kr;
2086 	atm_mailbox_offset_t offset = vatm->vatm_mailbox_offset;
2087 	mach_voucher_attr_content_t kvc_in = (mach_voucher_attr_content_t)&offset;
2088 	mach_voucher_attr_content_size_t kvc_in_size = sizeof(offset);
2089 	mach_voucher_attr_content_t kvc_out = NULL;
2090 	mach_voucher_attr_content_size_t kvc_out_size = 0;
2091 	kr = mach_voucher_attr_command(kv, MACH_VOUCHER_ATTR_KEY_ATM,
2092 			ATM_ACTION_REGISTER, kvc_in, kvc_in_size, kvc_out,
2093 			&kvc_out_size);
2094 	DISPATCH_VERIFY_MIG(kr);
2095 	if (dispatch_assume_zero(kr)) {
2096 		DISPATCH_CLIENT_CRASH("Could not register ATM ID");
2097 	}
2098 	_dispatch_voucher_atm_debug("mailbox registered %lld", vatm,
2099 			vatm->vatm_mailbox_offset);
2100 #endif // RDAR_17510224
2101 }
2102 
2103 static bool
_voucher_atm_mailbox_unregister(_voucher_atm_t vatm)2104 _voucher_atm_mailbox_unregister(_voucher_atm_t vatm)
2105 {
2106 	if (vatm->vatm_mailbox_offset == MAILBOX_OFFSET_UNSET) return false;
2107 	_dispatch_voucher_atm_debug("mailbox unregister %lld", vatm,
2108 			vatm->vatm_mailbox_offset);
2109 	mach_voucher_t kv = vatm->vatm_kvoucher;
2110 	dispatch_assert(kv);
2111 	kern_return_t kr;
2112 	atm_mailbox_offset_t offset = vatm->vatm_mailbox_offset;
2113 	mach_voucher_attr_content_t kvc_in = (mach_voucher_attr_content_t)&offset;
2114 	mach_voucher_attr_content_size_t kvc_in_size = sizeof(offset);
2115 	mach_voucher_attr_content_t kvc_out = NULL;
2116 	mach_voucher_attr_content_size_t kvc_out_size = 0;
2117 	kr = mach_voucher_attr_command(kv, MACH_VOUCHER_ATTR_KEY_ATM,
2118 			ATM_ACTION_UNREGISTER, kvc_in, kvc_in_size, kvc_out, &kvc_out_size);
2119 	DISPATCH_VERIFY_MIG(kr);
2120 	if (kr && kr != KERN_INVALID_VALUE) {
2121 		(void)dispatch_assume_zero(kr);
2122 		DISPATCH_CLIENT_CRASH("Could not unregister ATM ID");
2123 	}
2124 	_dispatch_voucher_atm_debug("mailbox unregistered %lld", vatm,
2125 			vatm->vatm_mailbox_offset);
2126 	return true;
2127 }
2128 
2129 static _voucher_atm_t
_voucher_atm_create(mach_voucher_t kv,atm_aid_t atm_id)2130 _voucher_atm_create(mach_voucher_t kv, atm_aid_t atm_id)
2131 {
2132 	atm_mailbox_offset_t mailbox_offset = _voucher_atm_mailbox_alloc();
2133 	if (kv && mailbox_offset == MAILBOX_OFFSET_UNSET) return NULL;
2134 	_voucher_atm_t vatm = _dispatch_calloc(1ul, sizeof(struct _voucher_atm_s));
2135 	if (!kv) {
2136 		kv = _voucher_atm_mach_voucher_create(&atm_id);
2137 		if (mailbox_offset == MAILBOX_OFFSET_UNSET) {
2138 			_voucher_dealloc_mach_voucher(kv);
2139 		} else {
2140 			vatm->vatm_kvoucher = kv;
2141 		}
2142 		kv = MACH_VOUCHER_NULL;
2143 	}
2144 	vatm->vatm_id = atm_id;
2145 	vatm->vatm_mailbox_offset = mailbox_offset;
2146 	_voucher_activity_lock_init(vatm_activities_lock(vatm));
2147 	TAILQ_INIT(&vatm->vatm_activities);
2148 	TAILQ_INIT(&vatm->vatm_used_activities);
2149 	_voucher_atm_mailbox_set(mailbox_offset, 0, true);
2150 	_voucher_atm_mailbox_set(mailbox_offset, ATM_SUBAID32_MAX, false);
2151 	_voucher_atm_t vatmx = _voucher_atm_try_insert(vatm);
2152 	if (vatmx) {
2153 		_voucher_atm_dispose(vatm, false);
2154 		vatm = vatmx;
2155 	} else if (kv) {
2156 		_voucher_atm_mailbox_mach_voucher_register(vatm, kv);
2157 	} else {
2158 		_voucher_atm_mailbox_register(vatm);
2159 	}
2160 	_dispatch_voucher_atm_debug("create with kvoucher[0x%08x]", vatm, kv);
2161 	return vatm;
2162 }
2163 
2164 static void
_voucher_atm_dispose(_voucher_atm_t vatm,bool unregister)2165 _voucher_atm_dispose(_voucher_atm_t vatm, bool unregister)
2166 {
2167 	_dispatch_voucher_atm_debug("dispose", vatm);
2168 	dispatch_assert(TAILQ_EMPTY(&vatm->vatm_activities));
2169 	dispatch_assert(TAILQ_EMPTY(&vatm->vatm_used_activities));
2170 	if (slowpath(_TAILQ_IS_ENQUEUED(vatm, vatm_list))) {
2171 		_dispatch_voucher_atm_debug("corruption", vatm);
2172 		DISPATCH_CRASH("ATM corruption");
2173 	}
2174 	vatm->vatm_list.tqe_next = DISPATCH_OBJECT_LISTLESS;
2175 	bool free_mailbox = (vatm->vatm_mailbox_offset != MAILBOX_OFFSET_UNSET);
2176 	if (vatm->vatm_kvoucher) {
2177 		if (unregister) free_mailbox = _voucher_atm_mailbox_unregister(vatm);
2178 		_voucher_dealloc_mach_voucher(vatm->vatm_kvoucher);
2179 		vatm->vatm_kvoucher = MACH_VOUCHER_NULL;
2180 	}
2181 	if (free_mailbox) {
2182 		_voucher_atm_mailbox_free(vatm->vatm_mailbox_offset);
2183 		vatm->vatm_mailbox_offset = MAILBOX_OFFSET_UNSET;
2184 	}
2185 	free(vatm);
2186 }
2187 
2188 static inline mach_voucher_t
_voucher_activity_get_atm_mach_voucher(_voucher_activity_t act)2189 _voucher_activity_get_atm_mach_voucher(_voucher_activity_t act)
2190 {
2191 	mach_voucher_t kv;
2192 	kv = act && act->va_atm ? act->va_atm->vatm_kvoucher : MACH_VOUCHER_NULL;
2193 	return kv;
2194 }
2195 
2196 DISPATCH_NOINLINE
2197 static _voucher_atm_t
_voucher_atm_base_copy_and_activity_id_make(voucher_activity_id_t * va_id_ptr)2198 _voucher_atm_base_copy_and_activity_id_make(voucher_activity_id_t *va_id_ptr)
2199 {
2200 	_voucher_atm_subid_t subid;
2201 	_voucher_atm_t vatm, vatm_old = NULL, vatm_new = NULL;
2202 	if (_voucher_activity_heap->vam_base_atm_subid_max == 1) {
2203 		vatm = _voucher_atm_create(0, 0);
2204 		subid = 1;
2205 		goto out;
2206 	}
2207 	_voucher_activity_lock_lock(vam_base_atm_lock());
2208 	vatm = _voucher_activity_heap->vam_base_atm;
2209 retry:
2210 	_voucher_atm_retain(vatm);
2211 	subid = _voucher_activity_heap->vam_base_atm_subid;
2212 	if (subid++ >= _voucher_activity_heap->vam_base_atm_subid_max) {
2213 		_voucher_activity_lock_unlock(vam_base_atm_lock());
2214 		if (!vatm_new) vatm_new = _voucher_atm_create(0, 0);
2215 		_voucher_activity_lock_lock(vam_base_atm_lock());
2216 		_voucher_atm_release(vatm);
2217 		vatm_old = vatm;
2218 		vatm = _voucher_activity_heap->vam_base_atm;
2219 		if (vatm != vatm_old) {
2220 			vatm_old = NULL;
2221 			goto retry;
2222 		}
2223 		_voucher_activity_heap->vam_base_atm = vatm = vatm_new;
2224 		_voucher_activity_heap->vam_base_atm_subid = subid = 1;
2225 		vatm_new = NULL;
2226 		_voucher_atm_retain(vatm);
2227 		_dispatch_voucher_atm_debug("base replace", vatm);
2228 	} else {
2229 		_voucher_activity_heap->vam_base_atm_subid = subid;
2230 		_dispatch_voucher_atm_debug("base copy", vatm);
2231 	}
2232 	_voucher_activity_lock_unlock(vam_base_atm_lock());
2233 	if (vatm_old) _voucher_atm_release(vatm_old);
2234 	if (vatm_new) _voucher_atm_release(vatm_new);
2235 out:
2236 	*va_id_ptr = VATM_ACTID(vatm, subid);
2237 	return vatm;
2238 }
2239 
2240 static voucher_activity_id_t
_voucher_atm_nested_atm_id_make(void)2241 _voucher_atm_nested_atm_id_make(void)
2242 {
2243 	atm_aid_t atm_id;
2244 	mach_voucher_t kv = _voucher_atm_mach_voucher_create(&atm_id);
2245 	_voucher_dealloc_mach_voucher(kv); // just need the unique ID
2246 	return VATMID2ACTID(atm_id);
2247 }
2248 
2249 static voucher_activity_id_t
_voucher_atm_nested_activity_id_make(void)2250 _voucher_atm_nested_activity_id_make(void)
2251 {
2252 	voucher_activity_id_t va_id, va_id_old, va_id_new;
2253 	_voucher_atm_subid_t subid;
2254 	_voucher_activity_lock_lock(vam_nested_atm_lock());
2255 	va_id = _voucher_activity_heap->vam_nested_atm_id;
2256 retry:
2257 	subid = _voucher_activity_heap->vam_nested_atm_subid;
2258 	if (subid++ >= VATM_SUBID_MAX) {
2259 		_voucher_activity_lock_unlock(vam_nested_atm_lock());
2260 		va_id_new = _voucher_atm_nested_atm_id_make();
2261 		va_id_old = va_id;
2262 		_voucher_activity_lock_lock(vam_nested_atm_lock());
2263 		va_id = _voucher_activity_heap->vam_nested_atm_id;
2264 		if (va_id != va_id_old) goto retry;
2265 		_voucher_activity_heap->vam_nested_atm_id = va_id = va_id_new;
2266 		subid = 1;
2267 	}
2268 	_voucher_activity_heap->vam_nested_atm_subid = subid;
2269 	_voucher_activity_lock_unlock(vam_nested_atm_lock());
2270 	return va_id + subid;
2271 }
2272 
2273 #pragma mark -
2274 #pragma mark voucher_activity_id_t
2275 
2276 voucher_activity_id_t
voucher_activity_start_with_location(voucher_activity_trace_id_t trace_id,uint64_t location,voucher_activity_flag_t flags)2277 voucher_activity_start_with_location(voucher_activity_trace_id_t trace_id,
2278 		uint64_t location, voucher_activity_flag_t flags)
2279 {
2280 	dispatch_once_f(&_voucher_activity_heap_pred, NULL,
2281 			_voucher_activity_heap_init);
2282 	if (!_voucher_activity_trace_id_enabled(trace_id)) return 0;
2283 	voucher_activity_id_t va_id = 0, va_base_id = 0;
2284 	_voucher_atm_t vatm = NULL;
2285 	_voucher_activity_t act = NULL;
2286 	_voucher_activity_tracepoint_t vat = NULL;
2287 	unsigned int activities = 1, oactivities = 0;
2288 	voucher_t ov = _voucher_get();
2289 	if (!(flags & voucher_activity_flag_force) && ov && ov->v_activities) {
2290 		oactivities = ov->v_activities;
2291 		activities += oactivities;
2292 		if (activities > _voucher_max_activities) {
2293 			va_id = _voucher_atm_nested_activity_id_make();
2294 			goto out;
2295 		}
2296 	}
2297 	if (activities == 1) {
2298 		vatm = _voucher_atm_base_copy_and_activity_id_make(&va_id);
2299 		if (vatm->vatm_kvoucher) {
2300 			// consumes vatm reference:
2301 			act = _voucher_activity_create_with_atm(vatm, va_id, trace_id,
2302 					location, NULL);
2303 			vat = (_voucher_activity_tracepoint_t)act;
2304 		} else {
2305 			_voucher_atm_release(vatm);
2306 		}
2307 		if (!act) {
2308 			activities++;
2309 			// default to _voucher_activity_default base activity
2310 			va_base_id = _voucher_activity_default->va_id;
2311 		}
2312 	}
2313 	pthread_priority_t priority = _voucher_get_priority(ov);
2314 	mach_voucher_attr_recipe_size_t extra = ov ? _voucher_extra_size(ov) : 0;
2315 	voucher_t v = _voucher_alloc(activities, priority, extra);
2316 	if (extra) {
2317 		memcpy(_voucher_extra_recipes(v), _voucher_extra_recipes(ov), extra);
2318 	}
2319 	if (ov && ov->v_kvoucher) {
2320 		voucher_t kvb = ov->v_kvbase ? ov->v_kvbase : ov;
2321 		v->v_kvbase = _voucher_retain(kvb);
2322 		v->v_kvoucher = kvb->v_kvoucher;
2323 	}
2324 	voucher_activity_id_t *activity_ids = _voucher_activity_ids(v);
2325 	if (oactivities) {
2326 		memcpy(activity_ids, _voucher_activity_ids(ov),
2327 				oactivities * sizeof(voucher_activity_id_t));
2328 	}
2329 	if (!va_id) {
2330 		va_id = _voucher_atm_nested_activity_id_make();
2331 		if (ov && ov->v_activity) {
2332 			act = _voucher_activity_retain(ov->v_activity);
2333 		}
2334 	}
2335 	if (va_base_id) activity_ids[0] = va_base_id;
2336 	activity_ids[activities-1] = va_id;
2337 	v->v_activity = act;
2338 	_voucher_swap(ov, v);
2339 	if (vat) return va_id; // new _voucher_activity_s contains trace info
2340 out:
2341 	vat = _voucher_activity_trace_with_id(trace_id);
2342 	if (vat) {
2343 		vat->vat_flags |= _voucher_activity_trace_flag_activity |
2344 				_voucher_activity_trace_flag_start;
2345 		vat->vat_data[0] = va_id;
2346 	}
2347 	return va_id;
2348 }
2349 
2350 voucher_activity_id_t
voucher_activity_start(voucher_activity_trace_id_t trace_id,voucher_activity_flag_t flags)2351 voucher_activity_start(voucher_activity_trace_id_t trace_id,
2352 		voucher_activity_flag_t flags)
2353 {
2354 	return voucher_activity_start_with_location(trace_id, 0, flags);
2355 }
2356 
2357 void
voucher_activity_end(voucher_activity_id_t va_id)2358 voucher_activity_end(voucher_activity_id_t va_id)
2359 {
2360 	if (!va_id) return;
2361 	_voucher_activity_tracepoint_t vat;
2362 	vat = _voucher_activity_trace_with_id(_voucher_activity_trace_id_release);
2363 	if (vat) {
2364 		vat->vat_flags |= _voucher_activity_trace_flag_activity |
2365 				_voucher_activity_trace_flag_end;
2366 		vat->vat_data[0] = va_id;
2367 	}
2368 	voucher_t v = _voucher_get();
2369 	if (!v) return;
2370 	unsigned int activities =  v->v_activities, act_idx = activities;
2371 	voucher_activity_id_t *activity_ids = _voucher_activity_ids(v);
2372 	while (act_idx) {
2373 		if (activity_ids[act_idx-1] == va_id) break;
2374 		act_idx--;
2375 	}
2376 	if (!act_idx) return; // activity_id not found
2377 	pthread_priority_t priority = _voucher_get_priority(v);
2378 	mach_voucher_attr_recipe_size_t extra = _voucher_extra_size(v);
2379 	voucher_t nv = NULL;
2380 	if (act_idx > 1 || activities == 1) --activities;
2381 	if (priority || activities || extra || v->v_kvoucher) {
2382 		nv = _voucher_alloc(activities, priority, extra);
2383 		if (extra) {
2384 			memcpy(_voucher_extra_recipes(nv), _voucher_extra_recipes(v),extra);
2385 		}
2386 	}
2387 	if (v->v_kvoucher) {
2388 		voucher_t kvb = v->v_kvbase ? v->v_kvbase : v;
2389 		nv->v_kvbase = _voucher_retain(kvb);
2390 		nv->v_kvoucher = kvb->v_kvoucher;
2391 	}
2392 	bool atm_collect = !activities;
2393 	if (activities) {
2394 		voucher_activity_id_t *new_activity_ids = _voucher_activity_ids(nv);
2395 		if (act_idx == 1 && _voucher_activity_default) {
2396 			atm_collect = true;
2397 			// default to _voucher_activity_default base activity
2398 			new_activity_ids[0] = _voucher_activity_default->va_id;
2399 			memcpy(&new_activity_ids[1], &activity_ids[1],
2400 					(activities - 1) * sizeof(voucher_activity_id_t));
2401 		} else {
2402 			if (v->v_activity) {
2403 				nv->v_activity = _voucher_activity_retain(v->v_activity);
2404 			}
2405 			memcpy(new_activity_ids, activity_ids,
2406 					--act_idx * sizeof(voucher_activity_id_t));
2407 			if (act_idx < activities) {
2408 				memcpy(&new_activity_ids[act_idx], &activity_ids[act_idx+1],
2409 						(activities - act_idx) * sizeof(voucher_activity_id_t));
2410 			}
2411 		}
2412 	}
2413 	_voucher_swap(v, nv);
2414 }
2415 
2416 unsigned int
voucher_get_activities(voucher_activity_id_t * entries,unsigned int * count)2417 voucher_get_activities(voucher_activity_id_t *entries, unsigned int *count)
2418 {
2419 	voucher_t v = _voucher_get();
2420 	if (!v || !count) return 0;
2421 	unsigned int activities = v->v_activities;
2422 	if (*count < activities) activities = *count;
2423 	*count = v->v_activities;
2424 	voucher_activity_id_t *activity_ids = _voucher_activity_ids(v);
2425 	if (activities && entries) {
2426 		memcpy(entries, activity_ids, activities *
2427 				sizeof(voucher_activity_id_t));
2428 	}
2429 	return activities;
2430 }
2431 
2432 uint8_t
voucher_activity_get_namespace(void)2433 voucher_activity_get_namespace(void)
2434 {
2435 	voucher_t v = _voucher_get();
2436 	if (!v || !v->v_activity) return 0;
2437 	return v->v_activity->va_namespace;
2438 }
2439 
2440 DISPATCH_NOINLINE
2441 _voucher_activity_tracepoint_t
_voucher_activity_tracepoint_get_slow(unsigned int slots)2442 _voucher_activity_tracepoint_get_slow(unsigned int slots)
2443 {
2444 	_voucher_activity_t act;
2445 	_voucher_activity_buffer_header_t vab;
2446 	_voucher_activity_tracepoint_t vat = NULL;
2447 	voucher_t v = _voucher_get();
2448 	if (v && v->v_activity) {
2449 		act = v->v_activity;
2450 	} else {
2451 		dispatch_once_f(&_voucher_activity_heap_pred, NULL,
2452 				_voucher_activity_heap_init);
2453 		if (_voucher_activity_disabled()) return NULL;
2454 		act = _voucher_activity_default;
2455 	}
2456 	vab = act->va_current_buffer;
2457 	if (vab && vab->vabh_next_tracepoint_idx <=
2458 			_voucher_activity_tracepoints_per_buffer) {
2459 		goto retry; // another slowpath raced us
2460 	}
2461 	do {
2462 		vab = _voucher_activity_buffer_alloc(act, vab);
2463 		if (!vab) break;
2464 retry:
2465 		vat = _voucher_activity_buffer_tracepoint_get(vab, slots);
2466 	} while (!vat);
2467 	return vat;
2468 }
2469 
2470 static inline void
_voucher_activity_trace_fault(voucher_activity_trace_id_t trace_id)2471 _voucher_activity_trace_fault(voucher_activity_trace_id_t trace_id)
2472 {
2473 	if (!slowpath(_voucher_activity_trace_id_is_subtype(trace_id, error))) {
2474 		return;
2475 	}
2476 	mach_voucher_attr_command_t atm_cmd = ATM_ACTION_COLLECT;
2477 	if (_voucher_activity_trace_id_is_subtype(trace_id, fault)) {
2478 		atm_cmd = ATM_ACTION_LOGFAIL;
2479 	}
2480 	return _voucher_atm_fault(atm_cmd);
2481 }
2482 
2483 uint64_t
voucher_activity_trace(voucher_activity_trace_id_t trace_id,uint64_t location,void * buffer,size_t length)2484 voucher_activity_trace(voucher_activity_trace_id_t trace_id, uint64_t location,
2485 		void *buffer, size_t length)
2486 {
2487 	if (!_voucher_activity_trace_id_enabled(trace_id)) return 0;
2488 	_voucher_activity_tracepoint_t vat;
2489 	const unsigned int slots = length <= sizeof(vat->vat_data) ? 1 : 2;
2490 	vat = _voucher_activity_tracepoint_get(slots);
2491 	if (!vat) vat = _voucher_activity_tracepoint_get_slow(slots);
2492 	if (!vat) return 0;
2493 	uint64_t timestamp = _voucher_activity_tracepoint_init_with_id(vat,
2494 			trace_id, location);
2495 	void *tbuf = vat->vat_data;
2496 	size_t tlen = sizeof(vat->vat_data);
2497 	if (length < tlen) {
2498 		memcpy(tbuf, buffer, length);
2499 	} else {
2500 		memcpy(tbuf, buffer, tlen);
2501 	}
2502 	if (length > tlen) {
2503 		vat->vat_flags |= _voucher_activity_trace_flag_wide_first;
2504 		buffer += tlen;
2505 		length -= tlen;
2506 		(++vat)->vat_flags = _voucher_activity_trace_flag_tracepoint |
2507 				_voucher_activity_trace_flag_wide_second;
2508 		vat->vat_type = 0; vat->vat_namespace = 0;
2509 		tbuf = (void*)vat + offsetof(typeof(*vat), vat_code);
2510 		tlen = sizeof(*vat) - offsetof(typeof(*vat), vat_code);
2511 		if (length < tlen) {
2512 			memcpy(tbuf, buffer, length);
2513 		} else {
2514 			memcpy(tbuf, buffer, tlen);
2515 		}
2516 	}
2517 	_voucher_activity_trace_fault(trace_id);
2518 	return timestamp;
2519 }
2520 
2521 uint64_t
voucher_activity_trace_args(voucher_activity_trace_id_t trace_id,uint64_t location,uintptr_t arg1,uintptr_t arg2,uintptr_t arg3,uintptr_t arg4)2522 voucher_activity_trace_args(voucher_activity_trace_id_t trace_id,
2523 		uint64_t location, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
2524 		uintptr_t arg4)
2525 {
2526 	if (!_voucher_activity_trace_id_enabled(trace_id)) return 0;
2527 	_voucher_activity_tracepoint_t vat;
2528 	vat = _voucher_activity_tracepoint_get(1);
2529 	if (!vat) vat = _voucher_activity_tracepoint_get_slow(1);
2530 	if (!vat) return 0;
2531 	uint64_t timestamp = _voucher_activity_tracepoint_init_with_id(vat,
2532 			trace_id, location);
2533 	vat->vat_flags |= _voucher_activity_trace_flag_tracepoint_args;
2534 	vat->vat_data[0] = arg1;
2535 	vat->vat_data[1] = arg2;
2536 	vat->vat_data[2] = arg3;
2537 	vat->vat_data[3] = arg4;
2538 	_voucher_activity_trace_fault(trace_id);
2539 	return timestamp;
2540 }
2541 
2542 #pragma mark -
2543 #pragma mark _voucher_debug
2544 
2545 size_t
_voucher_debug(voucher_t v,char * buf,size_t bufsiz)2546 _voucher_debug(voucher_t v, char* buf, size_t bufsiz)
2547 {
2548 	size_t offset = 0;
2549 	#define bufprintf(...) \
2550 			offset += dsnprintf(&buf[offset], bufsiz - offset, ##__VA_ARGS__)
2551 	bufprintf("voucher[%p] = { xrefcnt = 0x%x, refcnt = 0x%x, ", v,
2552 			v->os_obj_xref_cnt + 1, v->os_obj_ref_cnt + 1);
2553 
2554 	if (v->v_kvbase) {
2555 		bufprintf("base voucher %p, ", v->v_kvbase);
2556 	}
2557 	if (v->v_kvoucher) {
2558 		bufprintf("kvoucher%s 0x%x, ", v->v_kvoucher == v->v_ipc_kvoucher ?
2559 				" & ipc kvoucher" : "", v->v_kvoucher);
2560 	}
2561 	if (v->v_ipc_kvoucher && v->v_ipc_kvoucher != v->v_kvoucher) {
2562 		bufprintf("ipc kvoucher 0x%x, ", v->v_ipc_kvoucher);
2563 	}
2564 	if (v->v_has_priority) {
2565 		bufprintf("QOS 0x%x, ", *_voucher_priority(v));
2566 	}
2567 	if (v->v_activities) {
2568 		voucher_activity_id_t *activity_ids = _voucher_activity_ids(v);
2569 		bufprintf("activity IDs = { ");
2570 		unsigned int i;
2571 		for (i = 0; i < v->v_activities; i++) {
2572 			bufprintf("0x%llx, ", *activity_ids++);
2573 		}
2574 		bufprintf("}, ");
2575 	}
2576 	if (v->v_activity) {
2577 		_voucher_activity_t va = v->v_activity;
2578 		_voucher_atm_t vatm = va->va_atm;
2579 		bufprintf("activity[%p] = { ID 0x%llx, use %d, atm[%p] = { "
2580 				"AID 0x%llx, ref %d, kvoucher 0x%x } }, ", va, va->va_id,
2581 				va->va_use_count + 1, va->va_atm, vatm->vatm_id,
2582 				vatm->vatm_refcnt + 1, vatm->vatm_kvoucher);
2583 	}
2584 	bufprintf("}");
2585 	return offset;
2586 }
2587 
2588 #else // VOUCHER_USE_MACH_VOUCHER
2589 
2590 #pragma mark -
2591 #pragma mark Simulator / vouchers disabled
2592 
2593 #if VOUCHER_ENABLE_RECIPE_OBJECTS
2594 voucher_t
voucher_create(voucher_recipe_t recipe)2595 voucher_create(voucher_recipe_t recipe)
2596 {
2597 	(void)recipe;
2598 	return NULL;
2599 }
2600 #endif
2601 
2602 voucher_t
voucher_adopt(voucher_t voucher)2603 voucher_adopt(voucher_t voucher)
2604 {
2605 	return voucher;
2606 }
2607 
2608 voucher_t
voucher_copy(void)2609 voucher_copy(void)
2610 {
2611 	return NULL;
2612 }
2613 
2614 voucher_t
voucher_copy_without_importance(void)2615 voucher_copy_without_importance(void)
2616 {
2617 	return NULL;
2618 }
2619 
2620 void
voucher_replace_default_voucher(void)2621 voucher_replace_default_voucher(void)
2622 {
2623 }
2624 
2625 void
voucher_decrement_importance_count4CF(voucher_t v)2626 voucher_decrement_importance_count4CF(voucher_t v)
2627 {
2628 	(void)v;
2629 }
2630 
2631 void
_voucher_thread_cleanup(void * voucher)2632 _voucher_thread_cleanup(void *voucher)
2633 {
2634 	(void)voucher;
2635 }
2636 
2637 void
_voucher_dealloc_mach_voucher(mach_voucher_t kv)2638 _voucher_dealloc_mach_voucher(mach_voucher_t kv)
2639 {
2640 	(void)kv;
2641 }
2642 
2643 mach_voucher_t
_voucher_create_mach_voucher_with_priority(voucher_t voucher,pthread_priority_t priority)2644 _voucher_create_mach_voucher_with_priority(voucher_t voucher,
2645 		pthread_priority_t priority)
2646 {
2647 	(void)voucher; (void)priority;
2648 	return MACH_VOUCHER_NULL;
2649 }
2650 
2651 voucher_t
_voucher_create_with_priority_and_mach_voucher(voucher_t voucher,pthread_priority_t priority,mach_voucher_t kv)2652 _voucher_create_with_priority_and_mach_voucher(voucher_t voucher,
2653 		pthread_priority_t priority, mach_voucher_t kv)
2654 {
2655 	(void)voucher; (void)priority; (void)kv;
2656 	return NULL;
2657 }
2658 
2659 voucher_t
voucher_create_with_mach_msg(mach_msg_header_t * msg)2660 voucher_create_with_mach_msg(mach_msg_header_t *msg)
2661 {
2662 	(void)msg;
2663 	return NULL;
2664 }
2665 
2666 #if VOUCHER_ENABLE_GET_MACH_VOUCHER
2667 mach_voucher_t
voucher_get_mach_voucher(voucher_t voucher)2668 voucher_get_mach_voucher(voucher_t voucher)
2669 {
2670 	(void)voucher;
2671 	return 0;
2672 }
2673 #endif
2674 
2675 void
_voucher_xref_dispose(voucher_t voucher)2676 _voucher_xref_dispose(voucher_t voucher)
2677 {
2678 	(void)voucher;
2679 }
2680 
2681 void
_voucher_dispose(voucher_t voucher)2682 _voucher_dispose(voucher_t voucher)
2683 {
2684 	(void)voucher;
2685 }
2686 
2687 void
_voucher_atfork_child(void)2688 _voucher_atfork_child(void)
2689 {
2690 }
2691 
2692 void
_voucher_init(void)2693 _voucher_init(void)
2694 {
2695 }
2696 
2697 void*
voucher_activity_get_metadata_buffer(size_t * length)2698 voucher_activity_get_metadata_buffer(size_t *length)
2699 {
2700 	*length = 0;
2701 	return NULL;
2702 }
2703 
2704 void
_voucher_activity_heap_pressure_normal(void)2705 _voucher_activity_heap_pressure_normal(void)
2706 {
2707 }
2708 
2709 void
_voucher_activity_heap_pressure_warn(void)2710 _voucher_activity_heap_pressure_warn(void)
2711 {
2712 }
2713 
2714 voucher_activity_id_t
voucher_activity_start_with_location(voucher_activity_trace_id_t trace_id,uint64_t location,voucher_activity_flag_t flags)2715 voucher_activity_start_with_location(voucher_activity_trace_id_t trace_id,
2716 		uint64_t location, voucher_activity_flag_t flags)
2717 {
2718 	(void)trace_id; (void)location; (void)flags;
2719 	return 0;
2720 }
2721 
2722 voucher_activity_id_t
voucher_activity_start(voucher_activity_trace_id_t trace_id,voucher_activity_flag_t flags)2723 voucher_activity_start(voucher_activity_trace_id_t trace_id,
2724 		voucher_activity_flag_t flags)
2725 {
2726 	(void)trace_id; (void)flags;
2727 	return 0;
2728 }
2729 
2730 void
voucher_activity_end(voucher_activity_id_t activity_id)2731 voucher_activity_end(voucher_activity_id_t activity_id)
2732 {
2733 	(void)activity_id;
2734 }
2735 
2736 unsigned int
voucher_get_activities(voucher_activity_id_t * entries,unsigned int * count)2737 voucher_get_activities(voucher_activity_id_t *entries, unsigned int *count)
2738 {
2739 	(void)entries; (void)count;
2740 	return 0;
2741 }
2742 
2743 uint8_t
voucher_activity_get_namespace(void)2744 voucher_activity_get_namespace(void)
2745 {
2746 	return 0;
2747 }
2748 
2749 uint64_t
voucher_activity_trace(voucher_activity_trace_id_t trace_id,uint64_t location,void * buffer,size_t length)2750 voucher_activity_trace(voucher_activity_trace_id_t trace_id, uint64_t location,
2751 		void *buffer, size_t length)
2752 {
2753 	(void)trace_id; (void)location; (void)buffer; (void)length;
2754 	return 0;
2755 }
2756 
2757 uint64_t
voucher_activity_trace_args(voucher_activity_trace_id_t trace_id,uint64_t location,uintptr_t arg1,uintptr_t arg2,uintptr_t arg3,uintptr_t arg4)2758 voucher_activity_trace_args(voucher_activity_trace_id_t trace_id,
2759 		uint64_t location, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
2760 		uintptr_t arg4)
2761 {
2762 	(void)trace_id; (void)location;
2763 	(void)arg1; (void)arg2; (void)arg3; (void)arg4;
2764 	return 0;
2765 }
2766 
2767 size_t
_voucher_debug(voucher_t v,char * buf,size_t bufsiz)2768 _voucher_debug(voucher_t v, char* buf, size_t bufsiz)
2769 {
2770 	(void)v; (void)buf; (void)bufsiz;
2771 	return 0;
2772 }
2773 
2774 #endif // VOUCHER_USE_MACH_VOUCHER
2775