summaryrefslogtreecommitdiff
path: root/libports/destroy-right.c
diff options
context:
space:
mode:
Diffstat (limited to 'libports/destroy-right.c')
-rw-r--r--libports/destroy-right.c56
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;
}