Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package spice for openSUSE:Factory checked 
in at 2026-10-21 20:51:23
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/spice (Old)
 and      /work/SRC/openSUSE:Factory/.spice.new.2252680 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "spice"

Wed Oct 21 20:51:23 2026 rev:48 rq:1386382 version:0.16.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/spice/spice.changes      2026-09-17 
15:15:53.888103518 +0200
+++ /work/SRC/openSUSE:Factory/.spice.new.2252680/spice.changes 2026-10-21 
20:51:25.120706388 +0200
@@ -1,0 +2,22 @@
+Mon Oct 19 14:23:01 MDT 2026 - [email protected]
+
+- bsc#1285876 - VUL-0: CVE-2026-105958: spice: heap corruption due
+  to insufficient bounds validation of client-supplied offsets in
+  multiple migration data handlers
+  000-smartcard-fix-NULL-dereference-of-scc-in-on_message_from_device.patch
+  001-CVE-2026-105958-migration-dont-abort-on-invalid-migration-data.patch
+  
002-CVE-2026-105958-reds-fix-wrong-struct-size-in-agent-migration-data-restore.patch
+  
003-CVE-2026-105958-display-validate-surface_id-bounds-in-migration-restore.patch
+  
004-CVE-2026-105958-display-validate-surfaces_at_client_ptr-in-display-migration-data.patch
+  
005-CVE-2026-105958-reds-validate-agent-migration-data-fields-before-use.patch
+  
006-CVE-2026-105958-char-device-validate-write_data_ptr-bounds-in-migration-restore.patch
+  
007-CVE-2026-105958-smartcard-validate-migration-read-bounds-and-buffer-sizing.patch
+  
008-CVE-2026-105958-smartcard-remove-spice_assert-on-attacker-controlled-read_size.patch
+  
009-CVE-2026-105958-smartcard-dont-allocate-based-on-attacker-controlled-message-length.patch
+- bsc#1285875 - VUL-0: CVE-2026-105959: spice: heap buffer
+  overflow due to incorrect truncation of `size_t` allocation in
+  the GLZ encoder dictionary restore path
+  
010-CVE-2026-105959-glz-fix-integer-truncation-in-encoder-malloc-callback.patch
+  011-CVE-2026-105959-glz-validate-max_encoders-from-migration-data.patch
+
+-------------------------------------------------------------------

New:
----
  000-smartcard-fix-NULL-dereference-of-scc-in-on_message_from_device.patch
  001-CVE-2026-105958-migration-dont-abort-on-invalid-migration-data.patch
  
002-CVE-2026-105958-reds-fix-wrong-struct-size-in-agent-migration-data-restore.patch
  
003-CVE-2026-105958-display-validate-surface_id-bounds-in-migration-restore.patch
  
004-CVE-2026-105958-display-validate-surfaces_at_client_ptr-in-display-migration-data.patch
  005-CVE-2026-105958-reds-validate-agent-migration-data-fields-before-use.patch
  
006-CVE-2026-105958-char-device-validate-write_data_ptr-bounds-in-migration-restore.patch
  
007-CVE-2026-105958-smartcard-validate-migration-read-bounds-and-buffer-sizing.patch
  
008-CVE-2026-105958-smartcard-remove-spice_assert-on-attacker-controlled-read_size.patch
  
009-CVE-2026-105958-smartcard-dont-allocate-based-on-attacker-controlled-message-length.patch
  
010-CVE-2026-105959-glz-fix-integer-truncation-in-encoder-malloc-callback.patch
  011-CVE-2026-105959-glz-validate-max_encoders-from-migration-data.patch

----------(New B)----------
  New:  multiple migration data handlers
  000-smartcard-fix-NULL-dereference-of-scc-in-on_message_from_device.patch
  001-CVE-2026-105958-migration-dont-abort-on-invalid-migration-data.patch
  New:  
000-smartcard-fix-NULL-dereference-of-scc-in-on_message_from_device.patch
  001-CVE-2026-105958-migration-dont-abort-on-invalid-migration-data.patch
  
002-CVE-2026-105958-reds-fix-wrong-struct-size-in-agent-migration-data-restore.patch
  New:  001-CVE-2026-105958-migration-dont-abort-on-invalid-migration-data.patch
  
002-CVE-2026-105958-reds-fix-wrong-struct-size-in-agent-migration-data-restore.patch
  
003-CVE-2026-105958-display-validate-surface_id-bounds-in-migration-restore.patch
  New:  
002-CVE-2026-105958-reds-fix-wrong-struct-size-in-agent-migration-data-restore.patch
  
003-CVE-2026-105958-display-validate-surface_id-bounds-in-migration-restore.patch
  
004-CVE-2026-105958-display-validate-surfaces_at_client_ptr-in-display-migration-data.patch
  New:  
003-CVE-2026-105958-display-validate-surface_id-bounds-in-migration-restore.patch
  
004-CVE-2026-105958-display-validate-surfaces_at_client_ptr-in-display-migration-data.patch
  005-CVE-2026-105958-reds-validate-agent-migration-data-fields-before-use.patch
  New:  
004-CVE-2026-105958-display-validate-surfaces_at_client_ptr-in-display-migration-data.patch
  005-CVE-2026-105958-reds-validate-agent-migration-data-fields-before-use.patch
  
006-CVE-2026-105958-char-device-validate-write_data_ptr-bounds-in-migration-restore.patch
  New:  
005-CVE-2026-105958-reds-validate-agent-migration-data-fields-before-use.patch
  
006-CVE-2026-105958-char-device-validate-write_data_ptr-bounds-in-migration-restore.patch
  
007-CVE-2026-105958-smartcard-validate-migration-read-bounds-and-buffer-sizing.patch
  New:  
006-CVE-2026-105958-char-device-validate-write_data_ptr-bounds-in-migration-restore.patch
  
007-CVE-2026-105958-smartcard-validate-migration-read-bounds-and-buffer-sizing.patch
  
008-CVE-2026-105958-smartcard-remove-spice_assert-on-attacker-controlled-read_size.patch
  New:  
007-CVE-2026-105958-smartcard-validate-migration-read-bounds-and-buffer-sizing.patch
  
008-CVE-2026-105958-smartcard-remove-spice_assert-on-attacker-controlled-read_size.patch
  
009-CVE-2026-105958-smartcard-dont-allocate-based-on-attacker-controlled-message-length.patch
  New:  
008-CVE-2026-105958-smartcard-remove-spice_assert-on-attacker-controlled-read_size.patch
  
009-CVE-2026-105958-smartcard-dont-allocate-based-on-attacker-controlled-message-length.patch
- bsc#1285875 - VUL-0: CVE-2026-105959: spice: heap buffer
  New:  the GLZ encoder dictionary restore path
  
010-CVE-2026-105959-glz-fix-integer-truncation-in-encoder-malloc-callback.patch
  011-CVE-2026-105959-glz-validate-max_encoders-from-migration-data.patch
  New:  
010-CVE-2026-105959-glz-fix-integer-truncation-in-encoder-malloc-callback.patch
  011-CVE-2026-105959-glz-validate-max_encoders-from-migration-data.patch
----------(New E)----------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ spice.spec ++++++
--- /var/tmp/diff_new_pack.meIEcy/_old  2026-10-21 20:51:26.347757549 +0200
+++ /var/tmp/diff_new_pack.meIEcy/_new  2026-10-21 20:51:26.349757632 +0200
@@ -35,6 +35,19 @@
 # PATCH-FIX-UPSTREAM fix-build-with-gstreamer-1.24.patch [email protected]
 Patch1:         fix-build-with-gstreamer-1.24.patch
 Patch2:         Fix-keyboard-and-mouse-state-leaks-on-interface-removal.patch
+# Upstream Security Fixes
+Patch10:        
000-smartcard-fix-NULL-dereference-of-scc-in-on_message_from_device.patch
+Patch11:        
001-CVE-2026-105958-migration-dont-abort-on-invalid-migration-data.patch
+Patch12:        
002-CVE-2026-105958-reds-fix-wrong-struct-size-in-agent-migration-data-restore.patch
+Patch13:        
003-CVE-2026-105958-display-validate-surface_id-bounds-in-migration-restore.patch
+Patch14:        
004-CVE-2026-105958-display-validate-surfaces_at_client_ptr-in-display-migration-data.patch
+Patch15:        
005-CVE-2026-105958-reds-validate-agent-migration-data-fields-before-use.patch
+Patch16:        
006-CVE-2026-105958-char-device-validate-write_data_ptr-bounds-in-migration-restore.patch
+Patch17:        
007-CVE-2026-105958-smartcard-validate-migration-read-bounds-and-buffer-sizing.patch
+Patch18:        
008-CVE-2026-105958-smartcard-remove-spice_assert-on-attacker-controlled-read_size.patch
+Patch19:        
009-CVE-2026-105958-smartcard-dont-allocate-based-on-attacker-controlled-message-length.patch
+Patch20:        
010-CVE-2026-105959-glz-fix-integer-truncation-in-encoder-malloc-callback.patch
+Patch21:        
011-CVE-2026-105959-glz-validate-max_encoders-from-migration-data.patch
 
 BuildRequires:  gcc-c++
 BuildRequires:  libjpeg-devel

++++++ 
000-smartcard-fix-NULL-dereference-of-scc-in-on_message_from_device.patch ++++++
Subject: smartcard: fix NULL dereference of scc in on_message_from_device
From: Alexandra Diupina [email protected] Thu Oct 8 18:33:05 2026 +0300
Date: Fri Oct 9 20:56:40 2026 +0100:
Git: 5f72434940d82157436b975b9ffbae17fd831870

dev->priv->scc is dereferenced in the reader_id warning path,
but the NULL check for dev->priv->scc happens afterwards.
Move the NULL check before first dereference.

Signed-off-by: Alexandra Diupina <[email protected]>

diff --git a/server/smartcard.cpp b/server/smartcard.cpp
index 023cf523..81b65455 100644
--- a/server/smartcard.cpp
+++ b/server/smartcard.cpp
@@ -159,7 +159,7 @@ 
smartcard_char_device_on_message_from_device(RedCharDeviceSmartcard *dev,
     vheader->length = ntohl(vheader->length);
     vheader->reader_id = ntohl(vheader->reader_id);
 
-    if (vheader->type == VSC_Init) {
+    if (vheader->type == VSC_Init || !dev->priv->scc) {
         return red::shared_ptr<RedMsgItem>();
     }
     /* We pass any VSC_Error right now - might need to ignore some? */
@@ -168,9 +168,6 @@ 
smartcard_char_device_on_message_from_device(RedCharDeviceSmartcard *dev,
                             "error: reader_id not assigned for message of type 
%d",
                             vheader->type);
     }
-    if (dev->priv->scc == NULL) {
-        return red::shared_ptr<RedMsgItem>();
-    }
     return smartcard_new_vsc_msg_item(dev->priv->reader_id, vheader);
 }
 

++++++ 001-CVE-2026-105958-migration-dont-abort-on-invalid-migration-data.patch 
++++++
Subject: migration: don't abort on invalid migration data
From: Marc-André Lureau [email protected] Fri Oct 2 15:08:29 2026 
+0400
Date: Sat Oct 10 14:53:22 2026 +0100:
Git: 92287780fb17ea14f28c740fcdba0ded70822fa7

spice_error() and spice_assert() call abort(), so a malicious or
corrupted migration message can crash the server. Replace them with
spice_warning() + return false across all migration data handlers,
allowing the connection to be dropped gracefully instead.

Also replace spice_return_val_if_fail() and spice_critical() with
explicit checks and warnings in the display channel migration handler
for the same reason.

This is part of CVE-2026-105958 fixes.

Related-to: https://gitlab.freedesktop.org/spice/spice/-/work_items/100
Reported-by: Tristan Madani (Talence Security)
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Marc-André Lureau <[email protected]>
Acked-by: Frediano Ziglio <[email protected]>

diff --git a/server/char-device.cpp b/server/char-device.cpp
index 1be96c06..c02ca6a3 100644
--- a/server/char-device.cpp
+++ b/server/char-device.cpp
@@ -828,12 +828,15 @@ bool RedCharDevice::restore(SpiceMigrateDataCharDevice 
*mig_data)
 
     dev_client = static_cast<RedCharDeviceClient 
*>(g_list_last(priv->clients)->data);
     if (mig_data->version > SPICE_MIGRATE_DATA_CHAR_DEVICE_VERSION) {
-        spice_error("dev %p error: migration data version %u is bigger than 
self %u",
-                    this, mig_data->version, 
SPICE_MIGRATE_DATA_CHAR_DEVICE_VERSION);
-        return FALSE;
+        spice_warning("dev %p error: migration data version %u is bigger than 
self %u",
+                      this, mig_data->version, 
SPICE_MIGRATE_DATA_CHAR_DEVICE_VERSION);
+        return false;
     }
     spice_assert(!priv->cur_write_buf && g_queue_is_empty(&priv->write_queue));
-    spice_assert(mig_data->connected);
+    if (!mig_data->connected) {
+        spice_warning("dev %p error: migration data has no connected client", 
this);
+        return false;
+    }
 
     client_tokens_window = dev_client->num_client_tokens; /* initial state of 
tokens */
     dev_client->num_client_tokens = mig_data->num_client_tokens;
@@ -865,7 +868,7 @@ bool RedCharDevice::restore(SpiceMigrateDataCharDevice 
*mig_data)
     priv->wait_for_migrate_data = FALSE;
     write_to_device();
     red_char_device_read_from_device(this);
-    return TRUE;
+    return true;
 }
 
 SpiceServer* RedCharDevice::get_server()
diff --git a/server/dcc.cpp b/server/dcc.cpp
index 6e86a186..200f63f4 100644
--- a/server/dcc.cpp
+++ b/server/dcc.cpp
@@ -1091,11 +1091,15 @@ bool DisplayChannelClient::handle_migrate_data(uint32_t 
size, void *message)
     uint8_t *surfaces;
     int i;
 
-    spice_return_val_if_fail(
-        size >= (sizeof(*migrate_data) + sizeof(SpiceMigrateDataHeader)), 
FALSE);
-    spice_return_val_if_fail(
-         migration_protocol_validate_header(header,
-             SPICE_MIGRATE_DATA_DISPLAY_MAGIC, 
SPICE_MIGRATE_DATA_DISPLAY_VERSION), FALSE);
+    if (size < sizeof(*migrate_data) + sizeof(SpiceMigrateDataHeader)) {
+        spice_warning("bad message size %u", size);
+        return false;
+    }
+    if (!migration_protocol_validate_header(header,
+            SPICE_MIGRATE_DATA_DISPLAY_MAGIC, 
SPICE_MIGRATE_DATA_DISPLAY_VERSION)) {
+        spice_warning("bad header");
+        return false;
+    }
 
     /* size is set to -1 in order to keep the cache frozen until the original
      * channel client that froze the cache on the src size receives the migrate
@@ -1103,7 +1107,10 @@ bool DisplayChannelClient::handle_migrate_data(uint32_t 
size, void *message)
      * pixmap_cache_reset */
     dcc->priv->pixmap_cache = pixmap_cache_get(dcc->get_client(),
                                                migrate_data->pixmap_cache_id, 
-1);
-    spice_return_val_if_fail(dcc->priv->pixmap_cache, FALSE);
+    if (!dcc->priv->pixmap_cache) {
+        spice_warning("pixmap cache get failed");
+        return false;
+    }
 
     pthread_mutex_lock(&dcc->priv->pixmap_cache->lock);
     for (i = 0; i < MAX_CACHE_CLIENTS; i++) {
@@ -1122,7 +1129,8 @@ bool DisplayChannelClient::handle_migrate_data(uint32_t 
size, void *message)
     if (dcc_handle_migrate_glz_dictionary(dcc, migrate_data)) {
         image_encoders_glz_create(&dcc->priv->encoders, dcc->priv->id);
     } else {
-        spice_critical("restoring global lz dictionary failed");
+        spice_warning("restoring global lz dictionary failed");
+        return false;
     }
 
     dcc->is_low_bandwidth = migrate_data->low_bandwidth_setting;
@@ -1144,12 +1152,15 @@ bool DisplayChannelClient::handle_migrate_data(uint32_t 
size, void *message)
         restore_surfaces_lossless(dcc,
                   reinterpret_cast<MigrateDisplaySurfacesAtClientLossless 
*>(surfaces));
 
-    spice_return_val_if_fail(surfaces_restored, FALSE);
+    if (!surfaces_restored) {
+        spice_warning("restoring surfaces failed");
+        return false;
+    }
 
     dcc->pipe_add_type(RED_PIPE_ITEM_TYPE_INVAL_PALETTE_CACHE);
     /* enable sending messages */
     dcc->ack_zero_messages_window();
-    return TRUE;
+    return true;
 }
 
 VideoStreamAgent* dcc_get_video_stream_agent(DisplayChannelClient *dcc, int 
stream_id)
diff --git a/server/display-channel.cpp b/server/display-channel.cpp
index 53a4724d..4f7e3c68 100644
--- a/server/display-channel.cpp
+++ b/server/display-channel.cpp
@@ -2139,10 +2139,14 @@ void DisplayChannelClient::handle_migrate_flush_mark()
 bool DisplayChannelClient::handle_migrate_data_get_serial(uint32_t size, void 
*message,
                                                           uint64_t &serial)
 {
-    SpiceMigrateDataDisplay *migrate_data;
+    if (size < sizeof(SpiceMigrateDataHeader) + 
sizeof(SpiceMigrateDataDisplay)) {
+        spice_warning("bad message size %u", size);
+        return false;
+    }
 
-    migrate_data = reinterpret_cast<SpiceMigrateDataDisplay 
*>(static_cast<uint8_t *>(message) +
-                                                               
sizeof(SpiceMigrateDataHeader));
+    auto migrate_data =
+        reinterpret_cast<SpiceMigrateDataDisplay *>(static_cast<uint8_t 
*>(message) +
+                                                    
sizeof(SpiceMigrateDataHeader));
 
     serial = migrate_data->message_serial;
     return true;
diff --git a/server/inputs-channel.cpp b/server/inputs-channel.cpp
index 37de1051..c756b8f0 100644
--- a/server/inputs-channel.cpp
+++ b/server/inputs-channel.cpp
@@ -504,7 +504,7 @@ bool InputsChannelClient::handle_migrate_data(uint32_t 
size, void *message)
 
     if (size < sizeof(SpiceMigrateDataHeader) + 
sizeof(SpiceMigrateDataInputs)) {
         spice_warning("bad message size %u", size);
-        return FALSE;
+        return false;
     }
 
     header = static_cast<SpiceMigrateDataHeader *>(message);
@@ -513,12 +513,12 @@ bool InputsChannelClient::handle_migrate_data(uint32_t 
size, void *message)
     if (!migration_protocol_validate_header(header,
                                             SPICE_MIGRATE_DATA_INPUTS_MAGIC,
                                             
SPICE_MIGRATE_DATA_INPUTS_VERSION)) {
-        spice_error("bad header");
-        return FALSE;
+        spice_warning("bad header");
+        return false;
     }
     InputsChannel::key_modifiers_sender(inputs);
     handle_migrate_data(mig_data->motion_count);
-    return TRUE;
+    return true;
 }
 
 red::shared_ptr<InputsChannel> inputs_channel_new(RedsState *reds)
diff --git a/server/main-channel.cpp b/server/main-channel.cpp
index e14a45d5..fd6f512e 100644
--- a/server/main-channel.cpp
+++ b/server/main-channel.cpp
@@ -89,13 +89,13 @@ bool MainChannelClient::handle_migrate_data(uint32_t size, 
void *message)
 
     if (size < sizeof(SpiceMigrateDataHeader) + sizeof(SpiceMigrateDataMain)) {
         red_channel_warning(channel, "bad message size %u", size);
-        return FALSE;
+        return false;
     }
     if (!migration_protocol_validate_header(header,
                                             SPICE_MIGRATE_DATA_MAIN_MAGIC,
                                             SPICE_MIGRATE_DATA_MAIN_VERSION)) {
-        spice_error("bad header");
-        return FALSE;
+        spice_warning("bad header");
+        return false;
     }
     return reds_handle_migrate_data(channel->get_server(), mcc,
                                     reinterpret_cast<SpiceMigrateDataMain 
*>(header + 1), size);
diff --git a/server/migration-protocol.h b/server/migration-protocol.h
index 2fc8e036..13b8ec4a 100644
--- a/server/migration-protocol.h
+++ b/server/migration-protocol.h
@@ -201,19 +201,19 @@ typedef struct SPICE_ATTR_PACKED SpiceMigrateDataInputs {
 
 #include <spice/end-packed.h>
 
-static inline int migration_protocol_validate_header(SpiceMigrateDataHeader 
*header,
-                                                     uint32_t magic,
-                                                     uint32_t version)
+static inline bool migration_protocol_validate_header(SpiceMigrateDataHeader 
*header,
+                                                      uint32_t magic,
+                                                      uint32_t version)
 {
     if (header->magic != magic) {
-        spice_error("bad magic %u (!= %u)", header->magic, magic);
-        return FALSE;
+        spice_warning("bad magic %u (!= %u)", header->magic, magic);
+        return false;
     }
     if (header->version > version) {
-        spice_error("unsupported version %u (> %u)", header->version, version);
-        return FALSE;
+        spice_warning("unsupported version %u (> %u)", header->version, 
version);
+        return false;
     }
-    return TRUE;
+    return true;
 }
 
 #endif /* MIGRATION_PROTOCOL_H_ */
diff --git a/server/smartcard-channel-client.cpp 
b/server/smartcard-channel-client.cpp
index 65c402f3..9d204089 100644
--- a/server/smartcard-channel-client.cpp
+++ b/server/smartcard-channel-client.cpp
@@ -256,18 +256,18 @@ bool SmartCardChannelClient::handle_migrate_data(uint32_t 
size, void *message)
     header = (SpiceMigrateDataHeader *)message;
     mig_data = (SpiceMigrateDataSmartcard *)(header + 1);
     if (size < sizeof(SpiceMigrateDataHeader) + 
sizeof(SpiceMigrateDataSmartcard)) {
-        spice_error("bad message size");
-        return FALSE;
+        spice_warning("bad message size %u", size);
+        return false;
     }
     if (!migration_protocol_validate_header(header,
                                             SPICE_MIGRATE_DATA_SMARTCARD_MAGIC,
                                             
SPICE_MIGRATE_DATA_SMARTCARD_VERSION)) {
-        spice_error("bad header");
-        return FALSE;
+        spice_warning("bad header");
+        return false;
     }
 
     if (!mig_data->base.connected) { /* client wasn't attached to a smartcard 
*/
-        return TRUE;
+        return true;
     }
 
     auto smartcard = scc->priv->smartcard.lock();
@@ -276,7 +276,7 @@ bool SmartCardChannelClient::handle_migrate_data(uint32_t 
size, void *message)
 
         if (!char_device) {
             spice_warning("no unattached device available");
-            return TRUE;
+            return true;
         } else {
             smartcard_char_device_attach_client(char_device, scc);
         }
@@ -287,7 +287,7 @@ bool SmartCardChannelClient::handle_migrate_data(uint32_t 
size, void *message)
     if (smartcard) {
         return smartcard_char_device_handle_migrate_data(smartcard.get(), 
mig_data);
     }
-    return TRUE;
+    return true;
 }
 
 void SmartCardChannelClient::handle_migrate_flush_mark()
diff --git a/server/spicevmc.cpp b/server/spicevmc.cpp
index 3dbc3eaf..c34f7684 100644
--- a/server/spicevmc.cpp
+++ b/server/spicevmc.cpp
@@ -336,13 +336,16 @@ bool VmcChannelClient::handle_migrate_data(uint32_t size, 
void *message)
 
     header = static_cast<SpiceMigrateDataHeader *>(message);
     mig_data = reinterpret_cast<SpiceMigrateDataSpiceVmc *>(header + 1);
-    spice_assert(size >= sizeof(SpiceMigrateDataHeader) + 
sizeof(SpiceMigrateDataSpiceVmc));
+    if (size < sizeof(SpiceMigrateDataHeader) + 
sizeof(SpiceMigrateDataSpiceVmc)) {
+        spice_warning("bad message size %u", size);
+        return false;
+    }
 
     if (!migration_protocol_validate_header(header,
                                             SPICE_MIGRATE_DATA_SPICEVMC_MAGIC,
                                             
SPICE_MIGRATE_DATA_SPICEVMC_VERSION)) {
-        spice_error("bad header");
-        return FALSE;
+        spice_warning("bad header");
+        return false;
     }
     return channel->chardev->restore(&mig_data->base);
 }

++++++ 
002-CVE-2026-105958-reds-fix-wrong-struct-size-in-agent-migration-data-restore.patch
 ++++++
Subject: reds: fix wrong struct size in agent migration data restore
From: Marc-André Lureau [email protected] Fri Oct 2 15:10:28 2026 
+0400
Date: Sat Oct 10 14:53:27 2026 +0100:
Git: 7655a7e32b2e5bb3b96d8a8b3fbce398b6a0392b

The pointer offset for msg_header_ptr was adjusted by
sizeof(SpiceMiniDataHeader) (6 bytes) instead of the correct
sizeof(SpiceMigrateDataHeader) (8 bytes). This caused the
partial_msg_header pointer to land 2 bytes too far into the
buffer, reading the wrong data during agent state restore.

This is part of CVE-2026-105958 fixes.

Related-to: https://gitlab.freedesktop.org/spice/spice/-/work_items/100
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Marc-André Lureau <[email protected]>
Acked-by: Frediano Ziglio <[email protected]>

diff --git a/server/reds.cpp b/server/reds.cpp
index 4beb6e6c..cb8e03d1 100644
--- a/server/reds.cpp
+++ b/server/reds.cpp
@@ -1379,7 +1379,7 @@ static int reds_agent_state_restore(RedsState *reds, 
SpiceMigrateDataMain *mig_d
             spice_assert(agent_dev->priv->current_read_buf);
             partial_msg_header = reinterpret_cast<uint8_t *>(mig_data) +
                                  mig_data->agent2client.msg_header_ptr -
-                                 sizeof(SpiceMiniDataHeader);
+                                 sizeof(SpiceMigrateDataHeader);
             memcpy(agent_dev->priv->current_read_buf->data,
                    partial_msg_header,
                    mig_data->agent2client.msg_header_partial_len);

++++++ 
003-CVE-2026-105958-display-validate-surface_id-bounds-in-migration-restore.patch
 ++++++
Subject: display: validate surface_id bounds in migration restore
From: Marc-André Lureau [email protected] Fri Oct 2 15:11:44 2026 
+0400
Date: Sat Oct 10 14:53:31 2026 +0100:
Git: 99876f49982674fd0c7c67099fcc04364d93638b

A surface_id from migration data was used as an array index without
bounds checking. A value >= NUM_SURFACES would cause an out-of-bounds
array access in surface_client_created[].

This is part of CVE-2026-105958 fixes.

Related-to: https://gitlab.freedesktop.org/spice/spice/-/work_items/100
Reported-by: Tristan Madani (Talence Security)
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Marc-André Lureau <[email protected]>
Acked-by: Frediano Ziglio <[email protected]>

diff --git a/server/dcc.cpp b/server/dcc.cpp
index 200f63f4..fd602128 100644
--- a/server/dcc.cpp
+++ b/server/dcc.cpp
@@ -1031,14 +1031,18 @@ static int 
dcc_handle_migrate_glz_dictionary(DisplayChannelClient *dcc,
 
 static bool restore_surface(DisplayChannelClient *dcc, uint32_t surface_id)
 {
+    if (surface_id >= NUM_SURFACES) {
+        spice_warning("surface id %u out of range", surface_id);
+        return false;
+    }
     /* we don't process commands till we receive the migration data, thus,
      * we should have not sent any surface to the client. */
     if (dcc->priv->surface_client_created[surface_id]) {
         spice_warning("surface %u is already marked as client_created", 
surface_id);
-        return FALSE;
+        return false;
     }
     dcc->priv->surface_client_created[surface_id] = TRUE;
-    return TRUE;
+    return true;
 }
 
 static bool restore_surfaces_lossless(DisplayChannelClient *dcc,

++++++ 
004-CVE-2026-105958-display-validate-surfaces_at_client_ptr-in-display-migration-data.patch
 ++++++
Subject: display: validate surfaces_at_client_ptr in display migration data
From: Marc-André Lureau [email protected] Fri Oct 2 15:17:28 2026 
+0400
Date: Sat Oct 10 14:53:35 2026 +0100:
Git: b36238b050dc3cc6574746c17ff3a4d192516c81

The surfaces_at_client_ptr offset and num_surfaces count from migration
data were used without validation. A crafted message could cause an
out-of-bounds read when restoring surfaces.

Validate that the pointer falls within the message and that the surface
array fits within the remaining space.

This is part of CVE-2026-105958 fixes.

Related-to: https://gitlab.freedesktop.org/spice/spice/-/work_items/100
Reported-by: Tristan Madani (Talence Security)
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Marc-André Lureau <[email protected]>
Acked-by: Frediano Ziglio <[email protected]>

diff --git a/server/dcc.cpp b/server/dcc.cpp
index fd602128..d3bdb64e 100644
--- a/server/dcc.cpp
+++ b/server/dcc.cpp
@@ -1149,7 +1149,23 @@ bool DisplayChannelClient::handle_migrate_data(uint32_t 
size, void *message)
         }
     }
 
+    if (migrate_data->surfaces_at_client_ptr > size ||
+        size - migrate_data->surfaces_at_client_ptr < sizeof(uint32_t)) {
+        spice_warning("bad surfaces_at_client_ptr in display migration data");
+        return false;
+    }
     surfaces = static_cast<uint8_t *>(message) + 
migrate_data->surfaces_at_client_ptr;
+    {
+        uint32_t num_surfaces =
+            reinterpret_cast<MigrateDisplaySurfacesAtClientLossless 
*>(surfaces)->num_surfaces;
+        uint32_t available = size - migrate_data->surfaces_at_client_ptr - 
sizeof(uint32_t);
+        size_t element_size = display->priv->enable_jpeg ?
+            sizeof(MigrateDisplaySurfaceLossy) : 
sizeof(MigrateDisplaySurfaceLossless);
+        if (num_surfaces > available / element_size) {
+            spice_warning("bad num_surfaces %u in display migration data", 
num_surfaces);
+            return false;
+        }
+    }
     surfaces_restored = display->priv->enable_jpeg ?
         restore_surfaces_lossy(dcc,
                   reinterpret_cast<MigrateDisplaySurfacesAtClientLossy 
*>(surfaces)) :

++++++ 
005-CVE-2026-105958-reds-validate-agent-migration-data-fields-before-use.patch 
++++++
Subject: reds: validate agent migration data fields before use
From: Marc-André Lureau [email protected] Fri Oct 2 15:23:58 2026 
+0400
Date: Sat Oct 10 14:53:38 2026 +0100:
Git: 4971adb9addba9e00bf485fb09735d09e54584cf

Replace spice_assert() calls on attacker-controlled fields with proper
validation and warning messages:

- chunk_header_size: could underflow chunk_header_remaining (uint32_t)
- msg_header_ptr and msg_header_partial_len: could cause out-of-bounds
  reads when restoring partial message headers
- msg_header_partial_len in the chunk-header-remaining path: unexpected
  non-zero value indicates corrupted migration data

Also propagate the message size through to reds_agent_state_restore()
and store it alongside deferred migration data for later validation.

This is part of CVE-2026-105958 fixes.

Related-to: https://gitlab.freedesktop.org/spice/spice/-/work_items/100
Reported-by: Tristan Madani (Talence Security)
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Marc-André Lureau <[email protected]>
Acked-by: Frediano Ziglio <[email protected]>

diff --git a/server/reds.cpp b/server/reds.cpp
index cb8e03d1..8f896fff 100644
--- a/server/reds.cpp
+++ b/server/reds.cpp
@@ -194,6 +194,7 @@ struct RedCharDeviceVDIPortPrivate {
 
     SpiceMigrateDataMain *mig_data; /* storing it when migration data arrives
                                        before agent is attached */
+    uint32_t mig_data_size;
 };
 
 /* messages that are addressed to the agent and are created in the server */
@@ -1350,13 +1351,18 @@ void reds_marshall_migrate_data(RedsState *reds, 
SpiceMarshaller *m)
                 agent_dev->priv->write_filter.result);
 }
 
-static int reds_agent_state_restore(RedsState *reds, SpiceMigrateDataMain 
*mig_data)
+static bool reds_agent_state_restore(RedsState *reds, SpiceMigrateDataMain 
*mig_data,
+                                     uint32_t msg_size)
 {
     RedCharDeviceVDIPort *agent_dev = reds->agent_dev.get();
     uint32_t chunk_header_remaining;
 
     agent_dev->priv->vdi_chunk_header = mig_data->agent2client.chunk_header;
-    spice_assert(mig_data->agent2client.chunk_header_size <= 
sizeof(VDIChunkHeader));
+    if (mig_data->agent2client.chunk_header_size > sizeof(VDIChunkHeader)) {
+        spice_warning("bad chunk_header_size %u in migration data",
+                      mig_data->agent2client.chunk_header_size);
+        return false;
+    }
     chunk_header_remaining = sizeof(VDIChunkHeader) - 
mig_data->agent2client.chunk_header_size;
     if (chunk_header_remaining) {
         agent_dev->priv->read_state = VDI_PORT_READ_STATE_READ_HEADER;
@@ -1374,6 +1380,16 @@ static int reds_agent_state_restore(RedsState *reds, 
SpiceMigrateDataMain *mig_d
         if (!chunk_header_remaining) {
             uint32_t cur_buf_size;
 
+            if (mig_data->agent2client.msg_header_ptr < 
sizeof(SpiceMigrateDataHeader) ||
+                mig_data->agent2client.msg_header_ptr > msg_size ||
+                mig_data->agent2client.msg_header_partial_len >
+                    msg_size - mig_data->agent2client.msg_header_ptr ||
+                mig_data->agent2client.msg_header_partial_len >=
+                    sizeof(agent_dev->priv->current_read_buf->data)) {
+                spice_warning("bad msg_header_ptr or msg_header_partial_len in 
migration data");
+                return false;
+            }
+
             agent_dev->priv->read_state = VDI_PORT_READ_STATE_READ_DATA;
             agent_dev->priv->current_read_buf = 
vdi_port_get_read_buf(agent_dev);
             spice_assert(agent_dev->priv->current_read_buf);
@@ -1392,7 +1408,10 @@ static int reds_agent_state_restore(RedsState *reds, 
SpiceMigrateDataMain *mig_d
                                                  
mig_data->agent2client.msg_header_partial_len;
             agent_dev->priv->message_receive_len -= 
agent_dev->priv->receive_len;
         } else {
-            spice_assert(mig_data->agent2client.msg_header_partial_len == 0);
+            if (mig_data->agent2client.msg_header_partial_len != 0) {
+                spice_warning("msg_header_partial_len should be 0 when chunk 
header is incomplete");
+                return false;
+            }
         }
     } else {
             agent_dev->priv->read_state = VDI_PORT_READ_STATE_GET_BUFF;
@@ -1455,7 +1474,7 @@ bool reds_handle_migrate_data(RedsState *reds, 
MainChannelClient *mcc,
                     reds->main_channel->push_agent_connected();
                 } else {
                     spice_debug("restoring state from mig_data");
-                    return reds_agent_state_restore(reds, mig_data);
+                    return reds_agent_state_restore(reds, mig_data, size);
                 }
             }
         } else {
@@ -1464,6 +1483,7 @@ bool reds_handle_migrate_data(RedsState *reds, 
MainChannelClient *mcc,
             spice_assert(agent_dev->priv->plug_generation == 0);
             agent_dev->priv->mig_data =
                 static_cast<SpiceMigrateDataMain *>(g_memdup2(mig_data, size));
+            agent_dev->priv->mig_data_size = size;
         }
     } else {
         spice_debug("agent was not attached on the source host");
@@ -1475,7 +1495,7 @@ bool reds_handle_migrate_data(RedsState *reds, 
MainChannelClient *mcc,
         }
     }
 
-    return TRUE;
+    return true;
 }
 
 static void reds_channel_init_auth_caps(RedLinkInfo *link, RedChannel *channel)
@@ -3100,9 +3120,13 @@ attach_to_red_agent(RedsState *reds, 
SpiceCharDeviceInstance *sin)
         if (dev->priv->mig_data) {
             spice_debug("restoring dev from stored migration data");
             spice_assert(dev->priv->plug_generation == 1);
-            reds_agent_state_restore(reds, dev->priv->mig_data);
+            bool restored = reds_agent_state_restore(reds, dev->priv->mig_data,
+                                                     dev->priv->mig_data_size);
             g_free(dev->priv->mig_data);
             dev->priv->mig_data = nullptr;
+            if (!restored) {
+                reds_disconnect(reds);
+            }
         }
         else {
             spice_debug("waiting for migration data");

++++++ 
006-CVE-2026-105958-char-device-validate-write_data_ptr-bounds-in-migration-restore.patch
 ++++++
Subject: char-device: validate write_data_ptr bounds in migration restore
From: Marc-André Lureau [email protected] Fri Oct 2 15:25:02 2026 
+0400
Date: Sat Oct 10 14:53:40 2026 +0100:
Git: a45256b853cc1304dcacbea900542a8061d60540

The write_data_ptr offset and write_size from migration data were used
in a memcpy without verifying they fall within the message buffer. A
crafted migration message could cause an out-of-bounds read.

Add bounds validation in RedCharDevice::restore() and propagate the
message size through all callers (spicevmc, smartcard, main/agent
channels).

This is part of CVE-2026-105958 fixes.

Related-to: https://gitlab.freedesktop.org/spice/spice/-/work_items/100
Reported-by: Tristan Madani (Talence Security)
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Marc-André Lureau <[email protected]>
Acked-by: Frediano Ziglio <[email protected]>

diff --git a/server/char-device.cpp b/server/char-device.cpp
index c02ca6a3..56194f71 100644
--- a/server/char-device.cpp
+++ b/server/char-device.cpp
@@ -818,7 +818,7 @@ void RedCharDevice::migrate_data_marshall(SpiceMarshaller 
*m)
     spice_marshaller_set_uint32(m, write_to_dev_sizes_ptr + sizeof(uint32_t), 
write_to_dev_tokens);
 }
 
-bool RedCharDevice::restore(SpiceMigrateDataCharDevice *mig_data)
+bool RedCharDevice::restore(SpiceMigrateDataCharDevice *mig_data, uint32_t 
msg_size)
 {
     RedCharDeviceClient *dev_client;
     uint32_t client_tokens_window;
@@ -847,6 +847,11 @@ bool RedCharDevice::restore(SpiceMigrateDataCharDevice 
*mig_data)
     dev_client->num_send_tokens = mig_data->num_send_tokens;
 
     if (mig_data->write_size > 0) {
+        if (mig_data->write_data_ptr > msg_size ||
+            mig_data->write_size > msg_size - mig_data->write_data_ptr) {
+            spice_warning("bad write_data_ptr or write_size in migration 
data");
+            return false;
+        }
         if (mig_data->write_num_client_tokens) {
             priv->cur_write_buf =
                 red_char_device_write_buffer_get(this, dev_client->client,
diff --git a/server/char-device.h b/server/char-device.h
index dce9ab8a..5617ae50 100644
--- a/server/char-device.h
+++ b/server/char-device.h
@@ -116,7 +116,7 @@ public:
     void migrate_data_marshall(SpiceMarshaller *m);
     static void migrate_data_marshall_empty(SpiceMarshaller *m);
 
-    bool restore(SpiceMigrateDataCharDevice *mig_data);
+    bool restore(SpiceMigrateDataCharDevice *mig_data, uint32_t msg_size);
 
     /*
      * Resets write/read queues, and moves that state to being stopped.
diff --git a/server/reds.cpp b/server/reds.cpp
index 8f896fff..3f119c7a 100644
--- a/server/reds.cpp
+++ b/server/reds.cpp
@@ -1438,7 +1438,7 @@ static bool reds_agent_state_restore(RedsState *reds, 
SpiceMigrateDataMain *mig_
                 agent_dev->priv->read_filter.discard_all,
                 agent_dev->priv->read_filter.msg_data_to_read,
                 agent_dev->priv->read_filter.result);
-    return agent_dev->restore(&mig_data->agent_base);
+    return agent_dev->restore(&mig_data->agent_base, msg_size);
 }
 
 /*
diff --git a/server/smartcard-channel-client.cpp 
b/server/smartcard-channel-client.cpp
index 9d204089..d00cde14 100644
--- a/server/smartcard-channel-client.cpp
+++ b/server/smartcard-channel-client.cpp
@@ -285,7 +285,7 @@ bool SmartCardChannelClient::handle_migrate_data(uint32_t 
size, void *message)
     spice_debug("reader added %d partial read_size %u", 
mig_data->reader_added, mig_data->read_size);
 
     if (smartcard) {
-        return smartcard_char_device_handle_migrate_data(smartcard.get(), 
mig_data);
+        return smartcard_char_device_handle_migrate_data(smartcard.get(), 
mig_data, size);
     }
     return true;
 }
diff --git a/server/smartcard.cpp b/server/smartcard.cpp
index 81b65455..b04bcaa9 100644
--- a/server/smartcard.cpp
+++ b/server/smartcard.cpp
@@ -438,13 +438,14 @@ static void 
smartcard_device_restore_partial_read(RedCharDeviceSmartcard *dev,
     dev->priv->buf_pos = dev->priv->buf + mig_data->read_size;
 }
 
-int smartcard_char_device_handle_migrate_data(RedCharDeviceSmartcard 
*smartcard,
-                                              SpiceMigrateDataSmartcard 
*mig_data)
+bool smartcard_char_device_handle_migrate_data(RedCharDeviceSmartcard 
*smartcard,
+                                               SpiceMigrateDataSmartcard 
*mig_data,
+                                               uint32_t msg_size)
 {
     smartcard->priv->reader_added = mig_data->reader_added;
 
     smartcard_device_restore_partial_read(smartcard, mig_data);
-    return smartcard->restore(&mig_data->base);
+    return smartcard->restore(&mig_data->base, msg_size);
 }
 
 void RedSmartcardChannel::on_connect(RedClient *client, RedStream *stream, int 
migration,
diff --git a/server/smartcard.h b/server/smartcard.h
index 7ac7cc5a..266e79d0 100644
--- a/server/smartcard.h
+++ b/server/smartcard.h
@@ -56,8 +56,9 @@ gboolean 
smartcard_char_device_notify_reader_remove(RedCharDeviceSmartcard *smar
 void smartcard_char_device_detach_client(RedCharDeviceSmartcard *smartcard,
                                          SmartCardChannelClient *scc);
 SmartCardChannelClient* 
smartcard_char_device_get_client(RedCharDeviceSmartcard *smartcard);
-int smartcard_char_device_handle_migrate_data(RedCharDeviceSmartcard 
*smartcard,
-                                              SpiceMigrateDataSmartcard 
*mig_data);
+bool smartcard_char_device_handle_migrate_data(RedCharDeviceSmartcard 
*smartcard,
+                                               SpiceMigrateDataSmartcard 
*mig_data,
+                                               uint32_t msg_size);
 
 enum {
     RED_PIPE_ITEM_TYPE_ERROR = RED_PIPE_ITEM_TYPE_CHANNEL_BASE,
diff --git a/server/spicevmc.cpp b/server/spicevmc.cpp
index c34f7684..02eb112a 100644
--- a/server/spicevmc.cpp
+++ b/server/spicevmc.cpp
@@ -347,7 +347,7 @@ bool VmcChannelClient::handle_migrate_data(uint32_t size, 
void *message)
         spice_warning("bad header");
         return false;
     }
-    return channel->chardev->restore(&mig_data->base);
+    return channel->chardev->restore(&mig_data->base, size);
 }
 
 static bool handle_compressed_msg(RedVmcChannel *channel, RedChannelClient 
*rcc,

++++++ 
007-CVE-2026-105958-smartcard-validate-migration-read-bounds-and-buffer-sizing.patch
 ++++++
Subject: smartcard: validate migration read bounds and buffer sizing
From: Marc-André Lureau [email protected] Fri Oct 2 15:25:32 2026 
+0400
Date: Sat Oct 10 14:53:44 2026 +0100:
Git: 22e00e0fcca06fafa18465ba69c7c418eccabf19

The read_data_ptr offset and read_size from smartcard migration data
were used in a memcpy without bounds checking, and the buffer was
allocated based on an attacker-controlled length field without
verifying consistency with read_size.

Validate that read_data_ptr and read_size fall within the message,
that the declared VSCMsgHeader length is consistent with read_size,
and that read_size fits in the allocated buffer before copying.
Reject invalid data instead of continuing migration restoration.

Size the read buffer for both the VSC header and payload, checking
for overflow when adding the header or doubling the buffer capacity.
Update the read cursor after reallocation so valid partial messages
can be restored and subsequent device reads use the new buffer.

This is part of CVE-2026-105958 fixes.

Related-to: https://gitlab.freedesktop.org/spice/spice/-/work_items/100
Reported-by: Tristan Madani (Talence Security)
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Marc-André Lureau <[email protected]>
Acked-by: Frediano Ziglio <[email protected]>

diff --git a/server/smartcard.cpp b/server/smartcard.cpp
index b04bcaa9..0069a262 100644
--- a/server/smartcard.cpp
+++ b/server/smartcard.cpp
@@ -88,15 +88,26 @@ static red::shared_ptr<RedMsgItem>
 smartcard_char_device_on_message_from_device(RedCharDeviceSmartcard *dev, 
VSCMsgHeader *header);
 static void smartcard_init(RedsState *reds);
 
-static void smartcard_read_buf_prepare(RedCharDeviceSmartcard *dev, 
VSCMsgHeader *vheader)
+static bool smartcard_read_buf_prepare(RedCharDeviceSmartcard *dev, 
VSCMsgHeader *vheader)
 {
-    uint32_t msg_len;
+    uint32_t msg_size = ntohl(vheader->length);
 
-    msg_len = ntohl(vheader->length);
-    if (msg_len > dev->priv->buf_size) {
-        dev->priv->buf_size = MAX(dev->priv->buf_size * 2, msg_len + 
sizeof(VSCMsgHeader));
-        dev->priv->buf = (uint8_t*) g_realloc(dev->priv->buf, 
dev->priv->buf_size);
+    if (msg_size > UINT32_MAX - sizeof(VSCMsgHeader)) {
+        spice_warning("smartcard message is too large");
+        return false;
     }
+    msg_size += sizeof(VSCMsgHeader);
+    if (msg_size > dev->priv->buf_size) {
+        uint32_t new_size = dev->priv->buf_size;
+        if (new_size <= UINT32_MAX / 2) {
+            new_size *= 2;
+        }
+        new_size = MAX(new_size, msg_size);
+        dev->priv->buf = (uint8_t*) g_realloc(dev->priv->buf, new_size);
+        dev->priv->buf_size = new_size;
+        dev->priv->buf_pos = dev->priv->buf + dev->priv->buf_used;
+    }
+    return true;
 }
 
 RedPipeItemPtr
@@ -122,7 +133,12 @@ RedCharDeviceSmartcard::read_one_msg_from_device()
             if (dev->priv->buf_used < sizeof(VSCMsgHeader)) {
                 continue;
             }
-            smartcard_read_buf_prepare(dev, vheader);
+            if (!smartcard_read_buf_prepare(dev, vheader)) {
+                if (dev->priv->scc) {
+                    dev->priv->scc->shutdown();
+                }
+                break;
+            }
             vheader = (VSCMsgHeader*)dev->priv->buf;
         }
         actual_length = ntohl(vheader->length);
@@ -421,21 +437,40 @@ void 
smartcard_channel_write_to_reader(RedCharDeviceWriteBuffer *write_buf)
     sin->st->write_buffer_add(write_buf);
 }
 
-static void smartcard_device_restore_partial_read(RedCharDeviceSmartcard *dev,
-                                                  SpiceMigrateDataSmartcard 
*mig_data)
+static bool smartcard_device_restore_partial_read(RedCharDeviceSmartcard *dev,
+                                                  SpiceMigrateDataSmartcard 
*mig_data,
+                                                  uint32_t msg_size)
 {
     uint8_t *read_data;
 
     spice_debug("read_size  %u", mig_data->read_size);
+    if (mig_data->read_data_ptr > msg_size ||
+        mig_data->read_size > msg_size - mig_data->read_data_ptr) {
+        spice_warning("bad read_data_ptr or read_size in smartcard migration 
data");
+        return false;
+    }
     read_data = (uint8_t *)mig_data + mig_data->read_data_ptr - 
sizeof(SpiceMigrateDataHeader);
     if (mig_data->read_size < sizeof(VSCMsgHeader)) {
         spice_assert(dev->priv->buf_size >= mig_data->read_size);
     } else {
-        smartcard_read_buf_prepare(dev, (VSCMsgHeader *)read_data);
+        VSCMsgHeader *vheader = (VSCMsgHeader *)read_data;
+        uint32_t msg_len = ntohl(vheader->length);
+        if (mig_data->read_size - sizeof(VSCMsgHeader) > msg_len) {
+            spice_warning("read_size exceeds declared message length in 
smartcard migration data");
+            return false;
+        }
+        if (!smartcard_read_buf_prepare(dev, vheader)) {
+            return false;
+        }
+    }
+    if (mig_data->read_size > dev->priv->buf_size) {
+        spice_warning("read_size exceeds buffer capacity in smartcard 
migration data");
+        return false;
     }
     memcpy(dev->priv->buf, read_data, mig_data->read_size);
     dev->priv->buf_used = mig_data->read_size;
     dev->priv->buf_pos = dev->priv->buf + mig_data->read_size;
+    return true;
 }
 
 bool smartcard_char_device_handle_migrate_data(RedCharDeviceSmartcard 
*smartcard,
@@ -444,7 +479,9 @@ bool 
smartcard_char_device_handle_migrate_data(RedCharDeviceSmartcard *smartcard
 {
     smartcard->priv->reader_added = mig_data->reader_added;
 
-    smartcard_device_restore_partial_read(smartcard, mig_data);
+    if (!smartcard_device_restore_partial_read(smartcard, mig_data, msg_size)) 
{
+        return false;
+    }
     return smartcard->restore(&mig_data->base, msg_size);
 }
 

++++++ 
008-CVE-2026-105958-smartcard-remove-spice_assert-on-attacker-controlled-read_size.patch
 ++++++
Subject: smartcard: remove spice_assert on attacker-controlled read_size
From: Marc-André Lureau [email protected] Thu Oct 8 15:21:56 2026 
+0400
Date: Sat Oct 10 14:53:47 2026 +0100:
Git: d0774f453b9259234df120438a334f1416717a82

The spice_assert(dev->priv->buf_size >= mig_data->read_size) operated
on migration data which is attacker-controlled. While practically
unreachable (buf_size is always initialized to APDUBufSize +
sizeof(VSCMsgHeader)), it is redundant with the graceful bounds check
added just below and inconsistent with the series' approach of not
aborting on invalid migration data.

Remove the assert and simplify the if/else into a single condition.

This is part of CVE-2026-105958 fixes.

Related-to: https://gitlab.freedesktop.org/spice/spice/-/work_items/100
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Marc-André Lureau <[email protected]>
Acked-by: Frediano Ziglio <[email protected]>

diff --git a/server/smartcard.cpp b/server/smartcard.cpp
index 0069a262..f142f929 100644
--- a/server/smartcard.cpp
+++ b/server/smartcard.cpp
@@ -450,9 +450,7 @@ static bool 
smartcard_device_restore_partial_read(RedCharDeviceSmartcard *dev,
         return false;
     }
     read_data = (uint8_t *)mig_data + mig_data->read_data_ptr - 
sizeof(SpiceMigrateDataHeader);
-    if (mig_data->read_size < sizeof(VSCMsgHeader)) {
-        spice_assert(dev->priv->buf_size >= mig_data->read_size);
-    } else {
+    if (mig_data->read_size >= sizeof(VSCMsgHeader)) {
         VSCMsgHeader *vheader = (VSCMsgHeader *)read_data;
         uint32_t msg_len = ntohl(vheader->length);
         if (mig_data->read_size - sizeof(VSCMsgHeader) > msg_len) {

++++++ 
009-CVE-2026-105958-smartcard-dont-allocate-based-on-attacker-controlled-message-length.patch
 ++++++
Subject: smartcard: don't allocate based on attacker-controlled message length
From: Marc-André Lureau [email protected] Thu Oct 8 15:25:54 2026 
+0400
Date: Sat Oct 10 14:53:52 2026 +0100:
Git: 1c55e33b51412b88695d9443743fe621f44d44e2

smartcard_read_buf_prepare() allocates a buffer sized by
ntohl(vheader->length) from migration data, which is attacker-
controlled and could be up to ~4GB, causing OOM.

Instead, grow the buffer to fit only the actual read_size (which is
bounded by the validated msg_size). The buffer will be resized during
normal operation via read_one_msg_from_device() if the full message
requires more space.

This is part of CVE-2026-105958 fixes.

Related-to: https://gitlab.freedesktop.org/spice/spice/-/work_items/100
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Marc-André Lureau <[email protected]>
Acked-by: Frediano Ziglio <[email protected]>

diff --git a/server/smartcard.cpp b/server/smartcard.cpp
index f142f929..66897a6b 100644
--- a/server/smartcard.cpp
+++ b/server/smartcard.cpp
@@ -457,13 +457,10 @@ static bool 
smartcard_device_restore_partial_read(RedCharDeviceSmartcard *dev,
             spice_warning("read_size exceeds declared message length in 
smartcard migration data");
             return false;
         }
-        if (!smartcard_read_buf_prepare(dev, vheader)) {
-            return false;
-        }
     }
     if (mig_data->read_size > dev->priv->buf_size) {
-        spice_warning("read_size exceeds buffer capacity in smartcard 
migration data");
-        return false;
+        dev->priv->buf_size = mig_data->read_size;
+        dev->priv->buf = (uint8_t *) g_realloc(dev->priv->buf, 
dev->priv->buf_size);
     }
     memcpy(dev->priv->buf, read_data, mig_data->read_size);
     dev->priv->buf_used = mig_data->read_size;

++++++ 
010-CVE-2026-105959-glz-fix-integer-truncation-in-encoder-malloc-callback.patch 
++++++
Subject: glz: fix integer truncation in encoder malloc callback
From: Marc-André Lureau [email protected] Tue Oct 6 00:07:47 2026 
+0400
Date: Thu Oct 8 23:02:28 2026 +0400:
Git: 4807b923d09e656d3f414ccde16d83189ba79ac9

The GlzEncoderUsrContext malloc callback takes an `int` size parameter,
but callers pass `size_t` expressions such as
`sizeof(uint32_t) * max_encoders`. On 64-bit systems this silently
truncates the allocation size from 64 to 32 bits.

For example, with a (client-controlled) max_encoders value of
0xC0000001, the product `4 * 0xC0000001 = 0x3_0000_0004` (about 12 GB)
is truncated to just 4 bytes when passed as `int`. The subsequent
initialisation loop then writes far past the allocated buffer, causing a
heap buffer overflow.

Fix by changing the callback signature from `int size` to `size_t size`
so the full allocation size is preserved.

This is part of CVE-2026-105959 fixes.

Related-to: https://gitlab.freedesktop.org/spice/spice/-/work_items/102
Reported-by: Tristan Madani (Talence Security)
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Marc-André Lureau <[email protected]>

diff --git a/server/glz-encoder.h b/server/glz-encoder.h
index 61da5ec4..d7c2181c 100644
--- a/server/glz-encoder.h
+++ b/server/glz-encoder.h
@@ -31,7 +31,7 @@ struct GlzEncoderUsrContext {
     SPICE_GNUC_PRINTF(2, 3) void (*error)(GlzEncoderUsrContext *usr, const 
char *fmt, ...);
     SPICE_GNUC_PRINTF(2, 3) void (*warn)(GlzEncoderUsrContext *usr, const char 
*fmt, ...);
     SPICE_GNUC_PRINTF(2, 3) void (*info)(GlzEncoderUsrContext *usr, const char 
*fmt, ...);
-    void    *(*malloc)(GlzEncoderUsrContext *usr, int size);
+    void    *(*malloc)(GlzEncoderUsrContext *usr, size_t size);
     void (*free)(GlzEncoderUsrContext *usr, void *ptr);
 
     // get the next chunk of the image which is entered to the dictionary. If 
the image is down to
diff --git a/server/image-encoders.cpp b/server/image-encoders.cpp
index 3b33bd81..efe4c47f 100644
--- a/server/image-encoders.cpp
+++ b/server/image-encoders.cpp
@@ -178,7 +178,7 @@ static void *lz_usr_malloc(LzUsrContext *usr, int size)
     return g_malloc(size);
 }
 
-static void *glz_usr_malloc(GlzEncoderUsrContext *usr, int size)
+static void *glz_usr_malloc(GlzEncoderUsrContext *usr, size_t size)
 {
     return g_malloc(size);
 }

++++++ 011-CVE-2026-105959-glz-validate-max_encoders-from-migration-data.patch 
++++++
Subject: glz: validate max_encoders from migration data
From: Marc-André Lureau [email protected] Tue Oct 6 00:07:59 2026 
+0400
Date: Thu Oct 8 23:02:28 2026 +0400:
Git: a3b9feb121fa6b19dc55cca6ec546e35fe4c9573

The max_encoders field in GlzEncDictRestoreData is read directly from
client-supplied migration data without validation. In normal operation
this value is always MAX_CACHE_CLIENTS (4), but a malicious migration
source can set it to an arbitrarily large value.

An oversized max_encoders causes glz_dictionary_window_create() to
allocate a buffer of `sizeof(uint32_t) * max_encoders` bytes, and then
glz_dictionary_window_reset() iterates max_encoders times writing to
that buffer. An attacker-controlled max_encoders could trigger enormous
allocations or out-of-bounds accesses.

Add a bounds check in restore_glz_dictionary() that rejects max_encoders
values exceeding MAX_CACHE_CLIENTS before calling
glz_enc_dictionary_restore().

This is part of CVE-2026-105959 fixes.

Related-to: https://gitlab.freedesktop.org/spice/spice/-/work_items/102
Reported-by: Tristan Madani (Talence Security)
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Marc-André Lureau <[email protected]>

diff --git a/server/image-encoders.cpp b/server/image-encoders.cpp
index efe4c47f..ea22302e 100644
--- a/server/image-encoders.cpp
+++ b/server/image-encoders.cpp
@@ -764,6 +764,10 @@ static GlzSharedDictionary 
*restore_glz_dictionary(ImageEncoders *enc,
                                                    uint8_t id,
                                                    GlzEncDictRestoreData 
*restore_data)
 {
+    if (restore_data->max_encoders > MAX_CACHE_CLIENTS) {
+        return nullptr;
+    }
+
     GlzEncDictContext *glz_dict =
         glz_enc_dictionary_restore(restore_data, &enc->glz_data.usr);
 

Reply via email to