protected payloads for GNU Mach
Hi :) this patch series implements protected payloads on top of the mach messaging system. It preserves the semantics for programs not using this functionality. This is the kernel part of my work that will (hopefully) speed up the Hurd. For a detailed sketch see: id:20131128235647.1092.90...@thinkbox.jade-hamburg.de or http://marc.info/?l=debian-hurd&m=138568308704140&w=2 I believe that the kernel part is feature complete. I had this patch series in the gnumach packages in hurd-ci since January 18th. I have not noticed any problems that I'd attribute to this series, hence I strongly believe that this does in fact preserve the current semantics for existing programs. I did not change the implementation since I posted it in the mail thread mentioned above, I just added documentation. Cheers, Justus
[PATCH 03/11] ipc: implement mach_port_set_protected_payload
* include/mach/mach_port.defs: Add mach_port_set_protected_payload. * ipc/mach_port.c: Implement mach_port_set_protected_payload. --- include/mach/mach_port.defs | 9 + ipc/mach_port.c | 37 + 2 files changed, 46 insertions(+) diff --git a/include/mach/mach_port.defs b/include/mach/mach_port.defs index 769d892..96a5987 100644 --- a/include/mach/mach_port.defs +++ b/include/mach/mach_port.defs @@ -349,3 +349,12 @@ skip; /* mach_port_create_act */ #endif /* MIGRATING_THREADS */ +/* + * Only valid for receive rights. + * Set the protected payload for this right to the given value. + */ + +routine mach_port_set_protected_payload( + task: ipc_space_t; + name: mach_port_name_t; + payload : natural_t); diff --git a/ipc/mach_port.c b/ipc/mach_port.c index 13572a1..14c3653 100644 --- a/ipc/mach_port.c +++ b/ipc/mach_port.c @@ -1564,3 +1564,40 @@ mach_port_set_syscall_right(task, name) } #endif #endif /* MIGRATING_THREADS */ + +/* + * Routine:mach_port_set_protected_payload [kernel call] + * Purpose: + * Changes a receive right's protected payload. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESSSet protected payload. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT Name doesn't denote receive rights. + */ + +kern_return_t +mach_port_set_protected_payload( + ipc_space_t space, + mach_port_t name, + unsigned long payload) +{ + ipc_port_t port; + kern_return_t kr; + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) + return kr; + /* port is locked and active */ + + ipc_port_set_protected_payload(port, payload); + + ip_unlock(port); + return KERN_SUCCESS; +} -- 1.8.5.2
[PATCH 04/11] doc: document mach_port_set_protected_payload
* doc/mach.texi (Receive Rights): Document mach_port_set_protected_payload. --- doc/mach.texi | 18 ++ 1 file changed, 18 insertions(+) diff --git a/doc/mach.texi b/doc/mach.texi index d089224..67c5fe9 100644 --- a/doc/mach.texi +++ b/doc/mach.texi @@ -2715,6 +2715,24 @@ In addition to the normal diagnostic return codes from the call's server (normally the kernel), the call may return @code{mach_msg} return codes. @end deftypefun +@deftypefun kern_return_t mach_port_set_protected_payload (@w{ipc_space_t @var{task}}, @w{mach_port_t @var{name}}, @w{unsigned long @var{payload}}) +The function @code{mach_port_set_protected_payload} sets the protected +payload to @var{payload}. If @var{payload} is non-zero, the +@code{msgh_protected_payload} field will be set to @var{payload} if a +message is delivered to @var{name}. + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_TASK} if @var{task} was invalid, +@code{KERN_INVALID_NAME} if @var{name} did not denote a right and +@code{KERN_INVALID_RIGHT} if @var{name} denoted a right, but not a +receive right. + +The @code{mach_port_set_protected_payload} call is actually an RPC to +@var{task}, normally a send right for a task port, but potentially any +send right. In addition to the normal diagnostic return codes from +the call's server (normally the kernel), the call may return +@code{mach_msg} return codes. +@end deftypefun @node Port Sets @subsection Port Sets -- 1.8.5.2
[PATCH 01/11] include: skip routines related to migrating threads
* include/mach/mach_port.defs: Skip the routines mach_port_set_rpcinfo and mach_port_create_act if MIGRATING_THREADS is not defined. --- include/mach/mach_port.defs | 5 + 1 file changed, 5 insertions(+) diff --git a/include/mach/mach_port.defs b/include/mach/mach_port.defs index e1f45e3..769d892 100644 --- a/include/mach/mach_port.defs +++ b/include/mach/mach_port.defs @@ -342,5 +342,10 @@ routine mach_port_create_act( user_rbuf_size : vm_size_t; out new_act : thread_t); +#else /* MIGRATING_THREADS */ + +skip; /* mach_port_set_rpcinfo */ +skip; /* mach_port_create_act */ + #endif /* MIGRATING_THREADS */ -- 1.8.5.2
[PATCH 02/11] ipc: add protected payload
* ipc/ipc_port.c (ipc_port_init): Initialize protected_payload. (ipc_port_print): Print protected_payload. (ipc_port_set_protected_payload): New function. * ipc/ipc_port.h (struct ipc_port): Add field protected_payload. (ipc_port_set_protected_payload): Add function declaration. --- ipc/ipc_port.c | 22 ++ ipc/ipc_port.h | 4 2 files changed, 26 insertions(+) diff --git a/ipc/ipc_port.c b/ipc/ipc_port.c index d4ade8e..4e6d598 100644 --- a/ipc/ipc_port.c +++ b/ipc/ipc_port.c @@ -425,6 +425,25 @@ ipc_port_set_seqno(port, seqno) } /* + * Routine:ipc_port_set_protected_payload + * Purpose: + * Changes a port's protected payload. + * Conditions: + * The port is locked and active. + */ + +void +ipc_port_set_protected_payload(ipc_port_t port, unsigned long payload) +{ + ipc_mqueue_t mqueue; + + mqueue = ipc_port_lock_mqueue(port); + port->ip_protected_payload = payload; + imq_unlock(mqueue); +} + + +/* * Routine:ipc_port_clear_receiver * Purpose: * Prepares a receive right for transmission/destruction. @@ -493,6 +512,7 @@ ipc_port_init( port->ip_seqno = 0; port->ip_msgcount = 0; port->ip_qlimit = MACH_PORT_QLIMIT_DEFAULT; + port->ip_protected_payload = 0; ipc_mqueue_init(&port->ip_messages); ipc_thread_queue_init(&port->ip_blocked); @@ -1240,6 +1260,8 @@ ipc_port_print(port) printf(", sndrs=0x%x", port->ip_blocked.ithq_base); printf(", kobj=0x%x\n", port->ip_kobject); + iprintf("protected_payload=%p\n", (void *) port->ip_protected_payload); + indent -= 2; } diff --git a/ipc/ipc_port.h b/ipc/ipc_port.h index 27d2e49..a1a0943 100644 --- a/ipc/ipc_port.h +++ b/ipc/ipc_port.h @@ -96,6 +96,7 @@ struct ipc_port { mach_port_msgcount_t ip_msgcount; mach_port_msgcount_t ip_qlimit; struct ipc_thread_queue ip_blocked; + unsigned long ip_protected_payload; }; #define ip_object ip_target.ipt_object @@ -262,6 +263,9 @@ extern void ipc_port_set_seqno(ipc_port_t, mach_port_seqno_t); extern void +ipc_port_set_protected_payload(ipc_port_t, unsigned long); + +extern void ipc_port_clear_receiver(ipc_port_t); extern void -- 1.8.5.2
[PATCH 07/11] include: define MACH_MSG_TYPE_PROTECTED_PAYLOAD
* include/mach/message.h: Define MACH_MSG_TYPE_PROTECTED_PAYLOAD. (MACH_MSG_TYPE_LAST): Adjust accordingly. --- include/mach/message.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/mach/message.h b/include/mach/message.h index 7464a57..0a7297e 100644 --- a/include/mach/message.h +++ b/include/mach/message.h @@ -256,7 +256,9 @@ typedef struct { #define MACH_MSG_TYPE_PORT_SENDMACH_MSG_TYPE_MOVE_SEND #define MACH_MSG_TYPE_PORT_SEND_ONCE MACH_MSG_TYPE_MOVE_SEND_ONCE -#define MACH_MSG_TYPE_LAST 22 /* Last assigned */ +#define MACH_MSG_TYPE_PROTECTED_PAYLOAD23 + +#define MACH_MSG_TYPE_LAST 23 /* Last assigned */ /* * A dummy value. Mostly used to indicate that the actual value -- 1.8.5.2
[PATCH 10/11] ipc: clear the payload when moving a receive port
Clear the protected payload when a receive port is moved from one ipc space to another. This is done to retain the old behavior of mach_msg, so that a port name is sent in the msgh_local_port field. If the new owner of that receive right wishes to use the protected payload mechanism, it has to be explicitly set with mach_port_set_protected_payload. * ipc/ipc_right.c (ipc_right_copyin): Clear the payload when moving a receive port. (ipc_right_copyout): Likewise. * ipc/ipc_port.c (ipc_port_destroy): Likewise. * ipc/ipc_object.c (ipc_object_copyin_from_kernel): Likewise. --- ipc/ipc_object.c | 1 + ipc/ipc_port.c | 1 + ipc/ipc_right.c | 12 3 files changed, 14 insertions(+) diff --git a/ipc/ipc_object.c b/ipc/ipc_object.c index 982bd4e..59f5a67 100644 --- a/ipc/ipc_object.c +++ b/ipc/ipc_object.c @@ -481,6 +481,7 @@ ipc_object_copyin_from_kernel( port->ip_receiver_name = MACH_PORT_NULL; port->ip_destination = IP_NULL; + port->ip_protected_payload = 0; ip_unlock(port); break; } diff --git a/ipc/ipc_port.c b/ipc/ipc_port.c index 4e6d598..4051746 100644 --- a/ipc/ipc_port.c +++ b/ipc/ipc_port.c @@ -635,6 +635,7 @@ ipc_port_destroy( /* make port be in limbo */ port->ip_receiver_name = MACH_PORT_NULL; port->ip_destination = IP_NULL; + port->ip_protected_payload = 0; ip_unlock(port); if (!ipc_port_check_circularity(port, pdrequest)) { diff --git a/ipc/ipc_right.c b/ipc/ipc_right.c index 41fe3de..1edbb47 100644 --- a/ipc/ipc_right.c +++ b/ipc/ipc_right.c @@ -1432,6 +1432,12 @@ ipc_right_copyin( port->ip_receiver_name = MACH_PORT_NULL; port->ip_destination = IP_NULL; + + /* +* Clear the protected payload field to retain +* the behavior of mach_msg. +*/ + port->ip_protected_payload = 0; ip_unlock(port); *objectp = (ipc_object_t) port; @@ -1932,6 +1938,12 @@ ipc_right_copyout( port->ip_receiver_name = name; port->ip_receiver = space; + /* +* Clear the protected payload field to retain +* the behavior of mach_msg. +*/ + port->ip_protected_payload = 0; + assert((bits & MACH_PORT_TYPE_RECEIVE) == 0); if (bits & MACH_PORT_TYPE_SEND) { -- 1.8.5.2
[PATCH 05/11] include: add msgh_protected_payload to mach_msg_header_t
* include/mach/message.h (mach_msg_header_t): Add msgh_protected_payload as a union with msgh_local_port. --- include/mach/message.h | 5 - 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/mach/message.h b/include/mach/message.h index f78e978..7464a57 100644 --- a/include/mach/message.h +++ b/include/mach/message.h @@ -136,7 +136,10 @@ typedefstruct { mach_msg_bits_tmsgh_bits; mach_msg_size_tmsgh_size; mach_port_tmsgh_remote_port; -mach_port_tmsgh_local_port; +union { +mach_port_tmsgh_local_port; +unsigned long msgh_protected_payload; +}; mach_port_seqno_t msgh_seqno; mach_msg_id_t msgh_id; } mach_msg_header_t; -- 1.8.5.2
[PATCH 09/11] ipc: provide the protected payload in ipc_kmsg_copyout_header
* ipc/ipc_kmsg.c (ipc_kmsg_copyout_header): If a protected payload is set for the destination port, provide it in msgh_protected_payload. --- ipc/ipc_kmsg.c | 58 +- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/ipc/ipc_kmsg.c b/ipc/ipc_kmsg.c index 0e43410..aa51100 100644 --- a/ipc/ipc_kmsg.c +++ b/ipc/ipc_kmsg.c @@ -1802,9 +1802,17 @@ ipc_kmsg_copyout_header(msg, space, notify) } else ip_unlock(dest); - msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | - MACH_MSGH_BITS(0, MACH_MSG_TYPE_PORT_SEND)); - msg->msgh_local_port = dest_name; + if (dest->ip_protected_payload == 0) { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(0, MACH_MSG_TYPE_PORT_SEND)); + msg->msgh_local_port = dest_name; + } else { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS( + 0, MACH_MSG_TYPE_PROTECTED_PAYLOAD)); + msg->msgh_protected_payload = \ + dest->ip_protected_payload; + } msg->msgh_remote_port = MACH_PORT_NULL; return MACH_MSG_SUCCESS; } @@ -1900,10 +1908,18 @@ ipc_kmsg_copyout_header(msg, space, notify) } else ip_unlock(dest); - msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | - MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, -MACH_MSG_TYPE_PORT_SEND)); - msg->msgh_local_port = dest_name; + if (dest->ip_protected_payload == 0) { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, + MACH_MSG_TYPE_PORT_SEND)); + msg->msgh_local_port = dest_name; + } else { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, + MACH_MSG_TYPE_PROTECTED_PAYLOAD)); + msg->msgh_protected_payload = \ + dest->ip_protected_payload; + } msg->msgh_remote_port = reply_name; return MACH_MSG_SUCCESS; } @@ -1935,9 +1951,18 @@ ipc_kmsg_copyout_header(msg, space, notify) dest_name = MACH_PORT_NULL; } - msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | - MACH_MSGH_BITS(0, MACH_MSG_TYPE_PORT_SEND_ONCE)); - msg->msgh_local_port = dest_name; + if (dest->ip_protected_payload == 0) { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(0, + MACH_MSG_TYPE_PORT_SEND_ONCE)); + msg->msgh_local_port = dest_name; + } else { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(0, + MACH_MSG_TYPE_PROTECTED_PAYLOAD)); + msg->msgh_protected_payload = \ + dest->ip_protected_payload; + } msg->msgh_remote_port = MACH_PORT_NULL; return MACH_MSG_SUCCESS; } @@ -2227,9 +2252,16 @@ ipc_kmsg_copyout_header(msg, space, notify) if (IP_VALID(reply)) ipc_port_release(reply); - msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | - MACH_MSGH_BITS(reply_type, dest_type)); - msg->msgh_local_port = dest_name; + if (dest->ip_protected_payload == 0) { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(reply_type, dest_type)); + msg->msgh_local_port = dest_name; + } else { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(reply_type, + MACH_MSG_TYPE_PROTECTED_PAYLOAD)); + msg->msgh_protected_payload = dest->ip_protected_payload; + } msg->msgh_remote_port = reply_name; } -- 1.8.5.2
[PATCH 08/11] doc: document MACH_MSG_TYPE_PROTECTED_PAYLOAD
* doc/mach.texi (Message Format): Document MACH_MSG_TYPE_PROTECTED_PAYLOAD. --- doc/mach.texi | 6 ++ 1 file changed, 6 insertions(+) diff --git a/doc/mach.texi b/doc/mach.texi index 251c290..26bf0c6 100644 --- a/doc/mach.texi +++ b/doc/mach.texi @@ -1426,6 +1426,7 @@ types are predefined: @item MACH_MSG_TYPE_STRING @item MACH_MSG_TYPE_STRING_C @item MACH_MSG_TYPE_PORT_NAME +@item MACH_MSG_TYPE_PROTECTED_PAYLOAD @end table The following predefined types specify port rights, and receive special @@ -1444,6 +1445,11 @@ should be used in preference to @code{MACH_MSG_TYPE_INTEGER_32}. @item MACH_MSG_TYPE_MAKE_SEND_ONCE @end table +The type @code{MACH_MSG_TYPE_PROTECTED_PAYLOAD} is used by the kernel +to indicate that a delivered message carries a payload in the +@code{msgh_protected_payload} field. See @ref{Message Receive} for +more information. + @item msgt_size : 8 The @code{msgt_size} field specifies the size of each datum, in bits. For example, the msgt_size of @code{MACH_MSG_TYPE_INTEGER_32} data is 32. -- 1.8.5.2
[PATCH 06/11] doc: document msgh_protected_payload
* doc/mach.texi (Message Format): Document msgh_protected_payload. --- doc/mach.texi | 9 + 1 file changed, 9 insertions(+) diff --git a/doc/mach.texi b/doc/mach.texi index 67c5fe9..251c290 100644 --- a/doc/mach.texi +++ b/doc/mach.texi @@ -1330,6 +1330,15 @@ which is conventionally used as a reply port by the recipient of the message. The field must carry a send right, a send-once right, @code{MACH_PORT_NULL}, or @code{MACH_PORT_DEAD}. +@item mach_port_t msgh_protected_payload +The @code{msgh_protected_payload} field carries a payload that is set +by the kernel during message delivery. The payload is an opaque +identifier that can be used by the receiver to lookup the associated +data structure. + +It is only valid in received messages. See @ref{Message Receive} for +further information. + @item mach_port_seqno_t msgh_seqno The @code{msgh_seqno} field provides a sequence number for the message. It is only valid in received messages; its value in sent messages is -- 1.8.5.2
[PATCH 11/11] doc: document message semantics with protected payloads
* doc/mach.texi (Message Receive): Document message semantics with protected payloads. --- doc/mach.texi | 19 +++ 1 file changed, 19 insertions(+) diff --git a/doc/mach.texi b/doc/mach.texi index 26bf0c6..b3cc2ec 100644 --- a/doc/mach.texi +++ b/doc/mach.texi @@ -1949,6 +1949,25 @@ loses the receive right after the message was dequeued from it, then right still exists, but isn't held by the caller, then @code{msgh_local_port} specifies @code{MACH_PORT_NULL}. +Servers usually associate some state with a receive right. To that +end, they might use a hash table to look up the state for the port a +message was sent to. To optimize this, a task may associate an opaque +@var{payload} with a receive right using the +@code{mach_port_set_protected_payload} function. Once this is done, +the kernel will set the @code{msgh_protected_payload} field to +@var{payload} when delivering a message to this right and indicate +this by setting the local part of @code{msgh_bits} to +@code{MACH_MSG_TYPE_PROTECTED_PAYLOAD}. + +The support for protected payloads was added to GNU Mach. To preserve +binary compatibility, the @code{msgh_local_port} and +@code{msgh_local_port} share the same location. This makes it +possible to add the payload information without increasing the size of +@code{mach_msg_header_t}. This is an implementation detail. Which +field is valid is determined by the local part of the +@code{msgh_bits}. Existing software is not affected. When a receive +right is transferred to another task, its payload is cleared. + Received messages are stamped with a sequence number, taken from the port from which the message was received. (Messages received from a port set are stamped with a sequence number from the appropriate member -- 1.8.5.2
Re: [PATCH 04/11] doc: document mach_port_set_protected_payload
On Mon, Feb 17, 2014 at 06:20:54PM +0100, Justus Winter wrote: > +@deftypefun kern_return_t mach_port_set_protected_payload (@w{ipc_space_t > @var{task}}, @w{mach_port_t @var{name}}, @w{unsigned long @var{payload}}) > +The function @code{mach_port_set_protected_payload} sets the protected > +payload to @var{payload}. If @var{payload} is non-zero, the > +@code{msgh_protected_payload} field will be set to @var{payload} if a > +message is delivered to @var{name}. If I'm right, this also means switching back from the protected payload is done by calling this RPC with a payload of 0. It could be worth emphasizing that 0 is an invalid value for a protected payload. -- Richard Braun