bug-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH gnumach] x86_64: Support 8 byte inlined port rights to avoid mess


From: Flavio Cruz
Subject: [PATCH gnumach] x86_64: Support 8 byte inlined port rights to avoid message resizing.
Date: Thu, 30 Nov 2023 02:08:01 -0500

If a port is inlined in a message, the user has to use
mach_port_name_inlined_t to define each port. Out of line memory
continues to use mach_port_name_t since that memory has to be copied to
the kernel anyway.

Both copyinmsg and copyoutmsg can be reduced to nothing (if we ignore
USER32) as a follow up but kept this patch simple for ease of review.
---
Now returning errors instead of failing assertions.

 i386/i386/copy_user.h  | 12 ++++----
 include/mach/message.h | 18 ++++++++++++
 x86_64/copy_user.c     | 67 +++++++++++++++++++++++++++++++++++++-----
 3 files changed, 83 insertions(+), 14 deletions(-)

diff --git a/i386/i386/copy_user.h b/i386/i386/copy_user.h
index 5cdbfa80..3d1c7278 100644
--- a/i386/i386/copy_user.h
+++ b/i386/i386/copy_user.h
@@ -87,16 +87,14 @@ static inline int copyout_port(const mach_port_t *kaddr, 
mach_port_name_t *uaddr
 #endif /* __x86_64__ */
 }
 
-// XXX we could add another field to kmsg to store the user-side size, but 
then we
-// should check if we can  obtain it for rpc and notifications originating from
-// the kernel
-#ifndef __x86_64__
+#if defined(__x86_64__) && defined(USER32)
+/* For 32 bit userland, kernel and user land messages are not the same size. */
+size_t msg_usize(const mach_msg_header_t *kmsg);
+#else
 static inline size_t msg_usize(const mach_msg_header_t *kmsg)
 {
   return kmsg->msgh_size;
 }
-#else /* __x86_64__ */
-size_t msg_usize(const mach_msg_header_t *kmsg);
-#endif /* __x86_64__ */
+#endif /* __x86_64__ && USER32 */
 
 #endif /* COPY_USER_H */
diff --git a/include/mach/message.h b/include/mach/message.h
index 17d3592f..21e99896 100644
--- a/include/mach/message.h
+++ b/include/mach/message.h
@@ -221,6 +221,24 @@ typedef unsigned int mach_msg_type_name_t;
 typedef unsigned int mach_msg_type_size_t;
 typedef natural_t  mach_msg_type_number_t;
 
+/**
+ * Structure used for inlined port rights in messages.
+ *
+ * We use this to avoid having to perform message resizing in the kernel
+ * since userspace port rights might be smaller than kernel ports in 64 bit
+ * architectures.
+ */
+typedef struct {
+    union {
+        mach_port_name_t name;
+#ifdef KERNEL
+        mach_port_t kernel_port;
+#else
+        uintptr_t kernel_port_do_not_use;
+#endif  /* KERNEL */
+    };
+} mach_port_name_inlined_t;
+
 typedef struct  {
 #ifdef __x86_64__
     /*
diff --git a/x86_64/copy_user.c b/x86_64/copy_user.c
index 0d3f301b..c6e125d9 100644
--- a/x86_64/copy_user.c
+++ b/x86_64/copy_user.c
@@ -265,8 +265,15 @@ static inline int copyout_unpack_msg_type(vm_offset_t 
kaddr,
       mach_msg_type_size_t orig_size = kmtl->msgtl_size;
       int ret;
 
-      if (MACH_MSG_TYPE_PORT_ANY(kmtl->msgtl_name))
+      if (MACH_MSG_TYPE_PORT_ANY(kmtl->msgtl_name)) {
+#ifdef USER32
         kmtl->msgtl_size = bytes_to_descsize(sizeof(mach_port_name_t));
+#else
+        /* 64 bit ABI uses mach_port_name_inlined_t for inlined ports. */
+        if (!kmt->msgt_inline)
+          kmtl->msgtl_size = bytes_to_descsize(sizeof(mach_port_name_t));
+#endif
+      }
       ret = copyout_mach_msg_type_long(kmtl, (void*)uaddr);
       kmtl->msgtl_size = orig_size;
       if (ret)
@@ -283,8 +290,15 @@ static inline int copyout_unpack_msg_type(vm_offset_t 
kaddr,
       mach_msg_type_size_t orig_size = kmt->msgt_size;
       int ret;
 
-      if (MACH_MSG_TYPE_PORT_ANY(kmt->msgt_name))
+      if (MACH_MSG_TYPE_PORT_ANY(kmt->msgt_name)) {
+#ifdef USER32
         kmt->msgt_size = bytes_to_descsize(sizeof(mach_port_name_t));
+#else
+        /* 64 bit ABI uses mach_port_name_inlined_t for inlined ports. */
+        if (!kmt->msgt_inline)
+          kmt->msgt_size = bytes_to_descsize(sizeof(mach_port_name_t));
+#endif
+      }
       ret = copyout_mach_msg_type(kmt, (void *)uaddr);
       kmt->msgt_size = orig_size;
       if (ret)
@@ -299,8 +313,10 @@ static inline int copyout_unpack_msg_type(vm_offset_t 
kaddr,
   return 0;
 }
 
+#ifdef USER32
 /*
- * Compute the user-space size of a message still in the kernel.
+ * Compute the user-space size of a message still in the kernel when processing
+ * messages from 32bit userland.
  * The message may be originating from userspace (in which case we could
  * optimize this by keeping the usize around) or from kernel space (we could
  * optimize if the message structure is fixed and known in advance).
@@ -333,7 +349,8 @@ size_t msg_usize(const mach_msg_header_t *kmsg)
             {
               if (MACH_MSG_TYPE_PORT_ANY(name))
                 {
-                  saddr += sizeof(mach_port_t) * number;
+                  const vm_size_t length = sizeof(mach_port_t) * number;
+                  saddr += length;
                   usize += sizeof(mach_port_name_t) * number;
                 }
               else
@@ -355,6 +372,7 @@ size_t msg_usize(const mach_msg_header_t *kmsg)
     }
   return usize;
 }
+#endif /* USER32 */
 
 /*
  * Expand the msg header and, if required, the msg body (ports, pointers)
@@ -423,6 +441,9 @@ int copyinmsg (const void *userbuf, void *kernelbuf, const 
size_t usize, const s
             {
               if (MACH_MSG_TYPE_PORT_ANY(name))
                 {
+#ifdef USER32
+                  if (size != bytes_to_descsize(sizeof(mach_port_name_t)))
+                    return 1;
                   if ((usaddr + sizeof(mach_port_name_t)*number) > ueaddr)
                     return 1;
                   adjust_msg_type_size(ktaddr, sizeof(mach_port_t) - 
sizeof(mach_port_name_t));
@@ -433,6 +454,17 @@ int copyinmsg (const void *userbuf, void *kernelbuf, const 
size_t usize, const s
                       ksaddr += sizeof(mach_port_t);
                       usaddr += sizeof(mach_port_name_t);
                     }
+#else
+                  if (size != 
bytes_to_descsize(sizeof(mach_port_name_inlined_t)))
+                    return 1;
+                  const vm_size_t length = number * 
sizeof(mach_port_name_inlined_t);
+                  if ((usaddr + length) > ueaddr)
+                    return 1;
+                  if (copyin((void*)usaddr, (void*)ksaddr, length))
+                    return 1;
+                  usaddr += length;
+                  ksaddr += length;
+#endif
                 }
               else
                 {
@@ -451,9 +483,13 @@ int copyinmsg (const void *userbuf, void *kernelbuf, const 
size_t usize, const s
               if ((usaddr + sizeof(rpc_vm_offset_t)) > ueaddr)
                 return 1;
 
-              // out-of-line port arrays are expanded in ipc_kmsg_copyin_body()
-              if (MACH_MSG_TYPE_PORT_ANY(name))
+              /* out-of-line port arrays are always arrays of mach_port_name_t 
(4 bytes)
+               * and are expanded in ipc_kmsg_copyin_body() */
+              if (MACH_MSG_TYPE_PORT_ANY(name)) {
+                if (size != bytes_to_descsize(sizeof(mach_port_name_t)))
+                  return 1;
                 adjust_msg_type_size(ktaddr, sizeof(mach_port_t) - 
sizeof(mach_port_name_t));
+              }
 
               if (copyin_address((rpc_vm_offset_t*)usaddr, 
(vm_offset_t*)ksaddr))
                 return 1;
@@ -470,6 +506,10 @@ int copyinmsg (const void *userbuf, void *kernelbuf, const 
size_t usize, const s
 
   kmsg->msgh_size = sizeof(mach_msg_header_t) + ksaddr - (vm_offset_t)(kmsg + 
1);
   assert(kmsg->msgh_size <= ksize);
+#ifndef USER32
+  if (kmsg->msgh_size != usize)
+    return 1;
+#endif
   return 0;
 }
 
@@ -519,6 +559,7 @@ int copyoutmsg (const void *kernelbuf, void *userbuf, const 
size_t ksize)
             {
               if (MACH_MSG_TYPE_PORT_ANY(name))
                 {
+#ifdef USER32
                   for (int i=0; i<number; i++)
                     {
                       if (copyout_port((mach_port_t*)ksaddr, 
(mach_port_name_t*)usaddr))
@@ -526,10 +567,18 @@ int copyoutmsg (const void *kernelbuf, void *userbuf, 
const size_t ksize)
                       ksaddr += sizeof(mach_port_t);
                       usaddr += sizeof(mach_port_name_t);
                     }
+#else
+                  if (size != 
bytes_to_descsize(sizeof(mach_port_name_inlined_t)))
+                    return 1;
+                  const vm_size_t length = number * 
sizeof(mach_port_name_inlined_t);
+                  if (copyout((void*)ksaddr, (void*)usaddr, length))
+                    return 1;
+                  ksaddr += length;
+                  usaddr += length;
+#endif
                 }
               else
                 {
-                  // type that doesn't need change
                   size_t n = descsize_to_bytes(size);
                   if (copyout((void*)ksaddr, (void*)usaddr, n*number))
                     return 1;
@@ -554,6 +603,10 @@ int copyoutmsg (const void *kernelbuf, void *userbuf, 
const size_t ksize)
   usize = sizeof(mach_msg_user_header_t) + usaddr - (vm_offset_t)(umsg + 1);
   if (copyout(&usize, &umsg->msgh_size, sizeof(umsg->msgh_size)))
     return 1;
+#ifndef USER32
+  if (usize != ksize)
+    return 1;
+#endif
 
   return 0;
 
-- 
2.39.2




reply via email to

[Prev in Thread] Current Thread [Next in Thread]