Xen Security Advisory 478 v2 (CVE-2025-58151) - varstored: TOCTOU issues with mapped guest memory

Xen.org security team posted 1 patch 1 week, 3 days ago
Failed in applying to current master (apply log)
include/serialize.h | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
Xen Security Advisory 478 v2 (CVE-2025-58151) - varstored: TOCTOU issues with mapped guest memory
Posted by Xen.org security team 1 week, 3 days ago
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

            Xen Security Advisory CVE-2025-58151 / XSA-478
                               version 2

           varstored: TOCTOU issues with mapped guest memory

UPDATES IN VERSION 2
====================

Public release.

ISSUE DESCRIPTION
=================

varstored is a component of the Xapi toolstack handling UEFI Variables
for a VM.  It has a communication path with OVMF inside the VM involving
mapping a buffer prepared by OVMF.

Within varstored, there were insufficient compiler barriers, creating
TOCTOU issues with data in the shared buffer.

The exact vulnerable behaviour depends on the code generated by the
compiler.  In a build of varstored using default settings, the attacker
can control an index used in a jump table.

IMPACT
======

An attacker with kernel level access in a VM can escalate privilege via
gaining code execution within varstored.

VULNERABLE SYSTEMS
==================

Only systems using the Xapi toolstack are potentially affected.

Systems running all versions of varstored are potentially affected.

x86 HVM guests which have been configured as UEFI VMs can leverage the
vulnerability.  x86 PV guests cannot leverage the vulnerability.

A Xapi VM is configured for UEFI if the `HVM-boot-params` map contains
`firmware=uefi`.  e.g.:

  xe vm-param-list uuid=$UUID

  ...
  HVM-boot-params (MRW): firmware: uefi
  ...

If `firmware` is set to `bios`, or is absent entirely (PV guests), then
the guest cannot leverage the vulnerability.

MITIGATION
==========

There are no mitigations.

CREDITS
=======

This issue was discovered by Teddy Astie of Vates.

RESOLUTION
==========

Applying the attached patch resolves this issue.

xsa478.patch           varstored master

$ sha256sum xsa478*
401679429e22e202fecf418c5100144ea0ee1cca3643f09960107cf3d88821db  xsa478.patch
$

DEPLOYMENT DURING EMBARGO
=========================

Deployment of the patches and/or mitigations described above (or
others which are substantially similar) is permitted during the
embargo, even on public-facing systems with untrusted guest users and
administrators.

But: Distribution of updated software is prohibited (except to other
members of the predisclosure list).

Predisclosure list members who wish to deploy significantly different
patches and/or mitigations, please contact the Xen Project Security
Team.


(Note: this during-embargo deployment notice is retained in
post-embargo publicly released Xen Project advisories, even though it
is then no longer applicable.  This is to enable the community to have
oversight of the Xen Project Security Team's decisionmaking.)

For more information about permissible uses of embargoed information,
consult the Xen Project community's agreed Security Policy:
  http://www.xenproject.org/security-policy.html
-----BEGIN PGP SIGNATURE-----

iQFABAEBCAAqFiEEI+MiLBRfRHX6gGCng/4UyVfoK9kFAml4qMEMHHBncEB4ZW4u
b3JnAAoJEIP+FMlX6CvZp94IAKAafDWRsyB3vmmHsGG2cF3I1LFKQMzhtogNUu/w
7QrhNwmyI9tdIhtlPk4JC75L1Em+kDXHh+vNkQF97QeKq2IyuEYt+q2ko6sV/RTF
Ewv0BhJJIiJCfyI/x55dz+YANOwsSOo7bZrSy1l/VgUJOdVKK5L1VtcloD57ZX2D
A4r/rfZbJwx/vJ+Zp8R+W0on7SWS6h4am6M0+7f2swiJ2MpoEUwhSgFMmigOcdUc
xbUo/IKOiQVNX2A6j+J5tQT6JlrXC/K8bIUwe2oDKRPG1qSMYAr2lKZ4GvoflUra
ckCA0k520KHw+ZfuHhQq/TzIFaLVDnr1kfChYdPSX0jXtb0=
=B9ua
-----END PGP SIGNATURE-----
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