+mach_msg_descriptor_t *ipc_kmsg_copyin_port_descriptor(
+ volatile mach_msg_port_descriptor_t *dsc,
+ mach_msg_legacy_port_descriptor_t *user_dsc,
+ ipc_space_t space,
+ ipc_object_t dest,
+ ipc_kmsg_t kmsg,
+ mach_msg_return_t *mr);
+
+void ipc_print_type_name(
+ int type_name);
+mach_msg_descriptor_t *
+ipc_kmsg_copyin_port_descriptor(
+ volatile mach_msg_port_descriptor_t *dsc,
+ mach_msg_legacy_port_descriptor_t *user_dsc_in,
+ ipc_space_t space,
+ ipc_object_t dest,
+ ipc_kmsg_t kmsg,
+ mach_msg_return_t *mr)
+{
+ volatile mach_msg_legacy_port_descriptor_t *user_dsc = user_dsc_in;
+ mach_msg_type_name_t user_disp;
+ mach_msg_type_name_t result_disp;
+ mach_port_name_t name;
+ ipc_object_t object;
+
+ user_disp = user_dsc->disposition;
+ result_disp = ipc_object_copyin_type(user_disp);
+
+ name = (mach_port_name_t)user_dsc->name;
+ if (MACH_PORT_VALID(name)) {
+
+ kern_return_t kr = ipc_object_copyin(space, name, user_disp, &object);
+ if (kr != KERN_SUCCESS) {
+ *mr = MACH_SEND_INVALID_RIGHT;
+ return NULL;
+ }
+
+ if ((result_disp == 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;
+ }
+ dsc->name = (ipc_port_t) object;
+ } else {
+ dsc->name = CAST_MACH_NAME_TO_PORT(name);
+ }
+ dsc->disposition = result_disp;
+ dsc->type = MACH_MSG_PORT_DESCRIPTOR;
+
+ 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;
+
+ if (count > (INT_MAX / sizeof(mach_port_t))) {
+ *mr = MACH_SEND_TOO_LARGE;
+ return NULL;
+ }
+
+ /* calculate length of data in bytes, rounding up */
+ ports_length = count * sizeof(mach_port_t);
+ names_length = count * sizeof(mach_port_name_t);
+
+ 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;
+}
+