diff options
Diffstat (limited to 'libports/destroy-right.c')
-rw-r--r-- | libports/destroy-right.c | 56 |
1 files changed, 44 insertions, 12 deletions
diff --git a/libports/destroy-right.c b/libports/destroy-right.c index 448b3798..276255f4 100644 --- a/libports/destroy-right.c +++ b/libports/destroy-right.c @@ -1,5 +1,5 @@ /* - Copyright (C) 1995, 1996, 1999 Free Software Foundation, Inc. + Copyright (C) 1995, 1996, 1999, 2014 Free Software Foundation, Inc. Written by Michael I. Bushnell. This file is part of the GNU Hurd. @@ -22,30 +22,62 @@ #include <hurd/ihash.h> #include <assert.h> +#include <pthread.h> +#include <error.h> +#include <time.h> +#include <unistd.h> + error_t ports_destroy_right (void *portstruct) { struct port_info *pi = portstruct; + mach_port_t port_right; + int defer = 0; error_t err; - if (pi->port_right != MACH_PORT_NULL) + pthread_mutex_lock (&_ports_lock); + port_right = pi->port_right; + pi->port_right = MACH_PORT_DEAD; + + if (pi->flags & PORT_HAS_SENDRIGHTS) + { + pi->flags &= ~PORT_HAS_SENDRIGHTS; + + /* There are outstanding send rights, so we might get more + messages. Attached to the messages is a reference to the + port_info object. Of course we destroyed the receive right + these were send to above, but the message could already have + been dequeued to userspace. + + Previously, those messages would have carried an stale name, + which would have caused a hash table lookup failure. + However, stale payloads results in port_info use-after-free. + Therefore, we cannot release the reference here, but defer + that instead until all currently running threads have gone + through a quiescent state. */ + defer = 1; + } + + if (MACH_PORT_VALID (port_right)) { + mach_port_clear_protected_payload (mach_task_self (), port_right); + pthread_rwlock_wrlock (&_ports_htable_lock); hurd_ihash_locp_remove (&_ports_htable, pi->ports_htable_entry); hurd_ihash_locp_remove (&pi->bucket->htable, pi->hentry); pthread_rwlock_unlock (&_ports_htable_lock); - err = mach_port_mod_refs (mach_task_self (), pi->port_right, - MACH_PORT_RIGHT_RECEIVE, -1); - assert_perror (err); - - pi->port_right = MACH_PORT_NULL; + } + pthread_mutex_unlock (&_ports_lock); - if (pi->flags & PORT_HAS_SENDRIGHTS) - { - pi->flags &= ~PORT_HAS_SENDRIGHTS; - ports_port_deref (pi); - } + if (MACH_PORT_VALID (port_right)) + { + err = mach_port_mod_refs (mach_task_self (), port_right, + MACH_PORT_RIGHT_RECEIVE, -1); + assert_perror (err); } + if (defer) + _ports_port_deref_deferred (pi); + return 0; } |