]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/ipc/ipc_voucher.c
xnu-4903.221.2.tar.gz
[apple/xnu.git] / osfmk / ipc / ipc_voucher.c
index 36e77dfd54e61e6fc9419535d2411684dfd9a885..4e7b4b9502368d0f57e11d520feae4ca42aac5e9 100644 (file)
@@ -27,6 +27,7 @@
  */
 
 #include <mach/mach_types.h>
+#include <mach/mach_traps.h>
 #include <mach/notify.h>
 #include <ipc/ipc_types.h>
 #include <ipc/ipc_port.h>
@@ -42,6 +43,7 @@
 #include <mach/mach_voucher_server.h>
 #include <mach/mach_voucher_attr_control_server.h>
 #include <mach/mach_host_server.h>
+#include <voucher/ipc_pthread_priority_types.h>
 
 /*
  * Sysctl variable; enable and disable tracing of voucher contents
@@ -97,24 +99,21 @@ static lck_spin_t ivgt_lock_data;
 ipc_voucher_t iv_alloc(iv_index_t entries);
 void iv_dealloc(ipc_voucher_t iv, boolean_t unhash);
 
-static inline iv_refs_t
+os_refgrp_decl(static, iv_refgrp, "voucher", NULL);
+os_refgrp_decl(static, ivac_refgrp, "voucher attribute control", NULL);
+
+static inline void
 iv_reference(ipc_voucher_t iv)
 {
-       iv_refs_t refs;
-
-       refs = hw_atomic_add(&iv->iv_refs, 1);
-       return refs;
+       os_ref_retain(&iv->iv_refs);
 }
 
 static inline void
 iv_release(ipc_voucher_t iv)
 {
-       iv_refs_t refs;
-
-       assert(0 < iv->iv_refs);
-       refs = hw_atomic_sub(&iv->iv_refs, 1);
-       if (0 == refs)
+       if (os_ref_release(&iv->iv_refs) == 0) {
                iv_dealloc(iv, TRUE);
+       }
 }
 
 /*
@@ -183,6 +182,14 @@ static void ivgt_lookup(iv_index_t,
                        ipc_voucher_attr_manager_t *,
                        ipc_voucher_attr_control_t *);
 
+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);
 
 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
 void user_data_attr_manager_init(void);
@@ -231,7 +238,7 @@ iv_alloc(iv_index_t entries)
        if (IV_NULL == iv)
                return IV_NULL;
                
-       iv->iv_refs = 1;
+       os_ref_init(&iv->iv_refs, &iv_refgrp);
        iv->iv_sum = 0;
        iv->iv_hash = 0;
        iv->iv_port = IP_NULL;
@@ -287,7 +294,7 @@ iv_dealloc(ipc_voucher_t iv, boolean_t unhash)
         */
        if (unhash) {
                ivht_lock();
-               assert(0 == iv->iv_refs);
+               assert(os_ref_get_count(&iv->iv_refs) == 0);
                assert(IV_HASH_BUCKETS > iv->iv_hash);
                queue_remove(&ivht_bucket[iv->iv_hash], iv, ipc_voucher_t, iv_hash_link);
                ivht_count--;
@@ -296,8 +303,10 @@ iv_dealloc(ipc_voucher_t iv, boolean_t unhash)
                KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC,MACH_IPC_VOUCHER_DESTROY) | DBG_FUNC_NONE,
                                      VM_KERNEL_ADDRPERM((uintptr_t)iv), 0, ivht_count, 0, 0);
 
-       } else
-               assert(0 == --iv->iv_refs);
+       } else {
+               os_ref_count_t cnt __assert_only = os_ref_release(&iv->iv_refs);
+               assert(cnt == 0);
+       }
 
        /*
         * if a port was allocated for this voucher,
@@ -440,13 +449,10 @@ convert_port_name_to_voucher(
 void
 ipc_voucher_reference(ipc_voucher_t voucher)
 {
-       iv_refs_t refs;
-
        if (IPC_VOUCHER_NULL == voucher)
                return;
 
-       refs = iv_reference(voucher);
-       assert(1 < refs);
+       iv_reference(voucher);
 }
 
 void
@@ -494,7 +500,7 @@ convert_voucher_to_port(ipc_voucher_t voucher)
        if (IV_NULL == voucher)
                return (IP_NULL);
 
-       assert(0 < voucher->iv_refs);
+       assert(os_ref_get_count(&voucher->iv_refs) > 0);
 
        /* create a port if needed */
        port = voucher->iv_port;
@@ -535,6 +541,7 @@ convert_voucher_to_port(ipc_voucher_t voucher)
 #define ivace_reset_data(ivace_elem, next_index) {       \
        (ivace_elem)->ivace_value = 0xDEADC0DEDEADC0DE;  \
        (ivace_elem)->ivace_refs = 0;                    \
+       (ivace_elem)->ivace_persist = 0;                 \
        (ivace_elem)->ivace_made = 0;                    \
        (ivace_elem)->ivace_free = TRUE;                 \
        (ivace_elem)->ivace_releasing = FALSE;           \
@@ -546,6 +553,7 @@ convert_voucher_to_port(ipc_voucher_t voucher)
 #define ivace_copy_data(ivace_src_elem, ivace_dst_elem) {  \
        (ivace_dst_elem)->ivace_value = (ivace_src_elem)->ivace_value; \
        (ivace_dst_elem)->ivace_refs = (ivace_src_elem)->ivace_refs;   \
+       (ivace_dst_elem)->ivace_persist = (ivace_src_elem)->ivace_persist; \
        (ivace_dst_elem)->ivace_made = (ivace_src_elem)->ivace_made;   \
        (ivace_dst_elem)->ivace_free = (ivace_src_elem)->ivace_free;   \
        (ivace_dst_elem)->ivace_layered = (ivace_src_elem)->ivace_layered;   \
@@ -566,7 +574,7 @@ ivac_alloc(iv_index_t key_index)
        if (IVAC_NULL == ivac)
                return IVAC_NULL;
                
-       ivac->ivac_refs = 1;
+       os_ref_init(&ivac->ivac_refs, &ivac_refgrp);
        ivac->ivac_is_growing = FALSE;
        ivac->ivac_port = IP_NULL;
 
@@ -604,7 +612,7 @@ ivac_dealloc(ipc_voucher_attr_control_t ivac)
         * that the reference count is still zero.
         */
        ivgt_lock();
-       if (ivac->ivac_refs > 0) {
+       if (os_ref_get_count(&ivac->ivac_refs) > 0) {
                ivgt_unlock();
                return;
        }
@@ -712,8 +720,9 @@ ipc_voucher_attr_control_notify(mach_msg_header_t *msg)
                ip_unlock(port);
 
                ivac_release(ivac);
+       } else {
+               ip_unlock(port);
        }
-       ip_unlock(port);
 }
 
 /*
@@ -755,7 +764,7 @@ convert_voucher_attr_control_to_port(ipc_voucher_attr_control_t control)
                assert(IP_NULL == port->ip_nsrequest);
                ipc_port_nsrequest(port, port->ip_mscount, ipc_port_make_sonce_locked(port), &old_notify);
                assert(IP_NULL == old_notify);
-               ip_unlock(port);
+               /* ipc_port_nsrequest unlocks the port */
        } else {
                /* piggyback on the existing port reference, so consume ours */
                ip_unlock(port);
@@ -824,23 +833,17 @@ ivac_grow_table(ipc_voucher_attr_control_t ivac)
        ivac->ivac_is_growing = 1;
        if (ivac->ivac_table_size >= IVAC_ENTRIES_MAX) {
                panic("Cannot grow ipc space beyond IVAC_ENTRIES_MAX. Some process is leaking vouchers");
+               return;
        }
 
        old_size = ivac->ivac_table_size;
        ivac_unlock(ivac);
 
-       /*
-        * if initial size is not leading to page aligned allocations,
-        * set new_size such that new_size * sizeof(ivac_entry) is page aligned.
-        */
-       
-       if ((old_size * sizeof(ivac_entry)) & PAGE_MASK){
-               new_size = (iv_index_t)round_page((old_size * sizeof(ivac_entry)))/(sizeof (ivac_entry));
-       } else {
-               new_size = old_size * 2;
-       }
+       new_size = old_size * 2;
 
        assert(new_size > old_size);
+       assert(new_size < IVAC_ENTRIES_MAX);
+
        new_table = kalloc(sizeof(ivac_entry) * new_size);
        if (!new_table){
                panic("Failed to grow ivac table to size %d\n", new_size);
@@ -904,7 +907,11 @@ ivace_reference_by_index(
        assert(0xdeadc0dedeadc0de != ivace->ivace_value);
        assert(0 < ivace->ivace_refs);
        assert(!ivace->ivace_free);
-       ivace->ivace_refs++;
+
+       /* Take ref only on non-persistent values */
+       if (!ivace->ivace_persist) {
+               ivace->ivace_refs++;
+       }
        ivac_unlock(ivac);
 }
 
@@ -920,7 +927,8 @@ ivace_reference_by_index(
 static iv_index_t
 ivace_reference_by_value(
        ipc_voucher_attr_control_t      ivac,
-       mach_voucher_attr_value_handle_t        value)
+       mach_voucher_attr_value_handle_t        value,
+       mach_voucher_attr_value_flags_t          flag)
 {
        ivac_entry_t ivace = IVACE_NULL;
        iv_index_t hash_index;
@@ -948,8 +956,8 @@ restart:
 
        /* found it? */
        if (index != IV_HASH_END) { 
-               /* only add reference on non-default value */
-               if (IV_UNUSED_VALINDEX != index) {
+               /* only add reference on non-persistent value */
+               if (!ivace->ivace_persist) {
                        ivace->ivace_refs++;
                        ivace->ivace_made++;
                }
@@ -976,6 +984,7 @@ restart:
        ivace->ivace_refs = 1;
        ivace->ivace_made = 1;
        ivace->ivace_free = FALSE;
+       ivace->ivace_persist = (flag & MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST) ? TRUE : FALSE;
 
        /* insert the new entry in the proper hash chain */
        ivace->ivace_next = ivac->ivac_table[hash_index].ivace_index;
@@ -1021,6 +1030,12 @@ static void ivace_release(
 
        assert(0 < ivace->ivace_refs);
 
+       /* cant release persistent values */
+       if (ivace->ivace_persist) {
+               ivac_unlock(ivac);
+               return;
+       }
+
        if (0 < --ivace->ivace_refs) {
                ivac_unlock(ivac);
                return;
@@ -1065,8 +1080,6 @@ static void ivace_release(
         * re-drive the release.
         */
        if (ivace->ivace_made != made) {
-               assert(made < ivace->ivace_made);
-
                if (KERN_SUCCESS == kr)
                        ivace->ivace_made -= made;
 
@@ -1193,6 +1206,7 @@ ipc_replace_voucher_value(
        mach_voucher_attr_value_handle_t previous_vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
        mach_voucher_attr_value_handle_array_size_t previous_vals_count;
        mach_voucher_attr_value_handle_t new_value;
+       mach_voucher_attr_value_flags_t new_flag;
        ipc_voucher_t new_value_voucher;
        ipc_voucher_attr_manager_t ivam;
        ipc_voucher_attr_control_t ivac;
@@ -1232,7 +1246,7 @@ ipc_replace_voucher_value(
                                    ivam, key, command,
                                    previous_vals, previous_vals_count,
                                    content, content_size,
-                                   &new_value, &new_value_voucher);
+                                   &new_value, &new_flag, &new_value_voucher);
        if (KERN_SUCCESS != kr) {
                ivac_release(ivac);
                return kr;
@@ -1248,7 +1262,7 @@ ipc_replace_voucher_value(
         * is transferred to a new value, or consumed if
         * we find a matching existing value.
         */
-       val_index = ivace_reference_by_value(ivac, new_value);
+       val_index = ivace_reference_by_value(ivac, new_value, new_flag);
        iv_set(voucher, key_index, val_index);
 
        /*
@@ -1302,7 +1316,8 @@ ipc_directly_replace_voucher_value(
         * is transferred to a new value, or consumed if
         * we find a matching existing value.
         */
-       val_index = ivace_reference_by_value(ivac, new_value);
+       val_index = ivace_reference_by_value(ivac, new_value,
+                               MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE);
        iv_set(voucher, key_index, val_index);
 
        /*
@@ -1597,8 +1612,7 @@ iv_dedup(ipc_voucher_t new_iv)
                assert(iv->iv_hash == hash);
 
                /* if not already deallocating and sums match... */
-               if (0 < iv->iv_refs && iv->iv_sum == sum) {
-                       iv_refs_t refs;
+               if ((os_ref_get_count(&iv->iv_refs) > 0) && (iv->iv_sum == sum)) {
                        iv_index_t i;
 
                        assert(iv->iv_table_size <= new_iv->iv_table_size);
@@ -1621,16 +1635,12 @@ iv_dedup(ipc_voucher_t new_iv)
 
                        /* can we get a ref before it hits 0
                         *
-                        * This is thread safe. The reference is just an atomic
-                        * add. If the reference count is zero when we adjust it,
-                        * no other thread can have a reference to the voucher.
+                        * This is thread safe. If the reference count is zero before we
+                        * adjust it, no other thread can have a reference to the voucher.
                         * The dealloc code requires holding the ivht_lock, so
                         * the voucher cannot be yanked out from under us.
                         */
-                       refs = iv_reference(iv);
-                       if (1 == refs) {
-                               /* drats! going away. Put back to zero */
-                               iv->iv_refs = 0;
+                       if (!os_ref_retain_try(&iv->iv_refs)) {
                                continue;
                        }
 
@@ -1682,7 +1692,7 @@ iv_dedup(ipc_voucher_t new_iv)
 #define PAYLOAD_PER_TRACEPOINT (4 * sizeof(uintptr_t))
 #define PAYLOAD_SIZE 1024
 
-                       _Static_assert(PAYLOAD_SIZE % PAYLOAD_PER_TRACEPOINT == 0, "size invariant violated");
+                       static_assert(PAYLOAD_SIZE % PAYLOAD_PER_TRACEPOINT == 0, "size invariant violated");
 
                        mach_voucher_attr_raw_recipe_array_size_t payload_size = PAYLOAD_SIZE;
                        uintptr_t payload[PAYLOAD_SIZE / sizeof(uintptr_t)];
@@ -1704,24 +1714,21 @@ iv_dedup(ipc_voucher_t new_iv)
                                }
                        }
 
-                       KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC,MACH_IPC_VOUCHER_CREATE) | DBG_FUNC_NONE,
-                                             voucher_addr,
-                                             new_iv->iv_table_size, ivht_count, payload_size, 0);
+                       KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_VOUCHER_CREATE),
+                                       voucher_addr, new_iv->iv_table_size, ivht_count,
+                                       payload_size);
 
                        uintptr_t index = 0;
                        while (attr_tracepoints_needed--) {
-                               KERNEL_DEBUG_CONSTANT1(MACHDBG_CODE(DBG_MACH_IPC,MACH_IPC_VOUCHER_CREATE_ATTR_DATA) | DBG_FUNC_NONE,
-                                                      payload[index],
-                                                      payload[index+1],
-                                                      payload[index+2],
-                                                      payload[index+3],
-                                                      voucher_addr);
+                               KDBG(MACHDBG_CODE(DBG_MACH_IPC,
+                                               MACH_IPC_VOUCHER_CREATE_ATTR_DATA), payload[index],
+                                               payload[index + 1], payload[index + 2],
+                                               payload[index + 3]);
                                index += 4;
                        }
                } else {
-                       KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC,MACH_IPC_VOUCHER_CREATE) | DBG_FUNC_NONE,
-                                             voucher_addr,
-                                             new_iv->iv_table_size, ivht_count, 0, 0);
+                       KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_VOUCHER_CREATE),
+                                       voucher_addr, new_iv->iv_table_size, ivht_count);
                }
        }
 #endif /* KDEBUG_LEVEL >= KDEBUG_LEVEL_STANDARD */
@@ -1915,6 +1922,7 @@ ipc_register_well_known_mach_voucher_attr_manager(
        new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_value = default_value;
        new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_refs = IVACE_REFS_MAX;
        new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_made = IVACE_REFS_MAX;
+       new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_persist = TRUE;
        assert(IV_HASH_END == new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_next);
 
        ivgt_lock();
@@ -1990,7 +1998,9 @@ mach_voucher_extract_attr_content(
         * manager referenced during the callout.
         */
        ivgt_lookup(key_index, FALSE, &manager, NULL);
-       assert(IVAM_NULL != manager);
+       if (IVAM_NULL == manager) {
+               return KERN_INVALID_ARGUMENT;
+       }
 
        /*
         * Get the value(s) to pass to the manager
@@ -2066,7 +2076,9 @@ mach_voucher_extract_attr_recipe(
         * manager referenced during the callout.
         */
        ivgt_lookup(key_index, FALSE, &manager, NULL);
-       assert(IVAM_NULL != manager);
+       if (IVAM_NULL == manager) {
+               return KERN_INVALID_ARGUMENT;
+       }
 
        /*
         * Get the value(s) to pass to the manager
@@ -2132,9 +2144,6 @@ mach_voucher_extract_all_attr_recipes(
                if (recipe_size - recipe_used < sizeof(*recipe))
                        return KERN_NO_SPACE;
 
-               recipe = (mach_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
-               content_size = recipe_size - recipe_used - sizeof(*recipe);
-               
                /*
                 * Get the manager for this key_index.  The
                 * existence of a non-default value for this
@@ -2143,6 +2152,12 @@ mach_voucher_extract_all_attr_recipes(
                 */
                ivgt_lookup(key_index, FALSE, &manager, NULL);
                assert(IVAM_NULL != manager);
+               if (IVAM_NULL == manager) {
+                       continue;
+               }
+
+               recipe = (mach_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
+               content_size = recipe_size - recipe_used - sizeof(*recipe);
 
                /*
                 * Get the value(s) to pass to the manager
@@ -2272,7 +2287,9 @@ mach_voucher_attr_command(
         * execution.
         */
        ivgt_lookup(key_index, TRUE, &manager, &control);
-       assert(IVAM_NULL != manager);
+       if (IVAM_NULL == manager) {
+               return KERN_INVALID_ARGUMENT;
+       }
 
        /*
         * Get the values for this <voucher, key> pair
@@ -2323,14 +2340,13 @@ mach_voucher_attr_control_get_values(
 
        key_index = control->ivac_key_index;
 
-       assert(0 < voucher->iv_refs);
+       assert(os_ref_get_count(&voucher->iv_refs) > 0);
        value_index = iv_lookup(voucher, key_index);
        ivace_lookup_values(key_index, value_index,
                            out_values, in_out_size);
        return KERN_SUCCESS;
 }
 
-
 /*
  *     Routine:        mach_voucher_attr_control_create_mach_voucher
  *     Purpose:
@@ -2581,6 +2597,267 @@ host_register_mach_voucher_attr_manager(
        return KERN_NOT_SUPPORTED;
 }
 
+/*
+ *     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);
+}
 
 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
 
@@ -2637,6 +2914,7 @@ user_data_get_value(
        mach_voucher_attr_content_t                     content,
        mach_voucher_attr_content_size_t                content_size,
        mach_voucher_attr_value_handle_t                *out_value,
+       mach_voucher_attr_value_flags_t                 *out_flags,
        ipc_voucher_t                                   *out_value_voucher);
 
 static kern_return_t
@@ -2671,6 +2949,7 @@ struct ipc_voucher_attr_manager user_data_manager = {
        .ivam_extract_content = user_data_extract_content,
        .ivam_command =         user_data_command,
        .ivam_release =         user_data_release,
+       .ivam_flags =           IVAM_FLAGS_NONE,
 };
 
 ipc_voucher_attr_control_t user_data_control;
@@ -2826,6 +3105,7 @@ user_data_get_value(
        mach_voucher_attr_content_t                     content,
        mach_voucher_attr_content_size_t                content_size,
        mach_voucher_attr_value_handle_t                *out_value,
+       mach_voucher_attr_value_flags_t                 *out_flags,
        ipc_voucher_t                                   *out_value_voucher)
 {
        user_data_element_t elem;
@@ -2835,6 +3115,7 @@ user_data_get_value(
 
        /* never an out voucher */
        *out_value_voucher = IPC_VOUCHER_NULL;
+       *out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE;
 
        switch (command) {