From 44cf244a5eba8c6aa8d85ade01abc2d3237596ac Mon Sep 17 00:00:00 2001 From: Lubos Dolezel Date: Tue, 8 Mar 2016 22:28:09 +0100 Subject: [PATCH] LKM: Some initial code for mach port sets --- src/lkm/ipc_msg.c | 63 +++++++++++++++++++++++++++++++++------------ src/lkm/ipc_port.c | 49 +++++++++++++++++++++++++++++++++++ src/lkm/ipc_port.h | 12 ++++++++- src/lkm/ipc_right.c | 3 ++- src/lkm/ipc_space.c | 5 +++- src/lkm/traps.c | 8 ++++++ 6 files changed, 120 insertions(+), 20 deletions(-) diff --git a/src/lkm/ipc_msg.c b/src/lkm/ipc_msg.c index ac2d2e4cb..ac0b4051e 100644 --- a/src/lkm/ipc_msg.c +++ b/src/lkm/ipc_msg.c @@ -455,26 +455,38 @@ mach_msg_return_t ipc_msg_deliver(struct ipc_kmsg* kmsg, ipc_port_unlock(port); -waiting: if (timeout) { - err = wait_event_interruptible_timeout(port->queue_send, - delivery->delivered, msecs_to_jiffies(timeout)); + if (options & MACH_SEND_INTERRUPT) + { + err = wait_event_interruptible_timeout(port->queue_send, + delivery->delivered, msecs_to_jiffies(timeout)); + } + else + { + err = wait_event_timeout(port->queue_send, + delivery->delivered, msecs_to_jiffies(timeout)); + } } else - { - err = wait_event_interruptible(port->queue_send, - delivery->delivered); - } - - if (err) // interruption { if (options & MACH_SEND_INTERRUPT) - ret = MACH_SEND_INTERRUPTED; + { + err = wait_event_interruptible(port->queue_send, + delivery->delivered); + } else - goto waiting; // FIXME: this is wrong!!! + { + err = wait_event_killable(port->queue_send, delivery->delivered); + if (err == 0) + err = 1; + } } - + if (err == -ERESTARTSYS) + ret = MACH_SEND_INTERRUPTED; + else if (err == 0) + ret = MACH_RCV_TIMED_OUT; + debug_msg("\t-> kfree(delivery) #1\n"); kfree(delivery); } @@ -833,7 +845,8 @@ mach_msg_return_t ipc_msg_recv(mach_task_t* task, right = ipc_space_lookup(&task->namespace, port_name); - if (right == NULL || right->type != MACH_PORT_RIGHT_RECEIVE) + if (right == NULL || (right->type != MACH_PORT_RIGHT_RECEIVE + && right->type != MACH_PORT_RIGHT_PORT_SET)) { debug_msg("\t-> MACH_RCV_INVALID_NAME\n"); @@ -841,9 +854,23 @@ mach_msg_return_t ipc_msg_recv(mach_task_t* task, ipc_space_unlock(&task->namespace); goto err; } + if (!PORT_IS_VALID(right->port)) + { + debug_msg("\t-> MACH_RCV_PORT_DIED\n"); + ret = MACH_RCV_PORT_DIED; + ipc_space_unlock(&task->namespace); + goto err; + } + if (right->type == MACH_PORT_RIGHT_RECEIVE && right->port->set != NULL) + { + ret = MACH_RCV_IN_SET; + ipc_space_unlock(&task->namespace); + ipc_port_unlock(right->port); + goto err; + } // Clone the right - right = ipc_right_new(right->port, MACH_PORT_RIGHT_RECEIVE); + right = ipc_right_new(right->port, right->type); locked = true; ipc_space_unlock(&task->namespace); @@ -895,9 +922,10 @@ mach_msg_return_t ipc_msg_recv(mach_task_t* task, } else { - wait_event(right->port->queue_send, + err = wait_event_killable(right->port->queue_send, (right->port->queue_size != 0 || !PORT_IS_VALID(right->port))); - err = 1; + if (err == 0) + err = 1; } } @@ -958,7 +986,7 @@ mach_msg_return_t ipc_msg_recv(mach_task_t* task, &kmsg->msg->msgh_remote_port) != KERN_SUCCESS) { ipc_right_lock_port(kmsg->reply); - ipc_right_put(kmsg->reply); + ipc_right_put_unlock(kmsg->reply); } else { @@ -1015,6 +1043,7 @@ mach_msg_return_t ipc_msg_recv(mach_task_t* task, } err: + debug_msg("ipc_msg_recv end (code=%d)\n", ret); if (right != NULL) { if (locked) diff --git a/src/lkm/ipc_port.c b/src/lkm/ipc_port.c index 51a83cead..a6e9e5b9c 100644 --- a/src/lkm/ipc_port.c +++ b/src/lkm/ipc_port.c @@ -44,10 +44,13 @@ mach_msg_return_t ipc_port_new(darling_mach_port_t** port_out) port->num_srights = 0; port->num_sorights = 0; port->is_server_port = false; + port->is_port_set = false; port->queue_size = 0; + port->set = NULL; INIT_LIST_HEAD(&port->refs); INIT_LIST_HEAD(&port->messages); + INIT_LIST_HEAD(&port->set_head); init_waitqueue_head(&port->queue_send); init_waitqueue_head(&port->queue_recv); @@ -58,6 +61,30 @@ mach_msg_return_t ipc_port_new(darling_mach_port_t** port_out) return KERN_SUCCESS; } +mach_msg_return_t ipc_port_set_new(darling_mach_port_t** port_out) +{ + darling_mach_port_t* port; + + *port_out = NULL; + + port = (darling_mach_port_t*) kmalloc(sizeof(darling_mach_port_t), GFP_KERNEL); + if (port == NULL) + return KERN_RESOURCE_SHORTAGE; + + mutex_init(&port->mutex); + port->num_srights = 0; + port->num_sorights = 0; + port->is_server_port = false; + port->is_port_set = true; + + INIT_LIST_HEAD(&port->refs); + INIT_LIST_HEAD(&port->members); + + atomic_inc(&port_count); + *port_out = port; + return KERN_SUCCESS; +} + mach_msg_return_t ipc_port_put(darling_mach_port_t* port) { struct list_head* p; @@ -68,6 +95,28 @@ mach_msg_return_t ipc_port_put(darling_mach_port_t* port) if (port->server_port.cb_free != NULL) port->server_port.cb_free(&port->server_port); } + else if (port->is_port_set) + { + // Iterate referenced ports + list_for_each(p, &port->members) + { + darling_mach_port_t* ref; + + ref = list_entry(p, darling_mach_port_t, set_head); + + ipc_port_lock(ref); + ref->set = NULL; + ipc_port_unlock(ref); + } + } + else // ordinary port + { + if (port->set != NULL) + { + list_del(&port->set_head); + port->set = NULL; + } + } // mark all references as dead ports list_for_each(p, &port->refs) diff --git a/src/lkm/ipc_port.h b/src/lkm/ipc_port.h index 9555e1d11..782b2144f 100644 --- a/src/lkm/ipc_port.h +++ b/src/lkm/ipc_port.h @@ -48,7 +48,8 @@ struct darling_mach_port int num_sorights; /* Whether this port is associated with a Mach server */ - bool is_server_port; + bool is_server_port : 1; + bool is_port_set : 1; union { @@ -61,6 +62,14 @@ struct darling_mach_port struct list_head messages; unsigned int queue_size; wait_queue_head_t queue_send, queue_recv; + darling_mach_port_t* set; + struct list_head set_head; + }; + + /* Port set vars */ + struct + { + struct list_head members; // refers via set_head }; }; }; @@ -79,6 +88,7 @@ struct ipc_delivered_msg }; mach_msg_return_t ipc_port_new(darling_mach_port_t** port); +mach_msg_return_t ipc_port_set_new(darling_mach_port_t** port); /** * Deallocates the port. Marks all refering rights as PORT_DEAD. diff --git a/src/lkm/ipc_right.c b/src/lkm/ipc_right.c index ba1e6c751..69d79cdf7 100644 --- a/src/lkm/ipc_right.c +++ b/src/lkm/ipc_right.c @@ -44,6 +44,7 @@ struct mach_port_right* ipc_right_new(darling_mach_port_t* port, mach_port_right switch (type) { case MACH_PORT_RIGHT_RECEIVE: + case MACH_PORT_RIGHT_PORT_SET: break; case MACH_PORT_RIGHT_SEND: port->num_srights++; @@ -74,7 +75,7 @@ void ipc_right_put(struct mach_port_right* right) port = right->port; - if (right->type == MACH_PORT_RIGHT_RECEIVE) + if (right->type == MACH_PORT_RIGHT_RECEIVE || right->type == MACH_PORT_RIGHT_PORT_SET) { debug_msg("\tCalling ipc_port_put()\n"); ipc_port_put(port); diff --git a/src/lkm/ipc_space.c b/src/lkm/ipc_space.c index 87d5bd5a9..dc04cd065 100644 --- a/src/lkm/ipc_space.c +++ b/src/lkm/ipc_space.c @@ -63,7 +63,10 @@ mach_msg_return_t ipc_space_make_receive(ipc_namespace_t* space, mutex_lock(&space->mutex); - right = ipc_right_new(port, MACH_PORT_RIGHT_RECEIVE); + if (port->is_port_set) + right = ipc_right_new(port, MACH_PORT_RIGHT_PORT_SET); + else + right = ipc_right_new(port, MACH_PORT_RIGHT_RECEIVE); id = idr_alloc(&space->names, right, 1, -1, GFP_KERNEL); if (id < 0) diff --git a/src/lkm/traps.c b/src/lkm/traps.c index de18953d0..6e413313c 100644 --- a/src/lkm/traps.c +++ b/src/lkm/traps.c @@ -383,6 +383,14 @@ kern_return_t _kernelrpc_mach_port_allocate_trap(mach_task_t* task, break; } // TODO: missing MACH_PORT_RIGHT_PORT_SET + case MACH_PORT_RIGHT_PORT_SET: + { + ret = ipc_port_set_new(&port); + if (ret != KERN_SUCCESS) + return ret; + + break; + } default: { return KERN_INVALID_VALUE; -- 2.51.2