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);
