xref: /xnu-8019.80.24/osfmk/bank/bank.c (revision a325d9c4a84054e40bbe985afedcb50ab80993ea)
1 /*
2  * Copyright (c) 2012-2020 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #include <bank/bank_internal.h>
30 #include <bank/bank_types.h>
31 #include <mach/mach_types.h>
32 #include <mach/kern_return.h>
33 #include <ipc/ipc_port.h>
34 #include <mach/mach_vm.h>
35 #include <mach/vm_map.h>
36 #include <vm/vm_map.h>
37 #include <mach/host_priv.h>
38 #include <mach/host_special_ports.h>
39 #include <kern/host.h>
40 #include <kern/ledger.h>
41 #include <kern/coalition.h>
42 #include <kern/thread_group.h>
43 #include <sys/kdebug.h>
44 #include <IOKit/IOBSD.h>
45 #include <mach/mach_voucher_attr_control.h>
46 #include <kern/policy_internal.h>
47 
48 /* we can't include the BSD <sys/persona.h> header here... */
49 #ifndef PERSONA_ID_NONE
50 #define PERSONA_ID_NONE ((uint32_t)-1)
51 #endif
52 
53 static ZONE_DECLARE(bank_task_zone, "bank_task",
54     sizeof(struct bank_task), ZC_NONE);
55 static ZONE_DECLARE(bank_account_zone, "bank_account",
56     sizeof(struct bank_account), ZC_NONE);
57 
58 #define MAX_BANK_TASK     (CONFIG_TASK_MAX)
59 #define MAX_BANK_ACCOUNT  (CONFIG_TASK_MAX + CONFIG_THREAD_MAX)
60 
61 #define BANK_ELEMENT_TO_HANDLE(x) (CAST_DOWN(bank_handle_t, (x)))
62 #define HANDLE_TO_BANK_ELEMENT(x) (CAST_DOWN(bank_element_t, (x)))
63 
64 /* Need macro since bank_element_t is 4 byte aligned on release kernel and direct type case gives compilation error */
65 #define CAST_TO_BANK_ELEMENT(x) ((bank_element_t)((void *)(x)))
66 #define CAST_TO_BANK_TASK(x) ((bank_task_t)((void *)(x)))
67 #define CAST_TO_BANK_ACCOUNT(x) ((bank_account_t)((void *)(x)))
68 
69 ipc_voucher_attr_control_t  bank_voucher_attr_control;    /* communication channel from ATM to voucher system */
70 struct persona;
71 extern struct persona *system_persona, *proxy_system_persona;
72 uint32_t persona_get_id(struct persona *persona);
73 extern bool unique_persona;
74 
75 static ledger_template_t bank_ledger_template = NULL;
76 struct _bank_ledger_indices bank_ledgers = { .cpu_time = -1, .energy = -1 };
77 
78 static bank_task_t bank_task_alloc_init(task_t task);
79 static bank_account_t bank_account_alloc_init(bank_task_t bank_holder, bank_task_t bank_merchant,
80     bank_task_t bank_secureoriginator, bank_task_t bank_proximateprocess, struct thread_group* banktg, uint32_t persona_id);
81 static bank_task_t get_bank_task_context(task_t task, boolean_t initialize);
82 static void bank_task_dealloc(bank_task_t bank_task, mach_voucher_attr_value_reference_t sync);
83 static kern_return_t bank_account_dealloc_with_sync(bank_account_t bank_account, mach_voucher_attr_value_reference_t sync);
84 static void bank_rollup_chit_to_tasks(ledger_t bill, ledger_t bank_holder_ledger, ledger_t bank_merchant_ledger,
85     int bank_holder_pid, int bank_merchant_pid);
86 static ledger_t bank_get_bank_task_ledger_with_ref(bank_task_t bank_task);
87 static void bank_destroy_bank_task_ledger(bank_task_t bank_task);
88 static void init_bank_ledgers(void);
89 static boolean_t bank_task_is_propagate_entitled(task_t t);
90 static boolean_t bank_task_is_adopt_any_persona_entitled(task_t t __unused);
91 static boolean_t bank_task_is_persona_modify_entitled(task_t t);
92 static struct thread_group *bank_get_bank_task_thread_group(bank_task_t bank_task __unused);
93 static struct thread_group *bank_get_bank_account_thread_group(bank_account_t bank_account __unused);
94 static boolean_t bank_verify_persona_id(uint32_t persona_id);
95 static boolean_t bank_merchant_needs_persona_replacement(mach_voucher_attr_recipe_command_t command,
96     bank_task_t bank_task, uint32_t persona_id);
97 
98 /* lock to protect task->bank_context transition */
99 static LCK_GRP_DECLARE(bank_lock_grp, "bank_lock");
100 static LCK_ATTR_DECLARE(bank_lock_attr, 0, 0);
101 static LCK_SPIN_DECLARE_ATTR(g_bank_task_lock_data, &bank_lock_grp, &bank_lock_attr);
102 
103 static TUNABLE(bool, disable_persona_propagate_check,
104     "disable_persona_propagate_check", false);
105 
106 #define global_bank_task_lock() \
107 	lck_spin_lock_grp(&g_bank_task_lock_data, &bank_lock_grp)
108 #define global_bank_task_lock_try() \
109 	lck_spin_try_lock_grp(&g_bank_task_lock_data, &bank_lock_grp)
110 #define global_bank_task_unlock() \
111 	lck_spin_unlock(&g_bank_task_lock_data)
112 
113 extern uint64_t proc_uniqueid(void *p);
114 struct proc;
115 extern int32_t proc_pid(struct proc *p);
116 extern int32_t proc_pidversion(void *p);
117 extern uint32_t proc_persona_id(void *p);
118 extern uint32_t proc_getuid(void *p);
119 extern uint32_t proc_getgid(void *p);
120 extern void proc_getexecutableuuid(void *p, unsigned char *uuidbuf, unsigned long size);
121 extern int kauth_cred_issuser(void *cred);
122 extern void* kauth_cred_get(void);
123 extern void* persona_lookup(uint32_t id);
124 extern void persona_put(void* persona);
125 
126 kern_return_t
127 bank_release_value(
128 	ipc_voucher_attr_manager_t __assert_only manager,
129 	mach_voucher_attr_key_t __assert_only key,
130 	mach_voucher_attr_value_handle_t value,
131 	mach_voucher_attr_value_reference_t sync);
132 
133 kern_return_t
134 bank_get_value(
135 	ipc_voucher_attr_manager_t __assert_only manager,
136 	mach_voucher_attr_key_t __assert_only key,
137 	mach_voucher_attr_recipe_command_t command,
138 	mach_voucher_attr_value_handle_array_t prev_values,
139 	mach_msg_type_number_t __assert_only prev_value_count,
140 	mach_voucher_attr_content_t recipe,
141 	mach_voucher_attr_content_size_t recipe_size,
142 	mach_voucher_attr_value_handle_t *out_value,
143 	mach_voucher_attr_value_flags_t  *out_flags,
144 	ipc_voucher_t *out_value_voucher);
145 
146 kern_return_t
147 bank_extract_content(
148 	ipc_voucher_attr_manager_t __assert_only manager,
149 	mach_voucher_attr_key_t __assert_only key,
150 	mach_voucher_attr_value_handle_array_t values,
151 	mach_msg_type_number_t value_count,
152 	mach_voucher_attr_recipe_command_t *out_command,
153 	mach_voucher_attr_content_t out_recipe,
154 	mach_voucher_attr_content_size_t *in_out_recipe_size);
155 
156 kern_return_t
157 bank_command(
158 	ipc_voucher_attr_manager_t __assert_only manager,
159 	mach_voucher_attr_key_t __assert_only key,
160 	mach_voucher_attr_value_handle_array_t values,
161 	mach_msg_type_number_t value_count,
162 	mach_voucher_attr_command_t command,
163 	mach_voucher_attr_content_t in_content,
164 	mach_voucher_attr_content_size_t in_content_size,
165 	mach_voucher_attr_content_t out_content,
166 	mach_voucher_attr_content_size_t *in_out_content_size);
167 
168 void
169 bank_release(ipc_voucher_attr_manager_t __assert_only manager);
170 
171 /*
172  * communication channel from voucher system to ATM
173  */
174 const struct ipc_voucher_attr_manager bank_manager = {
175 	.ivam_release_value    = bank_release_value,
176 	.ivam_get_value        = bank_get_value,
177 	.ivam_extract_content  = bank_extract_content,
178 	.ivam_command          = bank_command,
179 	.ivam_release          = bank_release,
180 	.ivam_flags            = (IVAM_FLAGS_SUPPORT_SEND_PREPROCESS | IVAM_FLAGS_SUPPORT_RECEIVE_POSTPROCESS),
181 };
182 
183 
184 #if DEVELOPMENT || DEBUG
185 LCK_GRP_DECLARE(bank_dev_lock_grp, "bank_dev_lock");
186 LCK_MTX_DECLARE(bank_tasks_list_lock, &bank_dev_lock_grp);
187 LCK_MTX_DECLARE(bank_accounts_list_lock, &bank_dev_lock_grp);
188 queue_head_t bank_tasks_list = QUEUE_HEAD_INITIALIZER(bank_tasks_list);
189 queue_head_t bank_accounts_list = QUEUE_HEAD_INITIALIZER(bank_accounts_list);
190 #endif
191 
192 /*
193  * Routine: bank_init
194  * Purpose: Initialize the BANK subsystem.
195  * Returns: None.
196  */
197 void
bank_init()198 bank_init()
199 {
200 	kern_return_t kr = KERN_SUCCESS;
201 
202 	init_bank_ledgers();
203 
204 	/* Register the bank manager with the Vouchers sub system. */
205 	kr = ipc_register_well_known_mach_voucher_attr_manager(
206 		&bank_manager,
207 		0,
208 		MACH_VOUCHER_ATTR_KEY_BANK,
209 		&bank_voucher_attr_control);
210 	if (kr != KERN_SUCCESS) {
211 		panic("BANK subsystem initialization failed");
212 	}
213 
214 
215 	kprintf("BANK subsystem is initialized\n");
216 }
217 
218 
219 /*
220  * BANK Resource Manager Routines.
221  */
222 
223 
224 /*
225  * Routine: bank_release_value
226  * Purpose: Release a value, if sync matches the sync count in value.
227  * Returns: KERN_SUCCESS: on Successful deletion.
228  *          KERN_FAILURE: if sync value does not matches.
229  */
230 kern_return_t
bank_release_value(ipc_voucher_attr_manager_t __assert_only manager,mach_voucher_attr_key_t __assert_only key,mach_voucher_attr_value_handle_t value,mach_voucher_attr_value_reference_t sync)231 bank_release_value(
232 	ipc_voucher_attr_manager_t              __assert_only manager,
233 	mach_voucher_attr_key_t                 __assert_only key,
234 	mach_voucher_attr_value_handle_t                      value,
235 	mach_voucher_attr_value_reference_t               sync)
236 {
237 	bank_task_t bank_task = BANK_TASK_NULL;
238 	bank_element_t bank_element = BANK_ELEMENT_NULL;
239 	bank_account_t bank_account = BANK_ACCOUNT_NULL;
240 	kern_return_t kr = KERN_SUCCESS;
241 
242 	assert(MACH_VOUCHER_ATTR_KEY_BANK == key);
243 	assert(manager == &bank_manager);
244 
245 
246 	bank_element = HANDLE_TO_BANK_ELEMENT(value);
247 	/* Voucher system should never release the default or persistent value */
248 	assert(bank_element != BANK_DEFAULT_VALUE && bank_element != BANK_DEFAULT_TASK_VALUE);
249 
250 	if (bank_element == BANK_DEFAULT_VALUE || bank_element == BANK_DEFAULT_TASK_VALUE) {
251 		/* Return success for default and default task value */
252 		return KERN_SUCCESS;
253 	}
254 
255 
256 	if (bank_element->be_type == BANK_TASK) {
257 		bank_task = CAST_TO_BANK_TASK(bank_element);
258 
259 		/* Checking of the made ref with sync and clearing of voucher ref should be done under a lock */
260 		lck_mtx_lock(&bank_task->bt_acc_to_pay_lock);
261 		if (bank_task->bt_made != sync) {
262 			lck_mtx_unlock(&bank_task->bt_acc_to_pay_lock);
263 			return KERN_FAILURE;
264 		}
265 
266 		bank_task_made_release_num(bank_task, sync);
267 		assert(bank_task->bt_voucher_ref == 1);
268 		bank_task->bt_voucher_ref = 0;
269 		lck_mtx_unlock(&bank_task->bt_acc_to_pay_lock);
270 
271 		bank_task_dealloc(bank_task, 1);
272 	} else if (bank_element->be_type == BANK_ACCOUNT) {
273 		bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
274 		kr = bank_account_dealloc_with_sync(bank_account, sync);
275 	} else {
276 		panic("Bogus bank type: %d passed in get_value", bank_element->be_type);
277 	}
278 
279 	return kr;
280 }
281 
282 
283 /*
284  * Routine: bank_get_value
285  *
286  * This function uses the recipe to create a bank attribute for a voucher.
287  */
288 kern_return_t
bank_get_value(ipc_voucher_attr_manager_t __assert_only manager,mach_voucher_attr_key_t __assert_only key,mach_voucher_attr_recipe_command_t command,mach_voucher_attr_value_handle_array_t prev_values,mach_msg_type_number_t prev_value_count,mach_voucher_attr_content_t recipe,mach_voucher_attr_content_size_t recipe_size,mach_voucher_attr_value_handle_t * out_value,mach_voucher_attr_value_flags_t * out_flags,ipc_voucher_t * out_value_voucher)289 bank_get_value(
290 	ipc_voucher_attr_manager_t              __assert_only manager,
291 	mach_voucher_attr_key_t                 __assert_only key,
292 	mach_voucher_attr_recipe_command_t                command,
293 	mach_voucher_attr_value_handle_array_t        prev_values,
294 	mach_msg_type_number_t                        prev_value_count,
295 	mach_voucher_attr_content_t                   recipe,
296 	mach_voucher_attr_content_size_t              recipe_size,
297 	mach_voucher_attr_value_handle_t             *out_value,
298 	mach_voucher_attr_value_flags_t              *out_flags,
299 	ipc_voucher_t                                            *out_value_voucher)
300 {
301 	bank_task_t bank_holder = BANK_TASK_NULL;
302 	bank_task_t bank_merchant = BANK_TASK_NULL;
303 	bank_task_t bank_secureoriginator = BANK_TASK_NULL;
304 	bank_task_t bank_proximateprocess = BANK_TASK_NULL;
305 	bank_element_t bank_element = BANK_ELEMENT_NULL;
306 	bank_account_t bank_account = BANK_ACCOUNT_NULL;
307 	bank_account_t old_bank_account = BANK_ACCOUNT_NULL;
308 	mach_voucher_attr_value_handle_t bank_handle;
309 	task_t task;
310 	kern_return_t kr = KERN_SUCCESS;
311 	mach_msg_type_number_t i;
312 	struct thread_group *thread_group = NULL;
313 	struct thread_group *cur_thread_group = NULL;
314 	uint32_t persona_id = proc_persona_id(NULL);
315 
316 	assert(MACH_VOUCHER_ATTR_KEY_BANK == key);
317 	assert(manager == &bank_manager);
318 
319 	/* never an out voucher */
320 	*out_value_voucher = IPC_VOUCHER_NULL;
321 	*out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE;
322 
323 	switch (command) {
324 	case MACH_VOUCHER_ATTR_BANK_CREATE:
325 
326 		/* It returns the default task value. This value is replaced by
327 		 * an actual bank task reference, by using a recipe with
328 		 * MACH_VOUCHER_ATTR_SEND_PREPROCESS command.
329 		 */
330 		*out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE);
331 		*out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST;
332 		break;
333 
334 	case MACH_VOUCHER_ATTR_BANK_MODIFY_PERSONA:
335 
336 		/* It creates a bank account attribute value with a new persona id
337 		 * and auto-redeems it on behalf of the bank_holder.
338 		 */
339 		*out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE);
340 
341 		for (i = 0; i < prev_value_count; i++) {
342 			bank_handle = prev_values[i];
343 			bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
344 
345 			/* Expect a pre-processed attribute value */
346 			if (bank_element == BANK_DEFAULT_VALUE || bank_element == BANK_DEFAULT_TASK_VALUE) {
347 				continue;
348 			}
349 
350 			if (!bank_task_is_persona_modify_entitled(current_task())) {
351 				return KERN_NO_ACCESS;
352 			}
353 
354 			struct persona_modify_info pmi = {};
355 			if (recipe_size == sizeof(pmi)) {
356 				memcpy((void *)&pmi, recipe, sizeof(pmi));
357 				persona_id = pmi.persona_id;
358 			} else {
359 				return KERN_INVALID_ARGUMENT;
360 			}
361 
362 			/* Verify if the persona id is valid */
363 			if (!bank_verify_persona_id(persona_id)) {
364 				return KERN_INVALID_ARGUMENT;
365 			}
366 
367 			/* Update the persona id only if the bank element is a bank task.
368 			 * This ensures that the bank_holder can be trusted.
369 			 */
370 			if (bank_element->be_type == BANK_TASK) {
371 				bank_holder = CAST_TO_BANK_TASK(bank_element);
372 				/* Ensure that the requestor validated by userspace matches
373 				 * the bank_holder
374 				 */
375 				if (pmi.unique_pid != bank_holder->bt_unique_pid) {
376 					return KERN_INVALID_CAPABILITY;
377 				}
378 				bank_merchant = bank_holder;
379 				bank_secureoriginator = bank_holder;
380 				bank_proximateprocess = bank_holder;
381 				thread_group = bank_get_bank_task_thread_group(bank_holder);
382 			} else if (bank_element->be_type == BANK_ACCOUNT) {
383 				return KERN_INVALID_ARGUMENT;
384 			} else {
385 				panic("Bogus bank type: %d passed in get_value", bank_element->be_type);
386 			}
387 
388 			/* Check if the task was spawned in persona that allows adoption of different persona */
389 			if (bank_merchant_needs_persona_replacement(MACH_VOUCHER_ATTR_BANK_MODIFY_PERSONA,
390 			    bank_merchant, persona_id)) {
391 				return KERN_INVALID_ARGUMENT;
392 			}
393 
394 			if (bank_holder->bt_persona_id == persona_id) {
395 				lck_mtx_lock(&bank_holder->bt_acc_to_pay_lock);
396 				bank_task_made_reference(bank_holder);
397 				if (bank_holder->bt_voucher_ref == 0) {
398 					/* Take a ref for voucher system, if voucher system does not have a ref */
399 					bank_task_reference(bank_holder);
400 					bank_holder->bt_voucher_ref = 1;
401 				}
402 				lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock);
403 
404 				*out_value = BANK_ELEMENT_TO_HANDLE(bank_holder);
405 				return kr;
406 			}
407 
408 			bank_account = bank_account_alloc_init(bank_holder, bank_merchant,
409 			    bank_secureoriginator, bank_proximateprocess,
410 			    thread_group, persona_id);
411 			if (bank_account == BANK_ACCOUNT_NULL) {
412 				return KERN_RESOURCE_SHORTAGE;
413 			}
414 
415 			*out_value = BANK_ELEMENT_TO_HANDLE(bank_account);
416 			return kr;
417 		}
418 		break;
419 
420 	case MACH_VOUCHER_ATTR_AUTO_REDEEM:
421 
422 		/* It creates a bank account with the bank_merchant set to the current task.
423 		 * A bank attribute voucher needs to be redeemed before it can be adopted by
424 		 * it's threads.
425 		 */
426 		for (i = 0; i < prev_value_count; i++) {
427 			bank_handle = prev_values[i];
428 			bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
429 
430 			/* Should not have received default task value from an IPC */
431 			if (bank_element == BANK_DEFAULT_VALUE || bank_element == BANK_DEFAULT_TASK_VALUE) {
432 				continue;
433 			}
434 
435 			task = current_task();
436 			if (bank_element->be_type == BANK_TASK) {
437 				bank_holder = CAST_TO_BANK_TASK(bank_element);
438 				bank_secureoriginator = bank_holder;
439 				bank_proximateprocess = bank_holder;
440 				thread_group = bank_get_bank_task_thread_group(bank_holder);
441 				persona_id = bank_holder->bt_persona_id;
442 			} else if (bank_element->be_type == BANK_ACCOUNT) {
443 				old_bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
444 				bank_holder = old_bank_account->ba_holder;
445 				bank_secureoriginator = old_bank_account->ba_secureoriginator;
446 				bank_proximateprocess = old_bank_account->ba_proximateprocess;
447 				thread_group = bank_get_bank_account_thread_group(old_bank_account);
448 				persona_id = old_bank_account->ba_so_persona_id;
449 			} else {
450 				panic("Bogus bank type: %d passed in get_value", bank_element->be_type);
451 			}
452 
453 			bank_merchant = get_bank_task_context(task, FALSE);
454 			if (bank_merchant == BANK_TASK_NULL) {
455 				return KERN_RESOURCE_SHORTAGE;
456 			}
457 
458 			cur_thread_group = bank_get_bank_task_thread_group(bank_merchant);
459 
460 			/* Change voucher thread group to current thread group for Apps */
461 			if (task_is_app(task)) {
462 				thread_group = cur_thread_group;
463 			}
464 
465 			/*
466 			 * Change the persona-id to current task persona-id if the task
467 			 * is not spawned in system persona, on macOS, make an exception
468 			 * if the task is a platform binary and not spawned in any persona.
469 			 */
470 			if (bank_merchant_needs_persona_replacement(MACH_VOUCHER_ATTR_AUTO_REDEEM,
471 			    bank_merchant, PERSONA_ID_NONE)) {
472 				persona_id = bank_merchant->bt_persona_id;
473 			}
474 
475 			/* Check if trying to redeem for self task, return the default bank task */
476 			if (bank_holder == bank_merchant &&
477 			    bank_holder == bank_secureoriginator &&
478 			    bank_holder == bank_proximateprocess &&
479 			    thread_group == cur_thread_group &&
480 			    persona_id == bank_holder->bt_persona_id) {
481 				*out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE);
482 				*out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST;
483 				return kr;
484 			}
485 
486 			bank_account = bank_account_alloc_init(bank_holder, bank_merchant,
487 			    bank_secureoriginator, bank_proximateprocess,
488 			    thread_group, persona_id);
489 			if (bank_account == BANK_ACCOUNT_NULL) {
490 				return KERN_RESOURCE_SHORTAGE;
491 			}
492 
493 			*out_value = BANK_ELEMENT_TO_HANDLE(bank_account);
494 			return kr;
495 		}
496 
497 		*out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE);
498 		break;
499 
500 	case MACH_VOUCHER_ATTR_SEND_PREPROCESS:
501 
502 		for (i = 0; i < prev_value_count; i++) {
503 			bank_handle = prev_values[i];
504 			bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
505 
506 			if (bank_element == BANK_DEFAULT_VALUE) {
507 				continue;
508 			}
509 
510 			task = current_task();
511 			if (bank_element == BANK_DEFAULT_TASK_VALUE) {
512 				bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(task, FALSE));
513 			}
514 
515 			if (bank_element->be_type == BANK_TASK) {
516 				bank_holder = CAST_TO_BANK_TASK(bank_element);
517 				bank_secureoriginator = bank_holder;
518 				thread_group = bank_get_bank_task_thread_group(bank_holder);
519 				persona_id = bank_holder->bt_persona_id;
520 			} else if (bank_element->be_type == BANK_ACCOUNT) {
521 				old_bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
522 				bank_holder = old_bank_account->ba_holder;
523 				bank_secureoriginator = old_bank_account->ba_secureoriginator;
524 				thread_group = bank_get_bank_account_thread_group(old_bank_account);
525 				persona_id = old_bank_account->ba_so_persona_id;
526 			} else {
527 				panic("Bogus bank type: %d passed in get_value", bank_element->be_type);
528 			}
529 
530 			bank_merchant = get_bank_task_context(task, FALSE);
531 			if (bank_merchant == BANK_TASK_NULL) {
532 				return KERN_RESOURCE_SHORTAGE;
533 			}
534 
535 			cur_thread_group = bank_get_bank_task_thread_group(bank_merchant);
536 
537 			/*
538 			 * If the process doesn't have secure persona entitlement,
539 			 * then replace the secure originator to current task.
540 			 * Also update the persona_id to match that of the secure originator.
541 			 */
542 			if (bank_merchant->bt_hasentitlement == 0) {
543 				KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
544 				    (BANK_CODE(BANK_ACCOUNT_INFO, (BANK_SECURE_ORIGINATOR_CHANGED))) | DBG_FUNC_NONE,
545 				    bank_secureoriginator->bt_pid, bank_merchant->bt_pid, 0, 0, 0);
546 				bank_secureoriginator = bank_merchant;
547 				persona_id = bank_merchant->bt_persona_id;
548 			}
549 
550 			bank_proximateprocess = bank_merchant;
551 
552 			/* Check if trying to pre-process for self task, return the bank task */
553 			if (bank_holder == bank_merchant &&
554 			    bank_holder == bank_secureoriginator &&
555 			    bank_holder == bank_proximateprocess &&
556 			    thread_group == cur_thread_group &&
557 			    persona_id == bank_holder->bt_persona_id) {
558 				lck_mtx_lock(&bank_holder->bt_acc_to_pay_lock);
559 				bank_task_made_reference(bank_holder);
560 				if (bank_holder->bt_voucher_ref == 0) {
561 					/* Take a ref for voucher system, if voucher system does not have a ref */
562 					bank_task_reference(bank_holder);
563 					bank_holder->bt_voucher_ref = 1;
564 				}
565 				lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock);
566 
567 				*out_value = BANK_ELEMENT_TO_HANDLE(bank_holder);
568 				return kr;
569 			}
570 			bank_account = bank_account_alloc_init(bank_holder, bank_merchant,
571 			    bank_secureoriginator, bank_proximateprocess,
572 			    thread_group, persona_id);
573 			if (bank_account == BANK_ACCOUNT_NULL) {
574 				return KERN_RESOURCE_SHORTAGE;
575 			}
576 
577 			*out_value = BANK_ELEMENT_TO_HANDLE(bank_account);
578 			return kr;
579 		}
580 
581 		*out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE);
582 		break;
583 
584 	case MACH_VOUCHER_ATTR_REDEEM:
585 		/* This command expects that the bank attribute has been auto-redeemed
586 		 * and returns a reference to that bank account value.
587 		 */
588 		for (i = 0; i < prev_value_count; i++) {
589 			bank_handle = prev_values[i];
590 			bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
591 
592 			if (bank_element == BANK_DEFAULT_VALUE) {
593 				continue;
594 			}
595 
596 			if (bank_element == BANK_DEFAULT_TASK_VALUE) {
597 				*out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE);
598 				*out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST;
599 				return kr;
600 			}
601 
602 			task = current_task();
603 			if (bank_element->be_type == BANK_TASK) {
604 				bank_holder =  CAST_TO_BANK_TASK(bank_element);
605 				if (bank_holder == get_bank_task_context(task, FALSE)) {
606 					*out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE);
607 					*out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST;
608 				} else {
609 					kr = KERN_INVALID_CAPABILITY;
610 				}
611 				return kr;
612 			} else if (bank_element->be_type == BANK_ACCOUNT) {
613 				bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
614 				bank_merchant = bank_account->ba_merchant;
615 				if (bank_merchant != get_bank_task_context(task, FALSE)) {
616 					/* This error can be used to verify if the task can
617 					 * adopt the voucher.
618 					 */
619 					kr = KERN_INVALID_CAPABILITY;
620 					return kr;
621 				}
622 				bank_account_made_reference(bank_account);
623 				*out_value = BANK_ELEMENT_TO_HANDLE(bank_account);
624 				return kr;
625 			} else {
626 				panic("Bogus bank type: %d passed in get_value", bank_element->be_type);
627 			}
628 		}
629 
630 		*out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE);
631 		break;
632 
633 	default:
634 		kr = KERN_INVALID_ARGUMENT;
635 		break;
636 	}
637 
638 	return kr;
639 }
640 
641 
642 /*
643  * Routine: bank_extract_content
644  * Purpose: Extract a set of aid from an array of voucher values.
645  * Returns: KERN_SUCCESS: on Success.
646  *          KERN_FAILURE: one of the value is not present in the hash.
647  *          KERN_NO_SPACE: insufficeint buffer provided to fill an array of aid.
648  */
649 kern_return_t
bank_extract_content(ipc_voucher_attr_manager_t __assert_only manager,mach_voucher_attr_key_t __assert_only key,mach_voucher_attr_value_handle_array_t values,mach_msg_type_number_t value_count,mach_voucher_attr_recipe_command_t * out_command,mach_voucher_attr_content_t out_recipe,mach_voucher_attr_content_size_t * in_out_recipe_size)650 bank_extract_content(
651 	ipc_voucher_attr_manager_t      __assert_only manager,
652 	mach_voucher_attr_key_t         __assert_only key,
653 	mach_voucher_attr_value_handle_array_t        values,
654 	mach_msg_type_number_t                                value_count,
655 	mach_voucher_attr_recipe_command_t           *out_command,
656 	mach_voucher_attr_content_t                   out_recipe,
657 	mach_voucher_attr_content_size_t             *in_out_recipe_size)
658 {
659 	bank_task_t bank_task = BANK_TASK_NULL;
660 	bank_element_t bank_element = BANK_ELEMENT_NULL;
661 	bank_account_t bank_account = BANK_ACCOUNT_NULL;
662 	mach_voucher_attr_value_handle_t bank_handle;
663 	char buf[MACH_VOUCHER_BANK_CONTENT_SIZE];
664 	mach_msg_type_number_t i;
665 
666 	assert(MACH_VOUCHER_ATTR_KEY_BANK == key);
667 	assert(manager == &bank_manager);
668 
669 	for (i = 0; i < value_count && *in_out_recipe_size > 0; i++) {
670 		bank_handle = values[i];
671 		bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
672 		if (bank_element == BANK_DEFAULT_VALUE) {
673 			continue;
674 		}
675 
676 		if (bank_element == BANK_DEFAULT_TASK_VALUE) {
677 			bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
678 		}
679 
680 		if (MACH_VOUCHER_BANK_CONTENT_SIZE > *in_out_recipe_size) {
681 			*in_out_recipe_size = 0;
682 			return KERN_NO_SPACE;
683 		}
684 
685 		if (bank_element->be_type == BANK_TASK) {
686 			bank_task = CAST_TO_BANK_TASK(bank_element);
687 			snprintf(buf, MACH_VOUCHER_BANK_CONTENT_SIZE,
688 			    " Bank Context for a pid %d\n", bank_task->bt_pid);
689 		} else if (bank_element->be_type == BANK_ACCOUNT) {
690 			bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
691 			snprintf(buf, MACH_VOUCHER_BANK_CONTENT_SIZE,
692 			    " Bank Account linking holder pid %d with merchant pid %d, originator PID/persona: %d, %u and proximate PID/persona: %d, %u\n",
693 			    bank_account->ba_holder->bt_pid,
694 			    bank_account->ba_merchant->bt_pid,
695 			    bank_account->ba_secureoriginator->bt_pid,
696 			    bank_account->ba_so_persona_id,
697 			    bank_account->ba_proximateprocess->bt_pid,
698 			    bank_account->ba_proximateprocess->bt_persona_id);
699 		} else {
700 			panic("Bogus bank type: %d passed in get_value", bank_element->be_type);
701 		}
702 
703 		memcpy(&out_recipe[0], buf, strlen(buf) + 1);
704 		*out_command = MACH_VOUCHER_ATTR_BANK_NULL;
705 		*in_out_recipe_size = (mach_voucher_attr_content_size_t)strlen(buf) + 1;
706 		return KERN_SUCCESS;
707 	}
708 
709 	return KERN_SUCCESS;
710 }
711 
712 /*
713  * Routine: bank_command
714  * Purpose: Execute a command against a set of bank values.
715  * Returns: KERN_SUCCESS: On successful execution of command.
716  *           KERN_FAILURE: On failure.
717  */
718 kern_return_t
bank_command(ipc_voucher_attr_manager_t __assert_only manager,mach_voucher_attr_key_t __assert_only key,mach_voucher_attr_value_handle_array_t __unused values,mach_msg_type_number_t __unused value_count,mach_voucher_attr_command_t __unused command,mach_voucher_attr_content_t __unused in_content,mach_voucher_attr_content_size_t __unused in_content_size,mach_voucher_attr_content_t __unused out_content,mach_voucher_attr_content_size_t __unused * out_content_size)719 bank_command(
720 	ipc_voucher_attr_manager_t                 __assert_only manager,
721 	mach_voucher_attr_key_t                    __assert_only key,
722 	mach_voucher_attr_value_handle_array_t  __unused values,
723 	mach_msg_type_number_t                  __unused value_count,
724 	mach_voucher_attr_command_t              __unused command,
725 	mach_voucher_attr_content_t        __unused in_content,
726 	mach_voucher_attr_content_size_t   __unused in_content_size,
727 	mach_voucher_attr_content_t        __unused out_content,
728 	mach_voucher_attr_content_size_t   __unused *out_content_size)
729 {
730 	bank_task_t bank_task = BANK_TASK_NULL;
731 	bank_task_t bank_merchant = BANK_TASK_NULL;
732 	bank_task_t bank_secureoriginator = BANK_TASK_NULL;
733 	bank_task_t bank_proximateprocess = BANK_TASK_NULL;
734 	struct persona_token *token = NULL;
735 	bank_element_t bank_element = BANK_ELEMENT_NULL;
736 	bank_account_t bank_account = BANK_ACCOUNT_NULL;
737 	mach_voucher_attr_value_handle_t bank_handle;
738 	mach_msg_type_number_t i;
739 	task_t task;
740 	int32_t pid;
741 	uint32_t persona_id;
742 	boolean_t adopt_any_persona = TRUE;
743 
744 	assert(MACH_VOUCHER_ATTR_KEY_BANK == key);
745 	assert(manager == &bank_manager);
746 
747 	switch (command) {
748 	case BANK_ORIGINATOR_PID:
749 
750 		if ((sizeof(pid)) > *out_content_size) {
751 			*out_content_size = 0;
752 			return KERN_NO_SPACE;
753 		}
754 
755 		for (i = 0; i < value_count; i++) {
756 			bank_handle = values[i];
757 			bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
758 			if (bank_element == BANK_DEFAULT_VALUE) {
759 				continue;
760 			}
761 
762 			if (bank_element == BANK_DEFAULT_TASK_VALUE) {
763 				bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
764 			}
765 
766 			if (bank_element->be_type == BANK_TASK) {
767 				bank_task = CAST_TO_BANK_TASK(bank_element);
768 			} else if (bank_element->be_type == BANK_ACCOUNT) {
769 				bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
770 				bank_task = bank_account->ba_holder;
771 			} else {
772 				panic("Bogus bank type: %d passed in voucher_command", bank_element->be_type);
773 			}
774 			pid = bank_task->bt_pid;
775 
776 			memcpy(&out_content[0], &pid, sizeof(pid));
777 			*out_content_size = (mach_voucher_attr_content_size_t)sizeof(pid);
778 			return KERN_SUCCESS;
779 		}
780 		/* In the case of no value, return error KERN_INVALID_VALUE */
781 		*out_content_size = 0;
782 		return KERN_INVALID_VALUE;
783 
784 	case BANK_PERSONA_TOKEN:
785 
786 		if ((sizeof(struct persona_token)) > *out_content_size) {
787 			*out_content_size = 0;
788 			return KERN_NO_SPACE;
789 		}
790 		for (i = 0; i < value_count; i++) {
791 			bank_handle = values[i];
792 			bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
793 			if (bank_element == BANK_DEFAULT_VALUE) {
794 				continue;
795 			}
796 
797 			if (bank_element == BANK_DEFAULT_TASK_VALUE) {
798 				bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
799 			}
800 
801 			if (bank_element->be_type == BANK_TASK) {
802 				*out_content_size = 0;
803 				return KERN_INVALID_OBJECT;
804 			} else if (bank_element->be_type == BANK_ACCOUNT) {
805 				bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
806 				bank_secureoriginator = bank_account->ba_secureoriginator;
807 				bank_proximateprocess = bank_account->ba_proximateprocess;
808 			} else {
809 				panic("Bogus bank type: %d passed in voucher_command", bank_element->be_type);
810 			}
811 			token = (struct persona_token *)(void *)&out_content[0];
812 			memcpy(&token->originator, &bank_secureoriginator->bt_proc_persona, sizeof(struct proc_persona_info));
813 			memcpy(&token->proximate, &bank_proximateprocess->bt_proc_persona, sizeof(struct proc_persona_info));
814 
815 			*out_content_size = (mach_voucher_attr_content_size_t)sizeof(*token);
816 			return KERN_SUCCESS;
817 		}
818 		/* In the case of no value, return error KERN_INVALID_VALUE */
819 		*out_content_size = 0;
820 		return KERN_INVALID_VALUE;
821 
822 	case BANK_PERSONA_ID:
823 
824 		if ((sizeof(persona_id)) > *out_content_size) {
825 			*out_content_size = 0;
826 			return KERN_NO_SPACE;
827 		}
828 
829 		for (i = 0; i < value_count; i++) {
830 			bank_handle = values[i];
831 			bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
832 			if (bank_element == BANK_DEFAULT_VALUE) {
833 				continue;
834 			}
835 
836 			if (bank_element == BANK_DEFAULT_TASK_VALUE) {
837 				bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
838 			}
839 
840 			if (bank_element->be_type == BANK_TASK) {
841 				bank_task = CAST_TO_BANK_TASK(bank_element);
842 				persona_id = bank_task->bt_persona_id;
843 			} else if (bank_element->be_type == BANK_ACCOUNT) {
844 				bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
845 				persona_id = bank_account->ba_so_persona_id;
846 			} else {
847 				panic("Bogus bank type: %d passed in voucher_command", bank_element->be_type);
848 			}
849 
850 			memcpy(out_content, &persona_id, sizeof(persona_id));
851 			*out_content_size = (mach_voucher_attr_content_size_t)sizeof(persona_id);
852 			return KERN_SUCCESS;
853 		}
854 		/* In the case of no value, return error KERN_INVALID_VALUE */
855 		*out_content_size = 0;
856 		return KERN_INVALID_VALUE;
857 
858 	case BANK_PERSONA_ADOPT_ANY:
859 		if ((sizeof(boolean_t)) > *out_content_size) {
860 			*out_content_size = 0;
861 			return KERN_NO_SPACE;
862 		}
863 
864 		task = current_task();
865 		bank_merchant = get_bank_task_context(task, FALSE);
866 		if (bank_merchant == BANK_TASK_NULL) {
867 			*out_content_size = 0;
868 			return KERN_RESOURCE_SHORTAGE;
869 		}
870 
871 		if (bank_merchant_needs_persona_replacement(MACH_VOUCHER_ATTR_AUTO_REDEEM,
872 		    bank_merchant, PERSONA_ID_NONE)) {
873 			adopt_any_persona = FALSE;
874 		}
875 
876 		memcpy(out_content, &adopt_any_persona, sizeof(adopt_any_persona));
877 		*out_content_size = (mach_voucher_attr_content_size_t)sizeof(adopt_any_persona);
878 		return KERN_SUCCESS;
879 
880 	default:
881 		return KERN_INVALID_ARGUMENT;
882 	}
883 	return KERN_SUCCESS;
884 }
885 
886 
887 void
bank_release(ipc_voucher_attr_manager_t __assert_only manager)888 bank_release(
889 	ipc_voucher_attr_manager_t              __assert_only manager)
890 {
891 	assert(manager == &bank_manager);
892 }
893 
894 
895 
896 /*
897  * Bank Internal Routines.
898  */
899 
900 /*
901  * Routine: bank_task_alloc_init
902  * Purpose: Allocate and initialize a bank task structure.
903  * Returns: bank_task_t on Success.
904  *          BANK_TASK_NULL: on Failure.
905  * Notes:   Leaves the task and ledger blank and has only 1 ref,
906  *           needs to take 1 extra ref after the task field is initialized.
907  */
908 static bank_task_t
bank_task_alloc_init(task_t task)909 bank_task_alloc_init(task_t task)
910 {
911 	bank_task_t new_bank_task;
912 
913 	new_bank_task = zalloc_flags(bank_task_zone, Z_WAITOK | Z_NOFAIL);
914 
915 	new_bank_task->bt_type = BANK_TASK;
916 	new_bank_task->bt_voucher_ref = 0;
917 	new_bank_task->bt_refs = 1;
918 	new_bank_task->bt_made = 0;
919 	new_bank_task->bt_ledger = LEDGER_NULL;
920 	new_bank_task->bt_hasentitlement = !!bank_task_is_propagate_entitled(task);
921 	new_bank_task->bt_platform_binary = (task->t_flags & TF_PLATFORM) == TF_PLATFORM;
922 	new_bank_task->bt_adopt_any_entitled = !!bank_task_is_adopt_any_persona_entitled(task);
923 	queue_init(&new_bank_task->bt_accounts_to_pay);
924 	queue_init(&new_bank_task->bt_accounts_to_charge);
925 	lck_mtx_init(&new_bank_task->bt_acc_to_pay_lock, &bank_lock_grp, &bank_lock_attr);
926 	lck_mtx_init(&new_bank_task->bt_acc_to_charge_lock, &bank_lock_grp, &bank_lock_attr);
927 
928 	/*
929 	 * Initialize the persona_id struct
930 	 */
931 	bzero(&new_bank_task->bt_proc_persona, sizeof(new_bank_task->bt_proc_persona));
932 	new_bank_task->bt_flags = 0;
933 	new_bank_task->bt_unique_pid = proc_uniqueid(task->bsd_info);
934 	new_bank_task->bt_pid = proc_pid(task->bsd_info);
935 	new_bank_task->bt_pidversion = proc_pidversion(task->bsd_info);
936 	new_bank_task->bt_persona_id = proc_persona_id(task->bsd_info);
937 	new_bank_task->bt_uid = proc_getuid(task->bsd_info);
938 	new_bank_task->bt_gid = proc_getgid(task->bsd_info);
939 #if CONFIG_THREAD_GROUPS
940 	new_bank_task->bt_thread_group = thread_group_retain(task_coalition_get_thread_group(task));
941 #endif
942 	proc_getexecutableuuid(task->bsd_info, new_bank_task->bt_macho_uuid, sizeof(new_bank_task->bt_macho_uuid));
943 
944 #if DEVELOPMENT || DEBUG
945 	new_bank_task->bt_task = NULL;
946 	lck_mtx_lock(&bank_tasks_list_lock);
947 	queue_enter(&bank_tasks_list, new_bank_task, bank_task_t, bt_global_elt);
948 	lck_mtx_unlock(&bank_tasks_list_lock);
949 #endif
950 	return new_bank_task;
951 }
952 
953 /*
954  * Routine: proc_is_propagate_entitled
955  * Purpose: Check if the process is allowed to propagate secure originator.
956  * Returns: TRUE if entitled.
957  *          FALSE if not.
958  */
959 static boolean_t
bank_task_is_propagate_entitled(task_t t)960 bank_task_is_propagate_entitled(task_t t)
961 {
962 	/* Check if it has an entitlement which disallows secure originator propagation */
963 	boolean_t entitled = FALSE;
964 	entitled = IOTaskHasEntitlement(t, ENTITLEMENT_PERSONA_NO_PROPAGATE);
965 	if (entitled) {
966 		return FALSE;
967 	}
968 
969 	/* If it's a platform binary, allow propagation by default */
970 	if (disable_persona_propagate_check || (t->t_flags & TF_PLATFORM)) {
971 		return TRUE;
972 	}
973 
974 	return FALSE;
975 }
976 
977 /*
978  * Routine: bank_task_is_adopt_any_persona_entitled
979  * Purpose: Check if the process is entitled to adopt any persona.
980  * Returns: TRUE if entitled.
981  *          FALSE if not.
982  */
983 static boolean_t
bank_task_is_adopt_any_persona_entitled(task_t t __unused)984 bank_task_is_adopt_any_persona_entitled(task_t t __unused)
985 {
986 	boolean_t entitled = FALSE;
987 #if defined(XNU_TARGET_OS_OSX)
988 	entitled = IOTaskHasEntitlement(t, ENTITLEMENT_PERSONA_ADOPT_ANY);
989 #endif
990 	return entitled;
991 }
992 
993 /*
994  * Routine: proc_is_persona_modify_entitled
995  * Purpose: Check if the process has persona modify entitlement.
996  * Returns: TRUE if entitled.
997  *          FALSE if not.
998  */
999 static boolean_t
bank_task_is_persona_modify_entitled(task_t t)1000 bank_task_is_persona_modify_entitled(task_t t)
1001 {
1002 	boolean_t entitled = FALSE;
1003 	entitled = IOTaskHasEntitlement(t, ENTITLEMENT_PERSONA_MODIFY);
1004 	return entitled;
1005 }
1006 
1007 /*
1008  * Routine: bank_account_alloc_init
1009  * Purpose: Allocate and Initialize the bank account struct.
1010  * Returns: bank_account_t : On Success.
1011  *          BANK_ACCOUNT_NULL: On Failure.
1012  */
1013 static bank_account_t
bank_account_alloc_init(bank_task_t bank_holder,bank_task_t bank_merchant,bank_task_t bank_secureoriginator,bank_task_t bank_proximateprocess,struct thread_group * thread_group,uint32_t persona_id)1014 bank_account_alloc_init(
1015 	bank_task_t bank_holder,
1016 	bank_task_t bank_merchant,
1017 	bank_task_t bank_secureoriginator,
1018 	bank_task_t bank_proximateprocess,
1019 	struct thread_group *thread_group,
1020 	uint32_t persona_id)
1021 {
1022 	bank_account_t new_bank_account;
1023 	bank_account_t bank_account;
1024 	boolean_t entry_found = FALSE;
1025 	ledger_t new_ledger = ledger_instantiate(bank_ledger_template, LEDGER_CREATE_INACTIVE_ENTRIES);
1026 
1027 	if (new_ledger == LEDGER_NULL) {
1028 		return BANK_ACCOUNT_NULL;
1029 	}
1030 
1031 	ledger_entry_setactive(new_ledger, bank_ledgers.cpu_time);
1032 	ledger_entry_setactive(new_ledger, bank_ledgers.energy);
1033 	new_bank_account = zalloc_flags(bank_account_zone, Z_WAITOK | Z_NOFAIL);
1034 
1035 	new_bank_account->ba_type = BANK_ACCOUNT;
1036 	new_bank_account->ba_voucher_ref = 0;
1037 	new_bank_account->ba_refs = 1;
1038 	new_bank_account->ba_made = 1;
1039 	new_bank_account->ba_bill = new_ledger;
1040 	new_bank_account->ba_merchant = bank_merchant;
1041 	new_bank_account->ba_holder = bank_holder;
1042 	new_bank_account->ba_secureoriginator = bank_secureoriginator;
1043 	new_bank_account->ba_proximateprocess = bank_proximateprocess;
1044 #if CONFIG_THREAD_GROUPS
1045 	new_bank_account->ba_thread_group = thread_group;
1046 #endif
1047 	new_bank_account->ba_so_persona_id = persona_id;
1048 
1049 	/* Iterate through accounts need to pay list to find the existing entry */
1050 	lck_mtx_lock(&bank_holder->bt_acc_to_pay_lock);
1051 	queue_iterate(&bank_holder->bt_accounts_to_pay, bank_account, bank_account_t, ba_next_acc_to_pay) {
1052 		if (bank_account->ba_merchant != bank_merchant ||
1053 		    bank_account->ba_secureoriginator != bank_secureoriginator ||
1054 		    bank_account->ba_proximateprocess != bank_proximateprocess ||
1055 		    bank_get_bank_account_thread_group(bank_account) != thread_group ||
1056 		    bank_account->ba_so_persona_id != persona_id) {
1057 			continue;
1058 		}
1059 
1060 		entry_found = TRUE;
1061 		/* Take a made ref, since this value would be returned to voucher system. */
1062 		bank_account_made_reference(bank_account);
1063 		break;
1064 	}
1065 
1066 	if (!entry_found) {
1067 		/*  Create a linkage between the holder and the merchant task, Grab both the list locks before adding it to the list. */
1068 		lck_mtx_lock(&bank_merchant->bt_acc_to_charge_lock);
1069 
1070 		/* Add the account entry into Accounts need to pay account link list. */
1071 		queue_enter(&bank_holder->bt_accounts_to_pay, new_bank_account, bank_account_t, ba_next_acc_to_pay);
1072 
1073 		/* Add the account entry into Accounts need to charge account link list. */
1074 		queue_enter(&bank_merchant->bt_accounts_to_charge, new_bank_account, bank_account_t, ba_next_acc_to_charge);
1075 
1076 		lck_mtx_unlock(&bank_merchant->bt_acc_to_charge_lock);
1077 	}
1078 
1079 	lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock);
1080 
1081 	if (entry_found) {
1082 		ledger_dereference(new_ledger);
1083 		zfree(bank_account_zone, new_bank_account);
1084 		return bank_account;
1085 	}
1086 
1087 	bank_task_reference(bank_holder);
1088 	bank_task_reference(bank_merchant);
1089 	bank_task_reference(bank_secureoriginator);
1090 	bank_task_reference(bank_proximateprocess);
1091 #if CONFIG_THREAD_GROUPS
1092 	assert(new_bank_account->ba_thread_group != NULL);
1093 	thread_group_retain(new_bank_account->ba_thread_group);
1094 #endif
1095 
1096 #if DEVELOPMENT || DEBUG
1097 	new_bank_account->ba_task = NULL;
1098 	lck_mtx_lock(&bank_accounts_list_lock);
1099 	queue_enter(&bank_accounts_list, new_bank_account, bank_account_t, ba_global_elt);
1100 	lck_mtx_unlock(&bank_accounts_list_lock);
1101 #endif
1102 
1103 	return new_bank_account;
1104 }
1105 
1106 /*
1107  * Routine: get_bank_task_context
1108  * Purpose: Get the bank context of the given task
1109  * Returns: bank_task_t on Success.
1110  *          BANK_TASK_NULL: on Failure.
1111  * Note:    Initialize bank context if NULL.
1112  */
1113 static bank_task_t
get_bank_task_context(task_t task,boolean_t initialize)1114 get_bank_task_context
1115 (task_t task,
1116     boolean_t initialize)
1117 {
1118 	bank_task_t bank_task;
1119 
1120 	if (task->bank_context || !initialize) {
1121 		assert(task->bank_context != NULL);
1122 		return task->bank_context;
1123 	}
1124 
1125 	bank_task = bank_task_alloc_init(task);
1126 
1127 	/* Grab the task lock and check if we won the race. */
1128 	task_lock(task);
1129 	if (task->bank_context) {
1130 		task_unlock(task);
1131 		if (bank_task != BANK_TASK_NULL) {
1132 			bank_task_dealloc(bank_task, 1);
1133 		}
1134 		return task->bank_context;
1135 	} else if (bank_task == BANK_TASK_NULL) {
1136 		task_unlock(task);
1137 		return BANK_TASK_NULL;
1138 	}
1139 	/* We won the race. Take a ref on the ledger and initialize bank task. */
1140 	bank_task->bt_ledger = task->ledger;
1141 #if DEVELOPMENT || DEBUG
1142 	bank_task->bt_task = task;
1143 #endif
1144 	ledger_reference(task->ledger);
1145 
1146 	/* Grab the global bank task lock before setting the bank context on a task */
1147 	global_bank_task_lock();
1148 	task->bank_context = bank_task;
1149 	global_bank_task_unlock();
1150 
1151 	task_unlock(task);
1152 
1153 	return bank_task;
1154 }
1155 
1156 /*
1157  * Routine: bank_task_dealloc
1158  * Purpose: Drops the reference on bank task.
1159  * Returns: None.
1160  */
1161 static void
bank_task_dealloc(bank_task_t bank_task,mach_voucher_attr_value_reference_t sync)1162 bank_task_dealloc(
1163 	bank_task_t bank_task,
1164 	mach_voucher_attr_value_reference_t sync)
1165 {
1166 	assert(bank_task->bt_refs >= 0);
1167 
1168 	if (bank_task_release_num(bank_task, sync) > (int)sync) {
1169 		return;
1170 	}
1171 
1172 	assert(bank_task->bt_refs == 0);
1173 	assert(queue_empty(&bank_task->bt_accounts_to_pay));
1174 	assert(queue_empty(&bank_task->bt_accounts_to_charge));
1175 
1176 	assert(!LEDGER_VALID(bank_task->bt_ledger));
1177 	lck_mtx_destroy(&bank_task->bt_acc_to_pay_lock, &bank_lock_grp);
1178 	lck_mtx_destroy(&bank_task->bt_acc_to_charge_lock, &bank_lock_grp);
1179 
1180 #if CONFIG_THREAD_GROUPS
1181 	thread_group_release(bank_task->bt_thread_group);
1182 #endif
1183 
1184 #if DEVELOPMENT || DEBUG
1185 	lck_mtx_lock(&bank_tasks_list_lock);
1186 	queue_remove(&bank_tasks_list, bank_task, bank_task_t, bt_global_elt);
1187 	lck_mtx_unlock(&bank_tasks_list_lock);
1188 #endif
1189 
1190 	zfree(bank_task_zone, bank_task);
1191 }
1192 
1193 /*
1194  * Routine: bank_account_dealloc_with_sync
1195  * Purpose: Drop the reference on bank account if the sync matches.
1196  * Returns: KERN_SUCCESS if sync matches.
1197  *          KERN_FAILURE on mismatch.
1198  */
1199 static kern_return_t
bank_account_dealloc_with_sync(bank_account_t bank_account,mach_voucher_attr_value_reference_t sync)1200 bank_account_dealloc_with_sync(
1201 	bank_account_t bank_account,
1202 	mach_voucher_attr_value_reference_t sync)
1203 {
1204 	bank_task_t bank_holder = bank_account->ba_holder;
1205 	bank_task_t bank_merchant = bank_account->ba_merchant;
1206 	bank_task_t bank_secureoriginator = bank_account->ba_secureoriginator;
1207 	bank_task_t bank_proximateprocess = bank_account->ba_proximateprocess;
1208 	ledger_t bank_merchant_ledger = LEDGER_NULL;
1209 
1210 	/*
1211 	 * Grab a reference on the bank_merchant_ledger, since we would not be able
1212 	 * to take bt_acc_to_pay_lock for bank_merchant later.
1213 	 */
1214 	bank_merchant_ledger = bank_get_bank_task_ledger_with_ref(bank_merchant);
1215 
1216 	/* Grab the acc to pay list lock and check the sync value */
1217 	lck_mtx_lock(&bank_holder->bt_acc_to_pay_lock);
1218 
1219 	if (bank_account->ba_made != sync) {
1220 		lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock);
1221 		if (bank_merchant_ledger) {
1222 			ledger_dereference(bank_merchant_ledger);
1223 		}
1224 		return KERN_FAILURE;
1225 	}
1226 
1227 	bank_account_made_release_num(bank_account, sync);
1228 
1229 	if (bank_account_release_num(bank_account, 1) > 1) {
1230 		panic("Releasing a non zero ref bank account %p", bank_account);
1231 	}
1232 
1233 
1234 	/* Grab both the acc to pay and acc to charge locks */
1235 	lck_mtx_lock(&bank_merchant->bt_acc_to_charge_lock);
1236 
1237 	/* No need to take ledger reference for bank_holder ledger since bt_acc_to_pay_lock is locked */
1238 	bank_rollup_chit_to_tasks(bank_account->ba_bill, bank_holder->bt_ledger, bank_merchant_ledger,
1239 	    bank_holder->bt_pid, bank_merchant->bt_pid);
1240 
1241 	/* Remove the account entry from Accounts need to pay account link list. */
1242 	queue_remove(&bank_holder->bt_accounts_to_pay, bank_account, bank_account_t, ba_next_acc_to_pay);
1243 
1244 	/* Remove the account entry from Accounts need to charge account link list. */
1245 	queue_remove(&bank_merchant->bt_accounts_to_charge, bank_account, bank_account_t, ba_next_acc_to_charge);
1246 
1247 	lck_mtx_unlock(&bank_merchant->bt_acc_to_charge_lock);
1248 	lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock);
1249 
1250 	if (bank_merchant_ledger) {
1251 		ledger_dereference(bank_merchant_ledger);
1252 	}
1253 	ledger_dereference(bank_account->ba_bill);
1254 
1255 	/* Drop the reference of bank holder and merchant */
1256 	bank_task_dealloc(bank_holder, 1);
1257 	bank_task_dealloc(bank_merchant, 1);
1258 	bank_task_dealloc(bank_secureoriginator, 1);
1259 	bank_task_dealloc(bank_proximateprocess, 1);
1260 #if CONFIG_THREAD_GROUPS
1261 	assert(bank_account->ba_thread_group != NULL);
1262 	thread_group_release(bank_account->ba_thread_group);
1263 #endif
1264 
1265 #if DEVELOPMENT || DEBUG
1266 	lck_mtx_lock(&bank_accounts_list_lock);
1267 	queue_remove(&bank_accounts_list, bank_account, bank_account_t, ba_global_elt);
1268 	lck_mtx_unlock(&bank_accounts_list_lock);
1269 #endif
1270 
1271 	zfree(bank_account_zone, bank_account);
1272 	return KERN_SUCCESS;
1273 }
1274 
1275 /*
1276  * Routine: bank_rollup_chit_to_tasks
1277  * Purpose: Debit and Credit holder's and merchant's ledgers.
1278  * Returns: None.
1279  */
1280 static void
bank_rollup_chit_to_tasks(ledger_t bill,ledger_t bank_holder_ledger,ledger_t bank_merchant_ledger,int bank_holder_pid,int bank_merchant_pid)1281 bank_rollup_chit_to_tasks(
1282 	ledger_t bill,
1283 	ledger_t bank_holder_ledger,
1284 	ledger_t bank_merchant_ledger,
1285 	int bank_holder_pid,
1286 	int bank_merchant_pid)
1287 {
1288 	ledger_amount_t credit;
1289 	ledger_amount_t debit;
1290 	kern_return_t ret;
1291 
1292 	if (bank_holder_ledger == bank_merchant_ledger) {
1293 		return;
1294 	}
1295 
1296 	ret = ledger_get_entries(bill, bank_ledgers.cpu_time, &credit, &debit);
1297 	if (ret == KERN_SUCCESS) {
1298 		KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
1299 		    (BANK_CODE(BANK_ACCOUNT_INFO, (BANK_SETTLE_CPU_TIME))) | DBG_FUNC_NONE,
1300 		    bank_merchant_pid, bank_holder_pid, credit, debit, 0);
1301 
1302 		if (bank_holder_ledger) {
1303 			ledger_credit(bank_holder_ledger, task_ledgers.cpu_time_billed_to_me, credit);
1304 			ledger_debit(bank_holder_ledger, task_ledgers.cpu_time_billed_to_me, debit);
1305 		}
1306 
1307 		if (bank_merchant_ledger) {
1308 			ledger_credit(bank_merchant_ledger, task_ledgers.cpu_time_billed_to_others, credit);
1309 			ledger_debit(bank_merchant_ledger, task_ledgers.cpu_time_billed_to_others, debit);
1310 		}
1311 	}
1312 
1313 	ret = ledger_get_entries(bill, bank_ledgers.energy, &credit, &debit);
1314 	if (ret == KERN_SUCCESS) {
1315 		KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
1316 		    (BANK_CODE(BANK_ACCOUNT_INFO, (BANK_SETTLE_ENERGY))) | DBG_FUNC_NONE,
1317 		    bank_merchant_pid, bank_holder_pid, credit, debit, 0);
1318 
1319 		if (bank_holder_ledger) {
1320 			ledger_credit(bank_holder_ledger, task_ledgers.energy_billed_to_me, credit);
1321 			ledger_debit(bank_holder_ledger, task_ledgers.energy_billed_to_me, debit);
1322 		}
1323 
1324 		if (bank_merchant_ledger) {
1325 			ledger_credit(bank_merchant_ledger, task_ledgers.energy_billed_to_others, credit);
1326 			ledger_debit(bank_merchant_ledger, task_ledgers.energy_billed_to_others, debit);
1327 		}
1328 	}
1329 }
1330 
1331 
1332 
1333 /*
1334  * Routine: bank_task_destroy
1335  * Purpose: Drops reference on bank task.
1336  * Returns: None.
1337  */
1338 void
bank_task_destroy(task_t task)1339 bank_task_destroy(task_t task)
1340 {
1341 	bank_task_t bank_task;
1342 
1343 	/* Grab the global bank task lock before dropping the ref on task bank context */
1344 	global_bank_task_lock();
1345 	bank_task = task->bank_context;
1346 	task->bank_context = NULL;
1347 	global_bank_task_unlock();
1348 
1349 	bank_destroy_bank_task_ledger(bank_task);
1350 	bank_task_dealloc(bank_task, 1);
1351 }
1352 
1353 /*
1354  * Routine: bank_task_initialize
1355  * Purpose: Initialize the bank context of a task.
1356  * Returns: None.
1357  */
1358 void
bank_task_initialize(task_t task)1359 bank_task_initialize(task_t task)
1360 {
1361 	get_bank_task_context(task, TRUE);
1362 }
1363 
1364 /*
1365  * Routine: init_bank_ledgers
1366  * Purpose: Initialize template for bank ledgers.
1367  * Returns: None.
1368  */
1369 static void
init_bank_ledgers(void)1370 init_bank_ledgers(void)
1371 {
1372 	ledger_template_t t;
1373 	int idx;
1374 
1375 	assert(bank_ledger_template == NULL);
1376 
1377 	if ((t = ledger_template_create("Bank ledger")) == NULL) {
1378 		panic("couldn't create bank ledger template");
1379 	}
1380 
1381 	if ((idx = ledger_entry_add(t, "cpu_time", "sched", "ns")) < 0) {
1382 		panic("couldn't create cpu_time entry for bank ledger template");
1383 	}
1384 	bank_ledgers.cpu_time = idx;
1385 
1386 	if ((idx = ledger_entry_add(t, "energy", "power", "nj")) < 0) {
1387 		panic("couldn't create energy entry for bank ledger template");
1388 	}
1389 	bank_ledgers.energy = idx;
1390 
1391 	ledger_template_complete(t);
1392 	bank_ledger_template = t;
1393 }
1394 
1395 /* Routine: bank_billed_balance_safe
1396  * Purpose: Walk through all the bank accounts billed to me by other tasks and get the current billing balance.
1397  *          Called from another task. It takes global bank task lock to make sure the bank context is
1398  *           not deallocated while accesing it.
1399  * Returns: cpu balance and energy balance in out paremeters.
1400  */
1401 void
bank_billed_balance_safe(task_t task,uint64_t * cpu_time,uint64_t * energy)1402 bank_billed_balance_safe(task_t task, uint64_t *cpu_time, uint64_t *energy)
1403 {
1404 	bank_task_t bank_task = BANK_TASK_NULL;
1405 	ledger_amount_t credit, debit;
1406 	uint64_t cpu_balance = 0;
1407 	uint64_t energy_balance = 0;
1408 	kern_return_t kr;
1409 
1410 	/* Task might be in exec, grab the global bank task lock before accessing bank context. */
1411 	global_bank_task_lock();
1412 	/* Grab a reference on bank context */
1413 	if (task->bank_context != NULL) {
1414 		bank_task = task->bank_context;
1415 		bank_task_reference(bank_task);
1416 	}
1417 	global_bank_task_unlock();
1418 
1419 	if (bank_task) {
1420 		bank_billed_balance(bank_task, &cpu_balance, &energy_balance);
1421 		bank_task_dealloc(bank_task, 1);
1422 	} else {
1423 		kr = ledger_get_entries(task->ledger, task_ledgers.cpu_time_billed_to_me,
1424 		    &credit, &debit);
1425 		if (kr == KERN_SUCCESS) {
1426 			cpu_balance = credit - debit;
1427 		}
1428 		kr = ledger_get_entries(task->ledger, task_ledgers.energy_billed_to_me,
1429 		    &credit, &debit);
1430 		if (kr == KERN_SUCCESS) {
1431 			energy_balance = credit - debit;
1432 		}
1433 	}
1434 
1435 	*cpu_time = cpu_balance;
1436 	*energy = energy_balance;
1437 	return;
1438 }
1439 
1440 /*
1441  * Routine: bank_billed_time
1442  * Purpose: Walk through the Accounts need to pay account list and get the current billing balance.
1443  * Returns: cpu balance and energy balance in out paremeters.
1444  */
1445 void
bank_billed_balance(bank_task_t bank_task,uint64_t * cpu_time,uint64_t * energy)1446 bank_billed_balance(bank_task_t bank_task, uint64_t *cpu_time, uint64_t *energy)
1447 {
1448 	int64_t cpu_balance = 0;
1449 	int64_t energy_balance = 0;
1450 	bank_account_t bank_account;
1451 	int64_t temp = 0;
1452 	kern_return_t kr;
1453 	if (bank_task == BANK_TASK_NULL) {
1454 		*cpu_time = 0;
1455 		*energy = 0;
1456 		return;
1457 	}
1458 
1459 	lck_mtx_lock(&bank_task->bt_acc_to_pay_lock);
1460 
1461 	/* bt_acc_to_pay_lock locked, no need to take ledger reference for bt_ledger */
1462 	if (bank_task->bt_ledger != LEDGER_NULL) {
1463 		kr = ledger_get_balance(bank_task->bt_ledger, task_ledgers.cpu_time_billed_to_me, &temp);
1464 		if (kr == KERN_SUCCESS && temp >= 0) {
1465 			cpu_balance += temp;
1466 		}
1467 #if DEVELOPMENT || DEBUG
1468 		else {
1469 			printf("bank_bill_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp);
1470 		}
1471 #endif /* DEVELOPMENT || DEBUG */
1472 
1473 		kr = ledger_get_balance(bank_task->bt_ledger, task_ledgers.energy_billed_to_me, &temp);
1474 		if (kr == KERN_SUCCESS && temp >= 0) {
1475 			energy_balance += temp;
1476 		}
1477 	}
1478 
1479 	queue_iterate(&bank_task->bt_accounts_to_pay, bank_account, bank_account_t, ba_next_acc_to_pay) {
1480 		temp = 0;
1481 		kr = ledger_get_balance(bank_account->ba_bill, bank_ledgers.cpu_time, &temp);
1482 		if (kr == KERN_SUCCESS && temp >= 0) {
1483 			cpu_balance += temp;
1484 		}
1485 #if DEVELOPMENT || DEBUG
1486 		else {
1487 			printf("bank_bill_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp);
1488 		}
1489 #endif /* DEVELOPMENT || DEBUG */
1490 
1491 		kr = ledger_get_balance(bank_account->ba_bill, bank_ledgers.energy, &temp);
1492 		if (kr == KERN_SUCCESS && temp >= 0) {
1493 			energy_balance += temp;
1494 		}
1495 	}
1496 	lck_mtx_unlock(&bank_task->bt_acc_to_pay_lock);
1497 	*cpu_time = (uint64_t)cpu_balance;
1498 	*energy = (uint64_t)energy_balance;
1499 	return;
1500 }
1501 
1502 /* Routine: bank_serviced_balance_safe
1503  * Purpose: Walk through the bank accounts billed to other tasks by me and get the current balance to be charged.
1504  *          Called from another task. It takes global bank task lock to make sure the bank context is
1505  *           not deallocated while accesing it.
1506  * Returns: cpu balance and energy balance in out paremeters.
1507  */
1508 void
bank_serviced_balance_safe(task_t task,uint64_t * cpu_time,uint64_t * energy)1509 bank_serviced_balance_safe(task_t task, uint64_t *cpu_time, uint64_t *energy)
1510 {
1511 	bank_task_t bank_task = BANK_TASK_NULL;
1512 	ledger_amount_t credit, debit;
1513 	uint64_t cpu_balance = 0;
1514 	uint64_t energy_balance = 0;
1515 	kern_return_t kr;
1516 
1517 	/* Task might be in exec, grab the global bank task lock before accessing bank context. */
1518 	global_bank_task_lock();
1519 	/* Grab a reference on bank context */
1520 	if (task->bank_context != NULL) {
1521 		bank_task = task->bank_context;
1522 		bank_task_reference(bank_task);
1523 	}
1524 	global_bank_task_unlock();
1525 
1526 	if (bank_task) {
1527 		bank_serviced_balance(bank_task, &cpu_balance, &energy_balance);
1528 		bank_task_dealloc(bank_task, 1);
1529 	} else {
1530 		kr = ledger_get_entries(task->ledger, task_ledgers.cpu_time_billed_to_others,
1531 		    &credit, &debit);
1532 		if (kr == KERN_SUCCESS) {
1533 			cpu_balance = credit - debit;
1534 		}
1535 
1536 		kr = ledger_get_entries(task->ledger, task_ledgers.energy_billed_to_others,
1537 		    &credit, &debit);
1538 		if (kr == KERN_SUCCESS) {
1539 			energy_balance = credit - debit;
1540 		}
1541 	}
1542 
1543 	*cpu_time = cpu_balance;
1544 	*energy = energy_balance;
1545 	return;
1546 }
1547 
1548 /*
1549  * Routine: bank_serviced_balance
1550  * Purpose: Walk through the Account need to charge account list and get the current balance to be charged.
1551  * Returns: cpu balance and energy balance in out paremeters.
1552  */
1553 void
bank_serviced_balance(bank_task_t bank_task,uint64_t * cpu_time,uint64_t * energy)1554 bank_serviced_balance(bank_task_t bank_task, uint64_t *cpu_time, uint64_t *energy)
1555 {
1556 	int64_t cpu_balance = 0;
1557 	int64_t energy_balance = 0;
1558 	bank_account_t bank_account;
1559 	int64_t temp = 0;
1560 	kern_return_t kr;
1561 	ledger_t ledger = LEDGER_NULL;
1562 	if (bank_task == BANK_TASK_NULL) {
1563 		*cpu_time = 0;
1564 		*energy = 0;
1565 		return;
1566 	}
1567 
1568 	/* Grab a ledger reference on bt_ledger for bank_task */
1569 	ledger = bank_get_bank_task_ledger_with_ref(bank_task);
1570 
1571 	lck_mtx_lock(&bank_task->bt_acc_to_charge_lock);
1572 
1573 	if (ledger) {
1574 		kr = ledger_get_balance(ledger, task_ledgers.cpu_time_billed_to_others, &temp);
1575 		if (kr == KERN_SUCCESS && temp >= 0) {
1576 			cpu_balance += temp;
1577 		}
1578 #if DEVELOPMENT || DEBUG
1579 		else {
1580 			printf("bank_serviced_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp);
1581 		}
1582 #endif /* DEVELOPMENT || DEBUG */
1583 
1584 		kr = ledger_get_balance(ledger, task_ledgers.energy_billed_to_others, &temp);
1585 		if (kr == KERN_SUCCESS && temp >= 0) {
1586 			energy_balance += temp;
1587 		}
1588 	}
1589 
1590 	queue_iterate(&bank_task->bt_accounts_to_charge, bank_account, bank_account_t, ba_next_acc_to_charge) {
1591 		temp = 0;
1592 		kr = ledger_get_balance(bank_account->ba_bill, bank_ledgers.cpu_time, &temp);
1593 		if (kr == KERN_SUCCESS && temp >= 0) {
1594 			cpu_balance += temp;
1595 		}
1596 #if DEVELOPMENT || DEBUG
1597 		else {
1598 			printf("bank_serviced_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp);
1599 		}
1600 #endif /* DEVELOPMENT || DEBUG */
1601 
1602 		kr = ledger_get_balance(bank_account->ba_bill, bank_ledgers.energy, &temp);
1603 		if (kr == KERN_SUCCESS && temp >= 0) {
1604 			energy_balance += temp;
1605 		}
1606 	}
1607 	lck_mtx_unlock(&bank_task->bt_acc_to_charge_lock);
1608 	if (ledger) {
1609 		ledger_dereference(ledger);
1610 	}
1611 	*cpu_time = (uint64_t)cpu_balance;
1612 	*energy = (uint64_t)energy_balance;
1613 	return;
1614 }
1615 
1616 /*
1617  * Routine: bank_get_voucher_bank_account
1618  * Purpose: Get the bank account from the voucher.
1619  * Returns: bank_account if bank_account attribute present in voucher.
1620  *          NULL on no attribute or no bank_element
1621  */
1622 static bank_account_t
bank_get_voucher_bank_account(ipc_voucher_t voucher)1623 bank_get_voucher_bank_account(ipc_voucher_t voucher)
1624 {
1625 	bank_element_t bank_element = BANK_ELEMENT_NULL;
1626 	bank_account_t bank_account = BANK_ACCOUNT_NULL;
1627 	mach_voucher_attr_value_handle_t vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
1628 	mach_voucher_attr_value_handle_array_size_t val_count;
1629 	kern_return_t kr;
1630 
1631 	val_count = MACH_VOUCHER_ATTR_VALUE_MAX_NESTED;
1632 	kr = mach_voucher_attr_control_get_values(bank_voucher_attr_control,
1633 	    voucher,
1634 	    vals,
1635 	    &val_count);
1636 
1637 	if (kr != KERN_SUCCESS || val_count == 0) {
1638 		return BANK_ACCOUNT_NULL;
1639 	}
1640 
1641 	bank_element = HANDLE_TO_BANK_ELEMENT(vals[0]);
1642 	if (bank_element == BANK_DEFAULT_VALUE) {
1643 		return BANK_ACCOUNT_NULL;
1644 	}
1645 	if (bank_element == BANK_DEFAULT_TASK_VALUE) {
1646 		bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
1647 	}
1648 
1649 	if (bank_element->be_type == BANK_TASK) {
1650 		return BANK_ACCOUNT_NULL;
1651 	} else if (bank_element->be_type == BANK_ACCOUNT) {
1652 		bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
1653 		return bank_account;
1654 	} else {
1655 		panic("Bogus bank type: %d passed in bank_get_voucher_bank_account", bank_element->be_type);
1656 	}
1657 	return BANK_ACCOUNT_NULL;
1658 }
1659 
1660 /*
1661  * Routine: bank_get_bank_task_ledger_with_ref
1662  * Purpose: Get the bank ledger from the bank task and return a reference to it.
1663  */
1664 static ledger_t
bank_get_bank_task_ledger_with_ref(bank_task_t bank_task)1665 bank_get_bank_task_ledger_with_ref(bank_task_t bank_task)
1666 {
1667 	ledger_t ledger = LEDGER_NULL;
1668 
1669 	lck_mtx_lock(&bank_task->bt_acc_to_pay_lock);
1670 	ledger = bank_task->bt_ledger;
1671 	if (ledger) {
1672 		ledger_reference(ledger);
1673 	}
1674 	lck_mtx_unlock(&bank_task->bt_acc_to_pay_lock);
1675 
1676 	return ledger;
1677 }
1678 
1679 /*
1680  * Routine: bank_destroy_bank_task_ledger
1681  * Purpose: Drop the bank task reference on the task ledger.
1682  */
1683 static void
bank_destroy_bank_task_ledger(bank_task_t bank_task)1684 bank_destroy_bank_task_ledger(bank_task_t bank_task)
1685 {
1686 	ledger_t ledger;
1687 
1688 	/* Remove the ledger reference from the bank task */
1689 	lck_mtx_lock(&bank_task->bt_acc_to_pay_lock);
1690 	assert(LEDGER_VALID(bank_task->bt_ledger));
1691 	ledger = bank_task->bt_ledger;
1692 	bank_task->bt_ledger = LEDGER_NULL;
1693 	lck_mtx_unlock(&bank_task->bt_acc_to_pay_lock);
1694 
1695 	ledger_dereference(ledger);
1696 }
1697 
1698 /*
1699  * Routine: bank_get_bank_account_ledger
1700  * Purpose: Get the bankledger from the bank account if ba_merchant different than ba_holder
1701  */
1702 static ledger_t
bank_get_bank_account_ledger(bank_account_t bank_account)1703 bank_get_bank_account_ledger(bank_account_t bank_account)
1704 {
1705 	ledger_t bankledger = LEDGER_NULL;
1706 
1707 	if (bank_account != BANK_ACCOUNT_NULL &&
1708 	    bank_account->ba_holder != bank_account->ba_merchant) {
1709 		bankledger = bank_account->ba_bill;
1710 	}
1711 
1712 	return bankledger;
1713 }
1714 
1715 #if CONFIG_PREADOPT_TG
1716 /*
1717  * Routine: bank_get_preadopt_thread_group
1718  * Purpose: Get the thread group from the voucher for preadoption
1719  *                      (assuming post process is not done on voucher).
1720  * Returns: thread group for pre adoption
1721  * Note: Make sure that the receiver is not an App before preadopting the voucher.
1722  */
1723 kern_return_t
bank_get_preadopt_thread_group(ipc_voucher_t voucher,struct thread_group ** banktg)1724 bank_get_preadopt_thread_group(ipc_voucher_t     voucher,
1725     struct thread_group **banktg)
1726 {
1727 	bank_account_t bank_account = BANK_ACCOUNT_NULL;
1728 	bank_task_t bank_task = BANK_TASK_NULL;
1729 	struct thread_group *thread_group = NULL;
1730 	mach_voucher_attr_value_handle_t vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
1731 	mach_voucher_attr_value_handle_array_size_t val_count;
1732 	bank_element_t bank_element = BANK_ELEMENT_NULL;
1733 
1734 	kern_return_t kr;
1735 	val_count = MACH_VOUCHER_ATTR_VALUE_MAX_NESTED;
1736 	kr = mach_voucher_attr_control_get_values(bank_voucher_attr_control,
1737 	    voucher,
1738 	    vals,
1739 	    &val_count);
1740 	if (kr != KERN_SUCCESS || val_count == 0) {
1741 		goto errorout;
1742 	}
1743 	bank_element = HANDLE_TO_BANK_ELEMENT(vals[0]);
1744 	if (bank_element == BANK_DEFAULT_VALUE) {
1745 		goto errorout;
1746 	}
1747 
1748 	if (bank_element == BANK_DEFAULT_TASK_VALUE) {
1749 		bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
1750 	}
1751 
1752 	if (bank_element->be_type == BANK_TASK) {
1753 		bank_task = CAST_TO_BANK_TASK(bank_element);
1754 	} else if (bank_element->be_type == BANK_ACCOUNT) {
1755 		bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
1756 	} else {
1757 		panic("Bogus bank type: %d passed in bank_get_preadopt_thread_group", bank_element->be_type);
1758 	}
1759 
1760 errorout:
1761 	if (banktg != NULL) {
1762 		/* If the voucher has bank account, then give the thread group from
1763 		 * bank account. If the voucher has a bank task, this is the sender's bank task,
1764 		 * the receiver will convert the sender's bank task to bank account, so give
1765 		 * thread group from the bank task. */
1766 		if (bank_account != NULL) {
1767 			thread_group = bank_get_bank_account_thread_group(bank_account);
1768 		} else if (bank_task != NULL) {
1769 			thread_group = bank_get_bank_task_thread_group(bank_task);
1770 		}
1771 		*banktg = thread_group;
1772 	}
1773 	return KERN_SUCCESS;
1774 }
1775 #endif
1776 
1777 /*
1778  * Routine: bank_get_bank_task_thread_group
1779  * Purpose: Get the bank task's thread group from the bank task
1780  */
1781 static struct thread_group *
bank_get_bank_task_thread_group(bank_task_t bank_task __unused)1782 bank_get_bank_task_thread_group(bank_task_t bank_task __unused)
1783 {
1784 	struct thread_group *banktg = NULL;
1785 
1786 #if CONFIG_THREAD_GROUPS
1787 	if (bank_task != BANK_TASK_NULL) {
1788 		banktg = bank_task->bt_thread_group;
1789 	}
1790 #endif /* CONFIG_THREAD_GROUPS */
1791 
1792 	return banktg;
1793 }
1794 
1795 /*
1796  * Routine: bank_get_bank_account_thread_group
1797  * Purpose: Get the bank account's thread group from the bank account
1798  */
1799 static struct thread_group *
bank_get_bank_account_thread_group(bank_account_t bank_account __unused)1800 bank_get_bank_account_thread_group(bank_account_t bank_account __unused)
1801 {
1802 	struct thread_group *banktg = NULL;
1803 
1804 #if CONFIG_THREAD_GROUPS
1805 	if (bank_account != BANK_ACCOUNT_NULL) {
1806 		banktg = bank_account->ba_thread_group;
1807 	}
1808 #endif /* CONFIG_THREAD_GROUPS */
1809 
1810 	return banktg;
1811 }
1812 
1813 /*
1814  * Routine: bank_get_bank_ledger_thread_group_and_persona
1815  * Purpose: Get the bankledger (chit), thread group and persona id from the voucher.
1816  * Returns: bankledger, thread group if bank_account attribute present in voucher
1817  *          and persona_id
1818  */
1819 kern_return_t
bank_get_bank_ledger_thread_group_and_persona(ipc_voucher_t voucher,ledger_t * bankledger,struct thread_group ** banktg,uint32_t * persona_id)1820 bank_get_bank_ledger_thread_group_and_persona(
1821 	ipc_voucher_t     voucher,
1822 	ledger_t          *bankledger,
1823 	struct thread_group **banktg,
1824 	uint32_t *persona_id)
1825 {
1826 	bank_account_t bank_account;
1827 	bank_task_t bank_task;
1828 	struct thread_group *thread_group = NULL;
1829 
1830 	bank_account = bank_get_voucher_bank_account(voucher);
1831 	bank_task = get_bank_task_context(current_task(), FALSE);
1832 	if (persona_id != NULL) {
1833 		if (bank_account != BANK_ACCOUNT_NULL) {
1834 			*persona_id = bank_account->ba_so_persona_id;
1835 		} else {
1836 			*persona_id = bank_task->bt_persona_id;
1837 		}
1838 	}
1839 	/*
1840 	 * Use BANK_ACCOUNT_NULL if the ba_holder is same as ba_merchant
1841 	 * and bank account thread group is same as current thread group
1842 	 * i.e. ba_merchant's thread group.
1843 	 *
1844 	 * The bank account might have ba_holder same as ba_merchant but different
1845 	 * thread group if daemon sends a voucher to an App and then App sends the
1846 	 * same voucher back to the daemon (IPC code will replace thread group in the
1847 	 * voucher to App's thread group when it gets auto redeemed by the App).
1848 	 */
1849 	if ((bank_account != NULL) &&
1850 	    (bank_account->ba_holder == bank_account->ba_merchant) &&
1851 	    (bank_get_bank_account_thread_group(bank_account) ==
1852 	    bank_get_bank_task_thread_group(bank_account->ba_merchant))) {
1853 		bank_account = BANK_ACCOUNT_NULL;
1854 	}
1855 
1856 	if (bankledger != NULL) {
1857 		*bankledger = bank_get_bank_account_ledger(bank_account);
1858 	}
1859 
1860 	if (banktg != NULL) {
1861 		thread_group = bank_get_bank_account_thread_group(bank_account);
1862 
1863 		/* Return NULL thread group if voucher has current task's thread group */
1864 		if (thread_group == bank_get_bank_task_thread_group(bank_task)) {
1865 			thread_group = NULL;
1866 		}
1867 		*banktg = thread_group;
1868 	}
1869 	return KERN_SUCCESS;
1870 }
1871 
1872 /*
1873  * Routine: bank_swap_thread_bank_ledger
1874  * Purpose: swap the bank ledger on the thread.
1875  * Returns: None.
1876  * Note: Should be only called for current thread or thread which is not started.
1877  */
1878 void
bank_swap_thread_bank_ledger(thread_t thread __unused,ledger_t new_ledger __unused)1879 bank_swap_thread_bank_ledger(thread_t thread __unused, ledger_t new_ledger __unused)
1880 {
1881 	spl_t                   s;
1882 	processor_t             processor;
1883 	ledger_t old_ledger = thread->t_bankledger;
1884 	int64_t ctime, effective_ledger_time_consumed = 0;
1885 	int64_t remainder = 0, consumed = 0;
1886 	int64_t effective_energy_consumed = 0;
1887 	uint64_t thread_energy;
1888 
1889 	if (old_ledger == LEDGER_NULL && new_ledger == LEDGER_NULL) {
1890 		return;
1891 	}
1892 
1893 	assert((thread == current_thread() || thread->started == 0));
1894 
1895 	s = splsched();
1896 	thread_lock(thread);
1897 
1898 	/*
1899 	 * Calculation of time elapsed by the thread before voucher swap.
1900 	 * Following is the timeline which shows all the variables used in the calculation below.
1901 	 *
1902 	 *               thread ledger
1903 	 *                 cpu_time
1904 	 *                    |<-          consumed            ->|<- remainder  ->|
1905 	 * timeline  ----------------------------------------------------------------->
1906 	 *                    |                                  |                |
1907 	 *             thread_dispatch                        ctime           quantum end
1908 	 *
1909 	 *                           |<-effective_ledger_time -> |
1910 	 *               deduct_bank_ledger_time
1911 	 */
1912 
1913 	ctime = mach_absolute_time();
1914 	processor = thread->last_processor;
1915 	if (processor != NULL) {
1916 		if ((int64_t)processor->quantum_end > ctime) {
1917 			remainder = (int64_t)processor->quantum_end - ctime;
1918 		}
1919 
1920 		consumed = thread->quantum_remaining - remainder;
1921 		effective_ledger_time_consumed = consumed - thread->t_deduct_bank_ledger_time;
1922 	}
1923 
1924 	thread->t_deduct_bank_ledger_time = consumed;
1925 
1926 	thread_energy = ml_energy_stat(thread);
1927 	effective_energy_consumed =
1928 	    thread_energy - thread->t_deduct_bank_ledger_energy;
1929 	assert(effective_energy_consumed >= 0);
1930 	thread->t_deduct_bank_ledger_energy = thread_energy;
1931 
1932 	thread->t_bankledger = new_ledger;
1933 
1934 	thread_unlock(thread);
1935 	splx(s);
1936 
1937 	if (old_ledger != LEDGER_NULL) {
1938 		ledger_credit(old_ledger,
1939 		    bank_ledgers.cpu_time,
1940 		    effective_ledger_time_consumed);
1941 		ledger_credit(old_ledger,
1942 		    bank_ledgers.energy,
1943 		    effective_energy_consumed);
1944 	}
1945 }
1946 
1947 /*
1948  * Routine: bank_verify_persona_id
1949  * Purpose: Verifies if the persona id is valid
1950  *
1951  * The caller should check if the task is entitled
1952  * to do the lookup.
1953  */
1954 static boolean_t
bank_verify_persona_id(uint32_t persona_id)1955 bank_verify_persona_id(uint32_t persona_id)
1956 {
1957 	/* A successful lookup implies that the persona id is valid */
1958 	void *persona = persona_lookup(persona_id);
1959 	if (!persona) {
1960 		return FALSE;
1961 	}
1962 	persona_put(persona);
1963 
1964 	return TRUE;
1965 }
1966 
1967 /*
1968  * Routine: bank_merchant_needs_persona_replacement
1969  * Purpose: Check if persona needs to be replaced
1970  *          due to persona propagation restrictions.
1971  * Returns:
1972  *          TRUE: if persona needs to be replaced
1973  *          FALSE: no replacement needed
1974  */
1975 static boolean_t
bank_merchant_needs_persona_replacement(mach_voucher_attr_recipe_command_t command,bank_task_t bank_merchant,uint32_t persona_id)1976 bank_merchant_needs_persona_replacement(
1977 	mach_voucher_attr_recipe_command_t  command,
1978 	bank_task_t                         bank_merchant,
1979 	uint32_t                            persona_id)
1980 {
1981 	boolean_t platform_binary_no_persona_exception = FALSE;
1982 #if defined(XNU_TARGET_OS_OSX)
1983 	if (bank_merchant->bt_persona_id == PERSONA_ID_NONE &&
1984 	    (bank_merchant->bt_platform_binary || bank_merchant->bt_adopt_any_entitled)) {
1985 		platform_binary_no_persona_exception = TRUE;
1986 	}
1987 #endif
1988 
1989 	if (command == MACH_VOUCHER_ATTR_BANK_MODIFY_PERSONA) {
1990 		/*
1991 		 * This command is always called from context of usermanagerd.
1992 		 * The policy for this command is that the provided bank_task
1993 		 * has to be spawned in either System/ System proxy
1994 		 * or passed persona, else the persona needs to be replaced.
1995 		 * On macOS give an exception to platform binaries if
1996 		 * they are spawned in no-persona.
1997 		 */
1998 		if (unique_persona &&
1999 		    bank_merchant->bt_persona_id != persona_get_id(system_persona) &&
2000 		    bank_merchant->bt_persona_id != persona_get_id(proxy_system_persona) &&
2001 		    bank_merchant->bt_persona_id != persona_id &&
2002 		    !platform_binary_no_persona_exception) {
2003 			return TRUE;
2004 		}
2005 		return FALSE;
2006 	} else if (command == MACH_VOUCHER_ATTR_AUTO_REDEEM) {
2007 		/*
2008 		 * The policy for this command is that the provided bank_task
2009 		 * has to be spawned in either System or System proxy else
2010 		 * the persona needs to be replaced. On macOS give an exception
2011 		 * to platform binaries if they are spawned in no-persona.
2012 		 */
2013 		if (unique_persona &&
2014 		    bank_merchant->bt_persona_id != persona_get_id(system_persona) &&
2015 		    bank_merchant->bt_persona_id != persona_get_id(proxy_system_persona) &&
2016 		    !platform_binary_no_persona_exception) {
2017 			return TRUE;
2018 		}
2019 		return FALSE;
2020 	} else {
2021 		panic("Wrong command %u passed to bank_merchant_needs_persona_replacement", command);
2022 	}
2023 	return FALSE;
2024 }
2025