+/*
+ * Routine: ipc_get_pthpriority_from_kmsg_voucher
+ * Purpose:
+ * Get the canonicalized pthread priority from the voucher attached in the kmsg.
+ */
+kern_return_t
+ipc_get_pthpriority_from_kmsg_voucher(
+ ipc_kmsg_t kmsg,
+ ipc_pthread_priority_value_t *canonicalize_priority_value)
+{
+ ipc_voucher_t pthread_priority_voucher;
+ mach_voucher_attr_raw_recipe_size_t content_size =
+ sizeof(mach_voucher_attr_recipe_data_t) + sizeof(ipc_pthread_priority_value_t);
+ uint8_t content_data[content_size];
+ mach_voucher_attr_recipe_t cur_content;
+ kern_return_t kr = KERN_SUCCESS;
+
+ if (!IP_VALID(kmsg->ikm_voucher)) {
+ return KERN_FAILURE;
+ }
+
+ pthread_priority_voucher = (ipc_voucher_t)kmsg->ikm_voucher->ip_kobject;
+ kr = mach_voucher_extract_attr_recipe(pthread_priority_voucher,
+ MACH_VOUCHER_ATTR_KEY_PTHPRIORITY,
+ content_data,
+ &content_size);
+ if (kr != KERN_SUCCESS) {
+ return kr;
+ }
+
+ /* return KERN_INVALID_VALUE for default value */
+ if (content_size < sizeof(mach_voucher_attr_recipe_t)) {
+ return KERN_INVALID_VALUE;
+ }
+
+ cur_content = (mach_voucher_attr_recipe_t) (void *) &content_data[0];
+ assert(cur_content->content_size == sizeof(ipc_pthread_priority_value_t));
+ memcpy(canonicalize_priority_value, cur_content->content, sizeof(ipc_pthread_priority_value_t));
+
+ return KERN_SUCCESS;
+}
+
+
+/*
+ * Routine: ipc_voucher_send_preprocessing
+ * Purpose:
+ * Processing of the voucher in the kmsg before sending it.
+ * Currently use to switch PERSONA_TOKEN in case of process with
+ * no com.apple.private.personas.propagate entitlement.
+ */
+void
+ipc_voucher_send_preprocessing(ipc_kmsg_t kmsg)
+{
+ uint8_t recipes[(MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN + 1) * sizeof(ipc_voucher_attr_recipe_data_t)];
+ ipc_voucher_attr_raw_recipe_array_size_t recipe_size = (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN + 1) *
+ sizeof(ipc_voucher_attr_recipe_data_t);
+ ipc_voucher_t pre_processed_voucher;
+ ipc_voucher_t voucher_to_send;
+ kern_return_t kr;
+ int need_preprocessing = FALSE;
+
+ if (!IP_VALID(kmsg->ikm_voucher) || current_task() == kernel_task) {
+ return;
+ }
+
+ /* setup recipe for preprocessing of all the attributes. */
+ pre_processed_voucher = (ipc_voucher_t)kmsg->ikm_voucher->ip_kobject;
+
+ kr = ipc_voucher_prepare_processing_recipe(pre_processed_voucher,
+ (mach_voucher_attr_raw_recipe_array_t)recipes,
+ &recipe_size, MACH_VOUCHER_ATTR_SEND_PREPROCESS,
+ IVAM_FLAGS_SUPPORT_SEND_PREPROCESS, &need_preprocessing);
+
+ assert(KERN_SUCCESS == kr);
+ /*
+ * Only do send preprocessing if the voucher needs any pre processing.
+ */
+ if (need_preprocessing) {
+ kr = ipc_create_mach_voucher(recipes,
+ recipe_size,
+ &voucher_to_send);
+ assert(KERN_SUCCESS == kr);
+ ipc_port_release_send(kmsg->ikm_voucher);
+ kmsg->ikm_voucher = convert_voucher_to_port(voucher_to_send);
+ }
+}
+
+/*
+ * Routine: ipc_voucher_receive_postprocessing
+ * Purpose:
+ * Redeems the voucher attached to the kmsg.
+ * Note:
+ * Although it is possible to call ipc_importance_receive
+ * here, it is called in mach_msg_receive_results and not here
+ * in order to maintain symmetry with ipc_voucher_send_preprocessing.
+ */
+void
+ipc_voucher_receive_postprocessing(
+ ipc_kmsg_t kmsg,
+ mach_msg_option_t option)
+{
+ uint8_t recipes[(MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN + 1) * sizeof(ipc_voucher_attr_recipe_data_t)];
+ ipc_voucher_attr_raw_recipe_array_size_t recipe_size = (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN + 1) *
+ sizeof(ipc_voucher_attr_recipe_data_t);
+ ipc_voucher_t recv_voucher;
+ ipc_voucher_t sent_voucher;
+ kern_return_t kr;
+ int need_postprocessing = FALSE;
+
+ if ((option & MACH_RCV_VOUCHER) == 0 || (!IP_VALID(kmsg->ikm_voucher)) ||
+ current_task() == kernel_task) {
+ return;
+ }
+
+ /* setup recipe for auto redeem of all the attributes. */
+ sent_voucher = (ipc_voucher_t)kmsg->ikm_voucher->ip_kobject;
+
+ kr = ipc_voucher_prepare_processing_recipe(sent_voucher,
+ (mach_voucher_attr_raw_recipe_array_t)recipes,
+ &recipe_size, MACH_VOUCHER_ATTR_AUTO_REDEEM,
+ IVAM_FLAGS_SUPPORT_RECEIVE_POSTPROCESS, &need_postprocessing);
+
+ assert(KERN_SUCCESS == kr);
+
+ /*
+ * Only do receive postprocessing if the voucher needs any post processing.
+ */
+ if (need_postprocessing) {
+ kr = ipc_create_mach_voucher(recipes,
+ recipe_size,
+ &recv_voucher);
+ assert(KERN_SUCCESS == kr);
+ /* swap the voucher port (and set voucher bits in case it didn't already exist) */
+ kmsg->ikm_header->msgh_bits |= (MACH_MSG_TYPE_MOVE_SEND << 16);
+ ipc_port_release_send(kmsg->ikm_voucher);
+ kmsg->ikm_voucher = convert_voucher_to_port(recv_voucher);
+ }
+}
+
+/*
+ * Routine: ipc_voucher_prepare_processing_recipe
+ * Purpose:
+ * Check if the given voucher has an attribute which supports
+ * the given flag and prepare a recipe to apply that supported
+ * command.
+ */
+static kern_return_t
+ipc_voucher_prepare_processing_recipe(
+ ipc_voucher_t voucher,
+ ipc_voucher_attr_raw_recipe_array_t recipes,
+ ipc_voucher_attr_raw_recipe_array_size_t *in_out_size,
+ mach_voucher_attr_recipe_command_t command,
+ ipc_voucher_attr_manager_flags flags,
+ int *need_processing)
+{
+ ipc_voucher_attr_raw_recipe_array_size_t recipe_size = *in_out_size;
+ ipc_voucher_attr_raw_recipe_array_size_t recipe_used = 0;
+ iv_index_t key_index;
+ ipc_voucher_attr_recipe_t recipe;
+
+ if (IV_NULL == voucher)
+ return KERN_INVALID_ARGUMENT;
+
+ /* Setup a recipe to copy all attributes. */
+ if (recipe_size < sizeof(*recipe))
+ return KERN_NO_SPACE;
+
+ *need_processing = FALSE;
+ recipe = (ipc_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
+ recipe->key = MACH_VOUCHER_ATTR_KEY_ALL;
+ recipe->command = MACH_VOUCHER_ATTR_COPY;
+ recipe->previous_voucher = voucher;
+ recipe->content_size = 0;
+ recipe_used += sizeof(*recipe) + recipe->content_size;
+
+ for (key_index = 0; key_index < voucher->iv_table_size; key_index++) {
+ ipc_voucher_attr_manager_t manager;
+ mach_voucher_attr_key_t key;
+ iv_index_t value_index;
+
+ /* don't output anything for a default value */
+ value_index = iv_lookup(voucher, key_index);
+ if (IV_UNUSED_VALINDEX == value_index)
+ continue;
+
+ if (recipe_size - recipe_used < sizeof(*recipe))
+ return KERN_NO_SPACE;
+
+ recipe = (ipc_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
+
+ /*
+ * Get the manager for this key_index. The
+ * existence of a non-default value for this
+ * slot within our voucher will keep the
+ * manager referenced during the callout.
+ */
+ ivgt_lookup(key_index, FALSE, &manager, NULL);
+ assert(IVAM_NULL != manager);
+ if (IVAM_NULL == manager) {
+ continue;
+ }
+
+ /* Check if the supported flag is set in the manager */
+ if ((manager->ivam_flags & flags) == 0)
+ continue;
+
+ key = iv_index_to_key(key_index);
+
+ recipe->key = key;
+ recipe->command = command;
+ recipe->content_size = 0;
+ recipe->previous_voucher = voucher;
+
+ recipe_used += sizeof(*recipe) + recipe->content_size;
+ *need_processing = TRUE;
+ }
+
+ *in_out_size = recipe_used;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Activity id Generation
+ */
+uint64_t voucher_activity_id;
+
+#define generate_activity_id(x) \
+ ((uint64_t)OSAddAtomic64((x), (int64_t *)&voucher_activity_id))
+
+/*
+ * Routine: mach_init_activity_id
+ * Purpose:
+ * Initialize voucher activity id.
+ */
+void
+mach_init_activity_id(void)
+{
+ voucher_activity_id = 1;
+}
+
+/*
+ * Routine: mach_generate_activity_id
+ * Purpose:
+ * Generate a system wide voucher activity id.
+ */
+kern_return_t
+mach_generate_activity_id(
+ struct mach_generate_activity_id_args *args)
+{
+ uint64_t activity_id;
+ kern_return_t kr = KERN_SUCCESS;
+
+ if (args->count <= 0 || args->count > MACH_ACTIVITY_ID_COUNT_MAX) {
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ activity_id = generate_activity_id(args->count);
+ kr = copyout(&activity_id, args->activity_id, sizeof (activity_id));
+
+ return (kr);
+}