Mach allows messages to carry port rights along with data; the rights
contained in a message are transferred from the sender's IPC space to
the receiver.  The sender has to specify exactly how a right that the
message will carry is to be created from the right(s) that the sender
holds: in particular, a send right can be made (created) from a receive
right, copied from another send right, or a send right can be *moved*,
in which case the sender loses its right.

Send-once rights, new in Mach 3, similarly can be made from a receive
right, or moved.  However, it would appear that the original Mach
designers forgot to implement the functionality of *copying* a send-once
right when sending it in a message.

This patch fixes the oversight by adding the missing
MACH_MSG_TYPE_COPY_SEND_ONCE (aka copy-sonce) message type.  It behaves
very similar to MACH_MSG_TYPE_COPY_SEND: the sender must supply an
existing send-once right, so count of send-once references to the port
(ip_sorights) is incremented, and the new send-once right gets carried
in the message.  The receiver sees it as MACH_MSG_TYPE_PORT_SEND_ONCE
(equal to MACH_MSG_TYPE_MOVE_SEND_ONCE), it cannot tell (and should not
care) whether the sender has used make-sonce, move-sonce, or copy-sonce.
It is also possible to supply a dead name when using copy-sonce (no
matter whether the name originally denoted a send or send-once right);
in this case the receiver will get MACH_PORT_DEAD instead of any right,
and the sender keeps its dead name reference.

This patch also adds the mach_port_copy_send_once_t MIG type, which can
be used in routine definitions.  A corresponding MIG patch is required
to make MIG recognise MACH_MSG_TYPE_COPY_SEND_ONCE as a valid type.  To
bootstrap support for MACH_MSG_TYPE_COPY_SEND_ONCE in Mach and MIG, you
should run 'make install-data' in Mach first, then build and install MIG
with make-sonce support, the build Mach.

Note that the other potential COPY type, MACH_MSG_TYPE_COPY_RECEIVE,
makes no sense for obvious reasons: there can only ever be one receive
right for a port, and Mach relies on this being true in a number of
places (e.g. ip_receiver/ip_receiver_name).

Note also that fully supporting MACH_MSG_TYPE_COPY_SEND_ONCE in Mach is
required to take advantage of hardware port translation acceleration on
AArch64 CPUs that implement the FEAT_AFD extension: the MPTTR_EL1 (Mach
Port Translation Type Register) system register must hold a value of
port translation type, which can include MACH_MSG_TYPE_COPY_SEND_ONCE.
---
 doc/mach.texi               |  1 +
 include/mach/mach_port.defs |  1 +
 include/mach/message.h      | 18 +++++++----
 include/mach/std_types.defs |  2 ++
 ipc/ipc_kmsg.c              |  3 ++
 ipc/ipc_object.c            | 21 +++++++++++-
 ipc/ipc_right.c             | 64 +++++++++++++++++++++++++++++++++----
 7 files changed, 95 insertions(+), 15 deletions(-)

diff --git a/doc/mach.texi b/doc/mach.texi
index f85288e0..317a5930 100644
--- a/doc/mach.texi
+++ b/doc/mach.texi
@@ -1445,6 +1445,7 @@ should be used in preference to 
@code{MACH_MSG_TYPE_INTEGER_32}.
 @item MACH_MSG_TYPE_COPY_SEND
 @item MACH_MSG_TYPE_MAKE_SEND
 @item MACH_MSG_TYPE_MAKE_SEND_ONCE
+@item MACH_MSG_TYPE_COPY_SEND_ONCE
 @end table
 
 The type @code{MACH_MSG_TYPE_PROTECTED_PAYLOAD} is used by the kernel
diff --git a/include/mach/mach_port.defs b/include/mach/mach_port.defs
index 3823bb14..c291a268 100644
--- a/include/mach/mach_port.defs
+++ b/include/mach/mach_port.defs
@@ -277,6 +277,7 @@ routine mach_port_insert_right(
  *             MACH_MSG_TYPE_MOVE_SEND
  *             MACH_MSG_TYPE_MAKE_SEND_ONCE
  *             MACH_MSG_TYPE_MOVE_SEND_ONCE
+ *             MACH_MSG_TYPE_COPY_SEND_ONCE
  */
 
 routine mach_port_extract_right(
diff --git a/include/mach/message.h b/include/mach/message.h
index 9790ef98..aa068cab 100644
--- a/include/mach/message.h
+++ b/include/mach/message.h
@@ -357,8 +357,9 @@ _Static_assert (sizeof (mach_msg_type_t) == sizeof 
(mach_msg_type_long_t),
 #define MACH_MSG_TYPE_PORT_SEND_ONCE   MACH_MSG_TYPE_MOVE_SEND_ONCE
 
 #define MACH_MSG_TYPE_PROTECTED_PAYLOAD        23
+#define MACH_MSG_TYPE_COPY_SEND_ONCE   24      /* Must hold send-once rights */
 
-#define MACH_MSG_TYPE_LAST             23              /* Last assigned */
+#define MACH_MSG_TYPE_LAST             24      /* Last assigned */
 
 /*
  *  A dummy value.  Mostly used to indicate that the actual value
@@ -372,16 +373,19 @@ _Static_assert (sizeof (mach_msg_type_t) == sizeof 
(mach_msg_type_long_t),
  */
 
 #define MACH_MSG_TYPE_PORT_ANY(x)                      \
-       (((x) >= MACH_MSG_TYPE_MOVE_RECEIVE) &&         \
-        ((x) <= MACH_MSG_TYPE_MAKE_SEND_ONCE))
+       ((((x) >= MACH_MSG_TYPE_MOVE_RECEIVE) &&        \
+        ((x) <= MACH_MSG_TYPE_MAKE_SEND_ONCE)) ||      \
+        ((x) == MACH_MSG_TYPE_COPY_SEND_ONCE))
 
 #define        MACH_MSG_TYPE_PORT_ANY_SEND(x)                  \
-       (((x) >= MACH_MSG_TYPE_MOVE_SEND) &&            \
-        ((x) <= MACH_MSG_TYPE_MAKE_SEND_ONCE))
+       ((((x) >= MACH_MSG_TYPE_MOVE_SEND) &&           \
+        ((x) <= MACH_MSG_TYPE_MAKE_SEND_ONCE)) ||      \
+        ((x) == MACH_MSG_TYPE_COPY_SEND_ONCE))
 
 #define        MACH_MSG_TYPE_PORT_ANY_RIGHT(x)                 \
-       (((x) >= MACH_MSG_TYPE_MOVE_RECEIVE) &&         \
-        ((x) <= MACH_MSG_TYPE_MOVE_SEND_ONCE))
+       ((((x) >= MACH_MSG_TYPE_MOVE_RECEIVE) &&        \
+        ((x) <= MACH_MSG_TYPE_MOVE_SEND_ONCE)) ||      \
+        ((x) == MACH_MSG_TYPE_COPY_SEND_ONCE))
 
 typedef integer_t mach_msg_option_t;
 
diff --git a/include/mach/std_types.defs b/include/mach/std_types.defs
index b461f062..a687bdb5 100644
--- a/include/mach/std_types.defs
+++ b/include/mach/std_types.defs
@@ -85,6 +85,8 @@ type mach_port_make_send_once_t =     
MACH_MSG_TYPE_MAKE_SEND_ONCE
        ctype: mach_port_t;
 type mach_port_move_send_once_t =      MACH_MSG_TYPE_MOVE_SEND_ONCE
        ctype: mach_port_t;
+type mach_port_copy_send_once_t =      MACH_MSG_TYPE_COPY_SEND_ONCE
+       ctype: mach_port_t;
 
 type mach_port_receive_t =             MACH_MSG_TYPE_PORT_RECEIVE
        ctype: mach_port_t;
diff --git a/ipc/ipc_kmsg.c b/ipc/ipc_kmsg.c
index 179c43fa..6109fcb8 100644
--- a/ipc/ipc_kmsg.c
+++ b/ipc/ipc_kmsg.c
@@ -2731,6 +2731,9 @@ ipc_type_name(
                case MACH_MSG_TYPE_MAKE_SEND_ONCE:
                return "make_send_once";
 
+               case MACH_MSG_TYPE_COPY_SEND_ONCE:
+               return "copy_send_once";
+
                default:
                return (char *) 0;
        }
diff --git a/ipc/ipc_object.c b/ipc/ipc_object.c
index 1074fb2c..fe84e7d0 100644
--- a/ipc/ipc_object.c
+++ b/ipc/ipc_object.c
@@ -370,6 +370,7 @@ ipc_object_copyin_type(
 
            case MACH_MSG_TYPE_MOVE_SEND_ONCE:
            case MACH_MSG_TYPE_MAKE_SEND_ONCE:
+           case MACH_MSG_TYPE_COPY_SEND_ONCE:
                return MACH_MSG_TYPE_PORT_SEND_ONCE;
 
            case MACH_MSG_TYPE_MOVE_SEND:
@@ -416,7 +417,8 @@ ipc_object_copyin(
        /*
         *      Could first try a read lock when doing
         *      MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND,
-        *      and MACH_MSG_TYPE_MAKE_SEND_ONCE.
+        *      MACH_MSG_TYPE_COPY_SEND_ONCE, and
+        *      MACH_MSG_TYPE_MAKE_SEND_ONCE.
         */
 
        kr = ipc_right_lookup_write(space, name, &entry);
@@ -459,6 +461,10 @@ ipc_object_copyin(
  *                     The port gains a reference and a send-once right.
  *             MACH_MSG_TYPE_MOVE_SEND_ONCE
  *                     Consumes a naked send-once right.
+ *             MACH_MSG_TYPE_COPY_SEND_ONCE
+ *                     A naked send-once right must be supplied.
+ *                     The port gains a reference, and a send-once
+ *                     right if the port is still active.
  *     Conditions:
  *             Nothing locked.
  */
@@ -539,6 +545,19 @@ ipc_object_copyin_from_kernel(
                /* move naked send-once right into the message */
                break;
 
+           case MACH_MSG_TYPE_COPY_SEND_ONCE: {
+               ipc_port_t port = (ipc_port_t) object;
+
+               ip_lock(port);
+               if (ip_active(port)) {
+                       assert(port->ip_sorights > 0);
+                       port->ip_sorights++;
+               }
+               ip_reference(port);
+               ip_unlock(port);
+               break;
+           }
+
            default:
 #if MACH_ASSERT
                assert(!"ipc_object_copyin_from_kernel: strange rights");
diff --git a/ipc/ipc_right.c b/ipc/ipc_right.c
index 79f70c3d..829e9b05 100644
--- a/ipc/ipc_right.c
+++ b/ipc/ipc_right.c
@@ -1258,7 +1258,8 @@ ipc_right_copyin_check(
 
            case MACH_MSG_TYPE_COPY_SEND:
            case MACH_MSG_TYPE_MOVE_SEND:
-           case MACH_MSG_TYPE_MOVE_SEND_ONCE: {
+           case MACH_MSG_TYPE_MOVE_SEND_ONCE:
+           case MACH_MSG_TYPE_COPY_SEND_ONCE: {
                ipc_port_t port;
                boolean_t active;
 
@@ -1279,7 +1280,8 @@ ipc_right_copyin_check(
                        break;
                }
 
-               if (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE) {
+               if (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE ||
+                   msgt_name == MACH_MSG_TYPE_COPY_SEND_ONCE) {
                        if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0)
                                return FALSE;
                } else {
@@ -1600,6 +1602,49 @@ ipc_right_copyin(
                break;
            }
 
+           case MACH_MSG_TYPE_COPY_SEND_ONCE: {
+               ipc_port_t port;
+
+               if (bits & MACH_PORT_TYPE_DEAD_NAME)
+                       goto copy_dead;
+
+               if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0)
+                       goto invalid_right;
+
+               assert(IE_BITS_UREFS(bits) > 0);
+
+               port = (ipc_port_t) entry->ie_object;
+               assert(port != IP_NULL);
+
+               if (ipc_right_check(space, port, name, entry)) {
+                       bits = entry->ie_bits;
+                       goto copy_dead;
+               }
+
+               /* port is locked and active */
+
+               if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) {
+                       assert(bits & MACH_PORT_TYPE_SEND);
+                       assert(port->ip_srights > 0);
+
+                       ip_unlock(port);
+                       goto invalid_right;
+               }
+
+               assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
+               assert(IE_BITS_UREFS(bits) == 1);
+               assert((bits & IE_BITS_MAREQUEST) == 0);
+               assert(port->ip_sorights > 0);
+
+               port->ip_sorights++;
+               ip_reference(port);
+               ip_unlock(port);
+
+               *objectp = (ipc_object_t) port;
+               *sorightp = IP_NULL;
+               break;
+           }
+
            default:
 #if MACH_ASSERT
                assert(!"ipc_right_copyin: strange rights");
@@ -1672,7 +1717,8 @@ ipc_right_copyin_undo(
 
        assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) ||
               (msgt_name == MACH_MSG_TYPE_COPY_SEND) ||
-              (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE));
+              (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE) ||
+              (msgt_name == MACH_MSG_TYPE_COPY_SEND_ONCE));
 
        if (soright != IP_NULL) {
                assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) ||
@@ -1695,20 +1741,24 @@ ipc_right_copyin_undo(
                assert(object == IO_DEAD);
                assert(IE_BITS_UREFS(bits) > 0);
 
-               if (msgt_name != MACH_MSG_TYPE_COPY_SEND) {
+               if (msgt_name != MACH_MSG_TYPE_COPY_SEND &&
+                   msgt_name != MACH_MSG_TYPE_COPY_SEND_ONCE) {
                        assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX);
 
                        entry->ie_bits = bits+1; /* increment urefs */
                }
        } else {
                assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) ||
-                      (msgt_name == MACH_MSG_TYPE_COPY_SEND));
-               assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND);
+                      (msgt_name == MACH_MSG_TYPE_COPY_SEND) ||
+                      (msgt_name == MACH_MSG_TYPE_COPY_SEND_ONCE));
+               assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND ||
+                      IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
                assert(object != IO_DEAD);
                assert(entry->ie_object == object);
                assert(IE_BITS_UREFS(bits) > 0);
 
-               if (msgt_name != MACH_MSG_TYPE_COPY_SEND) {
+               if (msgt_name != MACH_MSG_TYPE_COPY_SEND &&
+                   msgt_name != MACH_MSG_TYPE_COPY_SEND_ONCE) {
                        assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX-1);
 
                        entry->ie_bits = bits+1; /* increment urefs */
-- 
2.44.0


Reply via email to