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