From: Andrew Cooper <andrew.cooper3@citrix.com>
Subject: [PATCH] Fix TOCTOU issues with mapped guest memory

memcpy() can be optimised by the compiler, leading to TOCTOU bugs with data in
guest memory.  Without these barriers, dispatch_command() compiles in a way
which is vulnerable to code injection.

This is XSA-478 / CVE-2025-58151

Reported-by: Teddy Astie <teddy.astie@vates.tech>
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Frediano Ziglio <frediano.ziglio@citrix.com>
Reviewed-by: Ross Lagerwall <ross.lagerwall@citrix.com>
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
---
 include/serialize.h | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/include/serialize.h b/include/serialize.h
index 04441ea8a372..1667868feab4 100644
--- a/include/serialize.h
+++ b/include/serialize.h
@@ -35,12 +35,15 @@
 #include "efi.h"
 #include "handler.h"
 
+#define barrier() asm volatile ("" ::: "memory")
+
 static inline enum command_t
 unserialize_command(uint8_t **ptr)
 {
     UINT32 data;
 
     memcpy(&data, *ptr, sizeof(data));
+    barrier();
     *ptr += sizeof data;
 
     return (enum command_t)data;
@@ -50,9 +53,11 @@ static inline void
 serialize_data(uint8_t **ptr, const uint8_t *data, UINTN data_len)
 {
     memcpy(*ptr, &data_len, sizeof(data_len));
+    barrier();
     *ptr += sizeof data_len;
     if (data_len) {
         memcpy(*ptr, data, data_len);
+        barrier();
         *ptr += data_len;
     }
 }
@@ -61,6 +66,7 @@ static inline void
 serialize_result(uint8_t **ptr, EFI_STATUS status)
 {
     memcpy(*ptr, &status, sizeof(status));
+    barrier();
     *ptr += sizeof status;
 }
 
@@ -68,6 +74,7 @@ static inline void
 serialize_guid(uint8_t **ptr, const EFI_GUID *guid)
 {
     memcpy(*ptr, guid, GUID_LEN);
+    barrier();
     *ptr += GUID_LEN;
 }
 
@@ -75,6 +82,7 @@ static inline void
 serialize_timestamp(uint8_t **ptr, EFI_TIME *timestamp)
 {
     memcpy(*ptr, timestamp, sizeof(*timestamp));
+    barrier();
     *ptr += sizeof(*timestamp);
 }
 
@@ -82,6 +90,7 @@ static inline void
 serialize_uintn(uint8_t **ptr, UINTN var)
 {
     memcpy(*ptr, &var, sizeof(var));
+    barrier();
     *ptr += sizeof var;
 }
 
@@ -89,6 +98,7 @@ static inline void
 serialize_uint32(uint8_t **ptr, UINT32 var)
 {
     memcpy(*ptr, &var, sizeof(var));
+    barrier();
     *ptr += sizeof var;
 }
 
@@ -96,6 +106,7 @@ static inline void
 serialize_uint64(uint8_t **ptr, UINT64 var)
 {
     memcpy(*ptr, &var, sizeof(var));
+    barrier();
     *ptr += sizeof var;
 }
 
@@ -105,6 +116,7 @@ unserialize_data(uint8_t **ptr, UINTN *len, UINTN limit)
     uint8_t *data;
 
     memcpy(len, *ptr, sizeof(*len));
+    barrier();
     *ptr += sizeof *len;
 
     if (*len > limit || *len == 0)
@@ -115,6 +127,7 @@ unserialize_data(uint8_t **ptr, UINTN *len, UINTN limit)
         return NULL;
 
     memcpy(data, *ptr, *len);
+    barrier();
     *ptr += *len;
 
     return data;
@@ -124,6 +137,7 @@ static inline void
 unserialize_data_inplace(uint8_t **ptr, uint8_t *buf, UINTN len)
 {
     memcpy(buf, *ptr, len);
+    barrier();
     *ptr += len;
 }
 
@@ -137,6 +151,7 @@ static inline void
 unserialize_timestamp(uint8_t **ptr, EFI_TIME *timestamp)
 {
     memcpy(timestamp, *ptr, sizeof(*timestamp));
+    barrier();
     *ptr += sizeof(*timestamp);
 }
 
@@ -146,6 +161,7 @@ unserialize_uintn(uint8_t **ptr)
     UINTN ret;
 
     memcpy(&ret, *ptr, sizeof(ret));
+    barrier();
     *ptr += sizeof ret;
 
     return ret;
@@ -157,6 +173,7 @@ unserialize_boolean(uint8_t **ptr)
     BOOLEAN ret;
 
     memcpy(&ret, *ptr, sizeof(ret));
+    barrier();
     *ptr += sizeof ret;
 
     return ret;
@@ -168,6 +185,7 @@ unserialize_uint32(uint8_t **ptr)
     UINT32 ret;
 
     memcpy(&ret, *ptr, sizeof(ret));
+    barrier();
     *ptr += sizeof ret;
 
     return ret;
-- 
2.39.5

