+ dsc->pad_end = 0; // debug, unnecessary
+
+ return (mach_msg_descriptor_t *)(user_dsc_in+1);
+}
+
+mach_msg_descriptor_t * ipc_kmsg_copyin_ool_descriptor(
+ mach_msg_ool_descriptor_t *dsc,
+ mach_msg_descriptor_t *user_dsc,
+ int is_64bit,
+ vm_offset_t *paddr,
+ vm_map_copy_t *copy,
+ vm_size_t *space_needed,
+ vm_map_t map,
+ mach_msg_return_t *mr);
+mach_msg_descriptor_t *
+ipc_kmsg_copyin_ool_descriptor(
+ mach_msg_ool_descriptor_t *dsc,
+ mach_msg_descriptor_t *user_dsc,
+ int is_64bit,
+ vm_offset_t *paddr,
+ vm_map_copy_t *copy,
+ vm_size_t *space_needed,
+ vm_map_t map,
+ mach_msg_return_t *mr)
+{
+ vm_size_t length;
+ boolean_t dealloc;
+ mach_msg_copy_options_t copy_options;
+ mach_vm_offset_t addr;
+ mach_msg_descriptor_type_t dsc_type;
+
+ if (is_64bit) {
+ mach_msg_ool_descriptor64_t *user_ool_dsc = (typeof(user_ool_dsc))user_dsc;
+
+ addr = (mach_vm_offset_t) user_ool_dsc->address;
+ length = user_ool_dsc->size;
+ dealloc = user_ool_dsc->deallocate;
+ copy_options = user_ool_dsc->copy;
+ dsc_type = user_ool_dsc->type;
+
+ user_dsc = (typeof(user_dsc))(user_ool_dsc+1);
+ } else {
+ mach_msg_ool_descriptor32_t *user_ool_dsc = (typeof(user_ool_dsc))user_dsc;
+
+ addr = CAST_USER_ADDR_T(user_ool_dsc->address);
+ dealloc = user_ool_dsc->deallocate;
+ copy_options = user_ool_dsc->copy;
+ dsc_type = user_ool_dsc->type;
+ length = user_ool_dsc->size;
+
+ user_dsc = (typeof(user_dsc))(user_ool_dsc+1);
+ }
+
+ dsc->size = (mach_msg_size_t)length;
+ dsc->deallocate = dealloc;
+ dsc->copy = copy_options;
+ dsc->type = dsc_type;
+
+ if (length == 0) {
+ dsc->address = NULL;
+ } else if ((length >= MSG_OOL_SIZE_SMALL) &&
+ (copy_options == MACH_MSG_PHYSICAL_COPY) && !dealloc) {
+
+ /*
+ * If the request is a physical copy and the source
+ * is not being deallocated, then allocate space
+ * in the kernel's pageable ipc copy map and copy
+ * the data in. The semantics guarantee that the
+ * data will have been physically copied before
+ * the send operation terminates. Thus if the data
+ * is not being deallocated, we must be prepared
+ * to page if the region is sufficiently large.
+ */
+ if (copyin(addr, (char *)*paddr, length)) {
+ *mr = MACH_SEND_INVALID_MEMORY;
+ return NULL;
+ }
+
+ /*
+ * The kernel ipc copy map is marked no_zero_fill.
+ * If the transfer is not a page multiple, we need
+ * to zero fill the balance.
+ */
+ if (!page_aligned(length)) {
+ (void) memset((void *) (*paddr + length), 0,
+ round_page(length) - length);
+ }
+ if (vm_map_copyin(ipc_kernel_copy_map, (vm_map_address_t)*paddr,
+ (vm_map_size_t)length, TRUE, copy) != KERN_SUCCESS) {
+ *mr = MACH_MSG_VM_KERNEL;
+ return NULL;
+ }
+ dsc->address = (void *)*copy;
+ *paddr += round_page(length);
+ *space_needed -= round_page(length);
+ } else {
+
+ /*
+ * Make a vm_map_copy_t of the of the data. If the
+ * data is small, this will do an optimized physical
+ * copy. Otherwise, it will do a virtual copy.
+ *
+ * NOTE: A virtual copy is OK if the original is being
+ * deallocted, even if a physical copy was requested.
+ */
+ kern_return_t kr = vm_map_copyin(map, addr,
+ (vm_map_size_t)length, dealloc, copy);
+ if (kr != KERN_SUCCESS) {
+ *mr = (kr == KERN_RESOURCE_SHORTAGE) ?
+ MACH_MSG_VM_KERNEL :
+ MACH_SEND_INVALID_MEMORY;
+ return NULL;
+ }
+ dsc->address = (void *)*copy;
+ }
+ return user_dsc;
+}
+
+mach_msg_descriptor_t * ipc_kmsg_copyin_ool_ports_descriptor(
+ mach_msg_ool_ports_descriptor_t *dsc,
+ mach_msg_descriptor_t *user_dsc,
+ int is_64bit,
+ vm_map_t map,
+ ipc_space_t space,
+ ipc_object_t dest,
+ ipc_kmsg_t kmsg,
+ mach_msg_return_t *mr);
+mach_msg_descriptor_t *
+ipc_kmsg_copyin_ool_ports_descriptor(
+ mach_msg_ool_ports_descriptor_t *dsc,
+ mach_msg_descriptor_t *user_dsc,
+ int is_64bit,
+ vm_map_t map,
+ ipc_space_t space,
+ ipc_object_t dest,
+ ipc_kmsg_t kmsg,
+ mach_msg_return_t *mr)
+{
+ void *data;
+ ipc_object_t *objects;
+ unsigned int i;
+ mach_vm_offset_t addr;
+ mach_msg_type_name_t user_disp;
+ mach_msg_type_name_t result_disp;
+ mach_msg_type_number_t count;
+ mach_msg_copy_options_t copy_option;
+ boolean_t deallocate;
+ mach_msg_descriptor_type_t type;
+ vm_size_t ports_length, names_length;
+
+ if (is_64bit) {
+ mach_msg_ool_ports_descriptor64_t *user_ool_dsc = (typeof(user_ool_dsc))user_dsc;
+
+ addr = (mach_vm_offset_t)user_ool_dsc->address;
+ count = user_ool_dsc->count;
+ deallocate = user_ool_dsc->deallocate;
+ copy_option = user_ool_dsc->copy;
+ user_disp = user_ool_dsc->disposition;
+ type = user_ool_dsc->type;
+
+ user_dsc = (typeof(user_dsc))(user_ool_dsc+1);
+ } else {
+ mach_msg_ool_ports_descriptor32_t *user_ool_dsc = (typeof(user_ool_dsc))user_dsc;
+
+ addr = CAST_USER_ADDR_T(user_ool_dsc->address);
+ count = user_ool_dsc->count;
+ deallocate = user_ool_dsc->deallocate;
+ copy_option = user_ool_dsc->copy;
+ user_disp = user_ool_dsc->disposition;
+ type = user_ool_dsc->type;
+
+ user_dsc = (typeof(user_dsc))(user_ool_dsc+1);
+ }
+
+ dsc->deallocate = deallocate;
+ dsc->copy = copy_option;
+ dsc->type = type;
+ dsc->count = count;
+ dsc->address = NULL; /* for now */
+
+ result_disp = ipc_object_copyin_type(user_disp);
+ dsc->disposition = result_disp;
+
+ /* We always do a 'physical copy', but you have to specify something valid */
+ if (copy_option != MACH_MSG_PHYSICAL_COPY &&
+ copy_option != MACH_MSG_VIRTUAL_COPY) {
+ *mr = MACH_SEND_INVALID_TYPE;
+ return NULL;
+ }
+
+ /* calculate length of data in bytes, rounding up */
+
+ if (os_mul_overflow(count, sizeof(mach_port_t), &ports_length)) {
+ *mr = MACH_SEND_TOO_LARGE;
+ return NULL;
+ }
+
+ if (os_mul_overflow(count, sizeof(mach_port_name_t), &names_length)) {
+ *mr = MACH_SEND_TOO_LARGE;
+ return NULL;
+ }
+
+ if (ports_length == 0) {
+ return user_dsc;
+ }
+
+ data = kalloc(ports_length);
+
+ if (data == NULL) {
+ *mr = MACH_SEND_NO_BUFFER;
+ return NULL;
+ }
+
+#ifdef __LP64__
+ mach_port_name_t *names = &((mach_port_name_t *)data)[count];
+#else
+ mach_port_name_t *names = ((mach_port_name_t *)data);
+#endif
+
+ if (copyinmap(map, addr, names, names_length) != KERN_SUCCESS) {
+ kfree(data, ports_length);
+ *mr = MACH_SEND_INVALID_MEMORY;
+ return NULL;
+ }
+
+ if (deallocate) {
+ (void) mach_vm_deallocate(map, addr, (mach_vm_size_t)ports_length);
+ }
+
+ objects = (ipc_object_t *) data;
+ dsc->address = data;
+
+ for ( i = 0; i < count; i++) {
+ mach_port_name_t name = names[i];
+ ipc_object_t object;
+
+ if (!MACH_PORT_VALID(name)) {
+ objects[i] = (ipc_object_t)CAST_MACH_NAME_TO_PORT(name);
+ continue;
+ }
+
+ kern_return_t kr = ipc_object_copyin(space, name, user_disp, &object);
+
+ if (kr != KERN_SUCCESS) {
+ unsigned int j;
+
+ for(j = 0; j < i; j++) {
+ object = objects[j];
+ if (IPC_OBJECT_VALID(object))
+ ipc_object_destroy(object, result_disp);
+ }
+ kfree(data, ports_length);
+ dsc->address = NULL;
+ *mr = MACH_SEND_INVALID_RIGHT;
+ return NULL;
+ }
+
+ if ((dsc->disposition == MACH_MSG_TYPE_PORT_RECEIVE) &&
+ ipc_port_check_circularity(
+ (ipc_port_t) object,
+ (ipc_port_t) dest))
+ kmsg->ikm_header->msgh_bits |= MACH_MSGH_BITS_CIRCULAR;
+
+ objects[i] = object;
+ }
+
+ return user_dsc;
+}
+
+/*
+ * Routine: ipc_kmsg_copyin_body
+ * Purpose:
+ * "Copy-in" port rights and out-of-line memory
+ * in the message body.
+ *
+ * In all failure cases, the message is left holding
+ * no rights or memory. However, the message buffer
+ * is not deallocated. If successful, the message
+ * contains a valid destination port.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * MACH_MSG_SUCCESS Successful copyin.
+ * MACH_SEND_INVALID_MEMORY Can't grab out-of-line memory.
+ * MACH_SEND_INVALID_RIGHT Can't copyin port right in body.
+ * MACH_SEND_INVALID_TYPE Bad type specification.
+ * MACH_SEND_MSG_TOO_SMALL Body is too small for types/data.
+ * MACH_SEND_INVALID_RT_OOL_SIZE OOL Buffer too large for RT
+ * MACH_MSG_INVALID_RT_DESCRIPTOR Dealloc and RT are incompatible
+ */
+
+mach_msg_return_t
+ipc_kmsg_copyin_body(
+ ipc_kmsg_t kmsg,
+ ipc_space_t space,
+ vm_map_t map)
+{
+ ipc_object_t dest;
+ mach_msg_body_t *body;
+ mach_msg_descriptor_t *daddr, *naddr;
+ mach_msg_descriptor_t *user_addr, *kern_addr;
+ mach_msg_type_number_t dsc_count;
+ boolean_t is_task_64bit = (map->max_offset > VM_MAX_ADDRESS);
+ boolean_t complex = FALSE;
+ vm_size_t space_needed = 0;
+ vm_offset_t paddr = 0;
+ vm_map_copy_t copy = VM_MAP_COPY_NULL;
+ mach_msg_type_number_t i;
+ mach_msg_return_t mr = MACH_MSG_SUCCESS;
+
+ vm_size_t descriptor_size = 0;
+
+ mach_msg_type_number_t total_ool_port_count = 0;
+
+ /*
+ * Determine if the target is a kernel port.
+ */
+ dest = (ipc_object_t) kmsg->ikm_header->msgh_remote_port;
+ body = (mach_msg_body_t *) (kmsg->ikm_header + 1);
+ naddr = (mach_msg_descriptor_t *) (body + 1);
+
+ dsc_count = body->msgh_descriptor_count;
+ if (dsc_count == 0)
+ return MACH_MSG_SUCCESS;
+
+ /*
+ * Make an initial pass to determine kernal VM space requirements for