From 004025a51180588354466aaf7322761585543715 Mon Sep 17 00:00:00 2001 From: pkgagent Date: Wed, 16 Sep 2026 14:28:46 +0800 Subject: [PATCH] Fix CVE-2026-91964, CVE-2026-91953, CVE-2026-91963, CVE-2026-91947 --- freerdp-2.11.6-CVE-2026-91947.patch | 172 +++++++++++++ freerdp-2.11.6-CVE-2026-91963.patch | 376 ++++++++++++++++++++++++++++ freerdp-2.11.6-CVE-2026-91964.patch | 119 +++++++++ freerdp.spec | 9 +- 4 files changed, 675 insertions(+), 1 deletion(-) create mode 100644 freerdp-2.11.6-CVE-2026-91947.patch create mode 100644 freerdp-2.11.6-CVE-2026-91963.patch create mode 100644 freerdp-2.11.6-CVE-2026-91964.patch diff --git a/freerdp-2.11.6-CVE-2026-91947.patch b/freerdp-2.11.6-CVE-2026-91947.patch new file mode 100644 index 0000000..3a36075 --- /dev/null +++ b/freerdp-2.11.6-CVE-2026-91947.patch @@ -0,0 +1,172 @@ +From a41558a3da0d59da3787e792c028e8953324e71a Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Mon, 24 Aug 2026 10:31:03 +0200 +Subject: [PATCH] [core,server] lock dynamic channel list while parsing + +--- + libfreerdp/core/server.c | 199 +++++++++++++++++++++------------------ + libfreerdp/core/server.h | 1 - + 2 files changed, 109 insertions(+), 91 deletions(-) +Adapted-by: PkgAgent/deepseek-v4 (modified to adapt to opencloudos-stream) +diff --git a/libfreerdp/core/server.c b/libfreerdp/core/server.c +index b8298a2..205d366 100644 +--- a/libfreerdp/core/server.c ++++ b/libfreerdp/core/server.c +@@ -61,13 +61,13 @@ typedef struct _wtsChannelMessage wtsChannelMessage; + static DWORD g_SessionId = 1; + static wHashTable* g_ServerHandles = NULL; + ++/* The caller must hold vcm->dynamicVirtualChannels locked for as long as the ++ * returned channel pointer is used. */ + static rdpPeerChannel* wts_get_dvc_channel_by_id(WTSVirtualChannelManager* vcm, UINT32 ChannelId) + { + int index; + int count; +- BOOL found = FALSE; + rdpPeerChannel* channel = NULL; +- ArrayList_Lock(vcm->dynamicVirtualChannels); + count = ArrayList_Count(vcm->dynamicVirtualChannels); + + for (index = 0; index < count; index++) +@@ -75,14 +75,10 @@ static rdpPeerChannel* wts_get_dvc_channel_by_id(WTSVirtualChannelManager* vcm, + channel = (rdpPeerChannel*)ArrayList_GetItem(vcm->dynamicVirtualChannels, index); + + if (channel->channelId == ChannelId) +- { +- found = TRUE; +- break; +- } ++ return channel; + } + +- ArrayList_Unlock(vcm->dynamicVirtualChannels); +- return found ? channel : NULL; ++ return NULL; + } + + static BOOL wts_queue_receive_data(rdpPeerChannel* channel, const BYTE* Buffer, UINT32 Length) +@@ -247,6 +243,51 @@ static void wts_read_drdynvc_close_response(rdpPeerChannel* channel) + MessageQueue_PostQuit(channel->queue, 0); + } + ++/* The caller must hold vcm->dynamicVirtualChannels locked. */ ++static BOOL wts_read_drdynvc_pdu_guarded(rdpPeerChannel* channel, int Cmd, int Sp, UINT32 ChannelId, ++ UINT32 length, rdpPeerChannel* dvc) ++{ ++ switch (Cmd) ++ { ++ case CREATE_REQUEST_PDU: ++ return wts_read_drdynvc_create_response(dvc, channel->receiveData, length); ++ ++ case DATA_FIRST_PDU: ++ if (dvc->dvc_open_state != DVC_OPEN_STATE_SUCCEEDED) ++ { ++ WLog_ERR(TAG, ++ "ChannelId %" PRIu32 " did not open successfully. " ++ "Ignoring DYNVC_DATA_FIRST PDU", ++ ChannelId); ++ return TRUE; ++ } ++ ++ return wts_read_drdynvc_data_first(dvc, channel->receiveData, Sp, length); ++ ++ case DATA_PDU: ++ if (dvc->dvc_open_state != DVC_OPEN_STATE_SUCCEEDED) ++ { ++ WLog_ERR(TAG, ++ "ChannelId %" PRIu32 " did not open successfully. " ++ "Ignoring DYNVC_DATA PDU", ++ ChannelId); ++ return TRUE; ++ } ++ ++ return wts_read_drdynvc_data(dvc, channel->receiveData, length); ++ ++ case CLOSE_REQUEST_PDU: ++ wts_read_drdynvc_close_response(dvc); ++ break; ++ ++ default: ++ WLog_ERR(TAG, "Cmd %d not recognized.", Cmd); ++ break; ++ } ++ ++ return TRUE; ++} ++ + static BOOL wts_read_drdynvc_pdu(rdpPeerChannel* channel) + { + UINT32 length; +@@ -255,7 +296,8 @@ static BOOL wts_read_drdynvc_pdu(rdpPeerChannel* channel) + int Sp; + int cbChId; + UINT32 ChannelId; +- rdpPeerChannel* dvc; ++ rdpPeerChannel* dvc = NULL; ++ BOOL ret; + length = Stream_GetPosition(channel->receiveData); + + if (length < 1) +@@ -281,52 +323,25 @@ static BOOL wts_read_drdynvc_pdu(rdpPeerChannel* channel) + + length -= value; + DEBUG_DVC("Cmd %d ChannelId %" PRIu32 " length %" PRIu32 "", Cmd, ChannelId, length); ++ ++ /* Keep the dynamic channel list locked for the whole dispatch: the channel ++ * pointer is only valid while a concurrent close (e.g. of an AUDIN channel) ++ * cannot remove and free it. */ ++ ArrayList_Lock(channel->vcm->dynamicVirtualChannels); + dvc = wts_get_dvc_channel_by_id(channel->vcm, ChannelId); + + if (dvc) + { +- switch (Cmd) +- { +- case CREATE_REQUEST_PDU: +- return wts_read_drdynvc_create_response(dvc, channel->receiveData, length); +- +- case DATA_FIRST_PDU: +- if (dvc->dvc_open_state != DVC_OPEN_STATE_SUCCEEDED) +- { +- WLog_ERR(TAG, +- "ChannelId %" PRIu32 " did not open successfully. " +- "Ignoring DYNVC_DATA_FIRST PDU", +- ChannelId); +- return TRUE; +- } +- +- return wts_read_drdynvc_data_first(dvc, channel->receiveData, Sp, length); +- +- case DATA_PDU: +- if (dvc->dvc_open_state != DVC_OPEN_STATE_SUCCEEDED) +- { +- WLog_ERR(TAG, +- "ChannelId %" PRIu32 " did not open successfully. " +- "Ignoring DYNVC_DATA PDU", +- ChannelId); +- return TRUE; +- } +- +- return wts_read_drdynvc_data(dvc, channel->receiveData, length); +- +- case CLOSE_REQUEST_PDU: +- wts_read_drdynvc_close_response(dvc); +- break; +- +- default: +- WLog_ERR(TAG, "Cmd %d not recognized.", Cmd); +- break; +- } ++ ret = wts_read_drdynvc_pdu_guarded(channel, Cmd, Sp, ChannelId, length, dvc); + } + else + { + DEBUG_DVC("ChannelId %" PRIu32 " not exists.", ChannelId); ++ ret = TRUE; + } ++ ++ ArrayList_Unlock(channel->vcm->dynamicVirtualChannels); ++ return ret; + } + else + { diff --git a/freerdp-2.11.6-CVE-2026-91963.patch b/freerdp-2.11.6-CVE-2026-91963.patch new file mode 100644 index 0000000..062ab24 --- /dev/null +++ b/freerdp-2.11.6-CVE-2026-91963.patch @@ -0,0 +1,376 @@ +From 78225a459b121bd8727b4dfeb62832d167c5afc9 Mon Sep 17 00:00:00 2001 +From: uchouT +Date: Wed, 29 Jul 2026 05:37:18 +0000 +Subject: [PATCH] [urbdrc] Fix completion framing for transfer requests + +Select URB_COMPLETION only for TRANSFER_IN_REQUEST results that carry output data. Keep transferred byte counts in URB_COMPLETION_NO_DATA for TRANSFER_OUT_REQUEST results without serializing an OutputBuffer. + +Preserve request direction across asynchronous callbacks and report actual transfer lengths for control and isochronous completions. + +Signed-off-by: uchouT +--- + channels/urbdrc/client/data_transfer.c | 53 ++++++++++++------- + .../urbdrc/client/libusb/libusb_udevice.c | 45 +++++++++------- + channels/urbdrc/client/urbdrc_main.h | 7 +-- + 3 files changed, 62 insertions(+), 43 deletions(-) +Adapted-by: PkgAgent/deepseek-v4 (modified to adapt to opencloudos-stream) +diff --git a/channels/urbdrc/client/data_transfer.c b/channels/urbdrc/client/data_transfer.c +index ed6ccf3..ddec730 100644 +--- a/channels/urbdrc/client/data_transfer.c ++++ b/channels/urbdrc/client/data_transfer.c +@@ -53,14 +53,25 @@ static void usb_process_get_port_status(IUDEVICE* pdev, wStream* out) + } + } + ++/* [MS-RDPEUSB] 2.2.7.2 and 2.2.7.3: ++ * Only a TRANSFER_IN_REQUEST that returns data carries an OutputBuffer. ++ * TRANSFER_OUT_REQUEST reports the transferred byte count in OutputBufferSize, ++ * but always uses URB_COMPLETION_NO_DATA. */ ++static UINT32 urb_completion_payload_size(int transferDir, UINT32 outputBufferSize) ++{ ++ return (transferDir == USBD_TRANSFER_DIRECTION_IN) ? outputBufferSize : 0; ++} ++ + static UINT urb_write_completion(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callback, BOOL noAck, + wStream* out, UINT32 InterfaceId, UINT32 MessageId, +- UINT32 RequestId, UINT32 usbd_status, UINT32 OutputBufferSize) ++ UINT32 RequestId, UINT32 usbd_status, UINT32 OutputBufferSize, ++ int transferDir) + { + if (!out) + return ERROR_INVALID_PARAMETER; + +- if (Stream_Capacity(out) < OutputBufferSize + 36) ++ const UINT32 payloadSize = urb_completion_payload_size(transferDir, OutputBufferSize); ++ if (Stream_Capacity(out) < payloadSize + 36ULL) + { + Stream_Free(out, TRUE); + return ERROR_INVALID_PARAMETER; +@@ -70,7 +81,7 @@ static UINT urb_write_completion(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callba + Stream_Write_UINT32(out, InterfaceId); /** interface */ + Stream_Write_UINT32(out, MessageId); /** message id */ + +- if (OutputBufferSize != 0) ++ if (payloadSize != 0) + Stream_Write_UINT32(out, URB_COMPLETION); + else + Stream_Write_UINT32(out, URB_COMPLETION_NO_DATA); +@@ -83,7 +94,7 @@ static UINT urb_write_completion(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callba + Stream_Write_UINT32(out, usbd_status); /** UsbdStatus */ + Stream_Write_UINT32(out, 0); /** HResult */ + Stream_Write_UINT32(out, OutputBufferSize); /** OutputBufferSize */ +- Stream_Seek(out, OutputBufferSize); ++ Stream_Seek(out, payloadSize); + + if (!noAck) + return stream_write_and_free(callback->plugin, callback->channel, out); +@@ -700,17 +711,17 @@ static UINT urb_control_transfer(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callba + } + + return urb_write_completion(pdev, callback, noAck, out, InterfaceId, MessageId, RequestId, +- usbd_status, OutputBufferSize); ++ usbd_status, OutputBufferSize, transferDir); + } + + static void urb_bulk_transfer_cb(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callback, wStream* out, + UINT32 InterfaceId, BOOL noAck, UINT32 MessageId, UINT32 RequestId, + UINT32 NumberOfPackets, UINT32 status, UINT32 StartFrame, +- UINT32 ErrorCount, UINT32 OutputBufferSize) ++ UINT32 ErrorCount, UINT32 OutputBufferSize, int transferDir) + { + if (!pdev->isChannelClosed(pdev)) + urb_write_completion(pdev, callback, noAck, out, InterfaceId, MessageId, RequestId, status, +- OutputBufferSize); ++ OutputBufferSize, transferDir); + else + Stream_Free(out, TRUE); + } +@@ -747,26 +758,28 @@ static UINT urb_bulk_or_interrupt_transfer(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBA + return pdev->bulk_or_interrupt_transfer( + pdev, callback, MessageId, RequestId, EndpointAddress, TransferFlags, noAck, + OutputBufferSize, (transferDir == USBD_TRANSFER_DIRECTION_OUT) ? Stream_Pointer(s) : NULL, +- urb_bulk_transfer_cb, 10000); ++ transferDir, urb_bulk_transfer_cb, 10000); + } + + static void urb_isoch_transfer_cb(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callback, wStream* out, + UINT32 InterfaceId, BOOL noAck, UINT32 MessageId, + UINT32 RequestId, UINT32 NumberOfPackets, UINT32 status, +- UINT32 StartFrame, UINT32 ErrorCount, UINT32 OutputBufferSize) ++ UINT32 StartFrame, UINT32 ErrorCount, UINT32 OutputBufferSize, ++ int transferDir) + { + if (!noAck) + { + UINT32 packetSize = (status == 0) ? NumberOfPackets * 12 : 0; ++ const UINT32 payloadSize = urb_completion_payload_size(transferDir, OutputBufferSize); + Stream_SetPosition(out, 0); + /* fill the send data */ + Stream_Write_UINT32(out, InterfaceId); /** interface */ + Stream_Write_UINT32(out, MessageId); /** message id */ + +- if (OutputBufferSize == 0) +- Stream_Write_UINT32(out, URB_COMPLETION_NO_DATA); /** function id */ +- else ++ if (payloadSize != 0) + Stream_Write_UINT32(out, URB_COMPLETION); /** function id */ ++ else ++ Stream_Write_UINT32(out, URB_COMPLETION_NO_DATA); /** function id */ + + Stream_Write_UINT32(out, RequestId); /** RequestId */ + Stream_Write_UINT32(out, 20 + packetSize); /** CbTsUrbResult */ +@@ -791,7 +804,7 @@ static void urb_isoch_transfer_cb(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callb + + Stream_Write_UINT32(out, 0); /** HResult */ + Stream_Write_UINT32(out, OutputBufferSize); /** OutputBufferSize */ +- Stream_Seek(out, OutputBufferSize); ++ Stream_Seek(out, payloadSize); + + stream_write_and_free(callback->plugin, callback->channel, out); + } +@@ -837,7 +850,7 @@ static UINT urb_isoch_transfer(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callback + return pdev->isoch_transfer( + pdev, callback, MessageId, RequestId, EndpointAddress, TransferFlags, StartFrame, + ErrorCount, noAck, packetDescriptorData, NumberOfPackets, OutputBufferSize, +- (transferDir == USBD_TRANSFER_DIRECTION_OUT) ? Stream_Pointer(s) : NULL, ++ (transferDir == USBD_TRANSFER_DIRECTION_OUT) ? Stream_Pointer(s) : NULL, transferDir, + urb_isoch_transfer_cb, 2000); + } + +@@ -918,7 +931,7 @@ static UINT urb_control_descriptor_request(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBA + } + + return urb_write_completion(pdev, callback, noAck, out, InterfaceId, MessageId, RequestId, +- usbd_status, OutputBufferSize); ++ usbd_status, OutputBufferSize, transferDir); + } + + static UINT urb_control_get_status_request(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callback, +@@ -977,7 +990,7 @@ static UINT urb_control_get_status_request(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBA + } + + return urb_write_completion(pdev, callback, noAck, out, InterfaceId, MessageId, RequestId, +- usbd_status, OutputBufferSize); ++ usbd_status, OutputBufferSize, transferDir); + } + + static UINT urb_control_vendor_or_class_request(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callback, +@@ -1060,7 +1073,7 @@ static UINT urb_control_vendor_or_class_request(IUDEVICE* pdev, URBDRC_CHANNEL_C + } + + return urb_write_completion(pdev, callback, noAck, out, InterfaceId, MessageId, RequestId, +- usbd_status, OutputBufferSize); ++ usbd_status, OutputBufferSize, transferDir); + } + + static UINT urb_os_feature_descriptor_request(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callback, +@@ -1145,7 +1158,7 @@ static UINT urb_os_feature_descriptor_request(IUDEVICE* pdev, URBDRC_CHANNEL_CAL + WLog_Print(urbdrc->log, WLOG_DEBUG, "os_feature_descriptor_request: error num %d", ret); + + return urb_write_completion(pdev, callback, noAck, out, InterfaceId, MessageId, RequestId, +- usbd_status, OutputBufferSize); ++ usbd_status, OutputBufferSize, transferDir); + } + + static UINT urb_pipe_request(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callback, wStream* s, +@@ -1224,7 +1237,7 @@ static UINT urb_pipe_request(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callback, + return ERROR_OUTOFMEMORY; + + return urb_write_completion(pdev, callback, noAck, out, InterfaceId, MessageId, RequestId, ret, +- 0); ++ 0, transferDir); + } + + static UINT urb_get_current_frame_number(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callback, +@@ -1341,7 +1354,7 @@ static UINT urb_control_get_configuration_request(IUDEVICE* pdev, URBDRC_CHANNEL + } + + return urb_write_completion(pdev, callback, noAck, out, InterfaceId, MessageId, RequestId, +- usbd_status, OutputBufferSize); ++ usbd_status, OutputBufferSize, transferDir); + } + + /* Unused function for current server */ +@@ -1399,7 +1412,7 @@ static UINT urb_control_get_interface_request(IUDEVICE* pdev, URBDRC_CHANNEL_CAL + } + + return urb_write_completion(pdev, callback, noAck, out, InterfaceId, MessageId, RequestId, +- usbd_status, OutputBufferSize); ++ usbd_status, OutputBufferSize, transferDir); + } + + static UINT urb_control_feature_request(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callback, +@@ -1493,7 +1506,7 @@ static UINT urb_control_feature_request(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* + } + + return urb_write_completion(pdev, callback, noAck, out, InterfaceId, MessageId, RequestId, +- usbd_status, OutputBufferSize); ++ usbd_status, OutputBufferSize, transferDir); + } + + static UINT urbdrc_process_transfer_request(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callback, +diff --git a/channels/urbdrc/client/libusb/libusb_udevice.c b/channels/urbdrc/client/libusb/libusb_udevice.c +index ef87f19..557ba6d 100644 +--- a/channels/urbdrc/client/libusb/libusb_udevice.c ++++ b/channels/urbdrc/client/libusb/libusb_udevice.c +@@ -73,6 +73,8 @@ struct _ASYNC_TRANSFER_USER_DATA + UINT32 ErrorCount; + IUDEVICE* idev; + UINT32 OutputBufferSize; ++ /* Completion framing depends on the outer RDPEUSB request direction. */ ++ int transferDir; + URBDRC_CHANNEL_CALLBACK* callback; + t_isoch_transfer_cb cb; + wArrayList* queue; +@@ -194,7 +196,8 @@ const char* usb_interface_class_to_string(uint8_t class) + static ASYNC_TRANSFER_USER_DATA* async_transfer_user_data_new(IUDEVICE* idev, UINT32 MessageId, + size_t offset, size_t BufferSize, + const BYTE* data, size_t packetSize, +- BOOL NoAck, t_isoch_transfer_cb cb, ++ BOOL NoAck, int transferDir, ++ t_isoch_transfer_cb cb, + URBDRC_CHANNEL_CALLBACK* callback) + { + ASYNC_TRANSFER_USER_DATA* user_data = calloc(1, sizeof(ASYNC_TRANSFER_USER_DATA)); +@@ -214,10 +217,9 @@ static ASYNC_TRANSFER_USER_DATA* async_transfer_user_data_new(IUDEVICE* idev, UI + Stream_Seek(user_data->data, offset); /* Skip header offset */ + if (data) + memcpy(Stream_Pointer(user_data->data), data, BufferSize); +- else +- user_data->OutputBufferSize = BufferSize; + + user_data->noack = NoAck; ++ user_data->transferDir = transferDir; + user_data->cb = cb; + user_data->callback = callback; + user_data->idev = idev; +@@ -275,6 +277,7 @@ static void LIBUSB_CALL func_iso_callback(struct libusb_transfer* transfer) + index += act_len; + } + } ++ user_data->OutputBufferSize = index; + } + /* fallthrough */ + +@@ -294,7 +297,7 @@ static void LIBUSB_CALL func_iso_callback(struct libusb_transfer* transfer) + InterfaceId, user_data->noack, user_data->MessageId, RequestID, + transfer->num_iso_packets, transfer->status, + user_data->StartFrame, user_data->ErrorCount, +- user_data->OutputBufferSize); ++ user_data->OutputBufferSize, user_data->transferDir); + user_data->data = NULL; + } + ArrayList_Remove(list, transfer); +@@ -361,7 +364,7 @@ static void LIBUSB_CALL func_bulk_transfer_cb(struct libusb_transfer* transfer) + user_data->cb(user_data->idev, user_data->callback, user_data->data, InterfaceId, + user_data->noack, user_data->MessageId, RequestID, transfer->num_iso_packets, + transfer->status, user_data->StartFrame, user_data->ErrorCount, +- transfer->actual_length); ++ transfer->actual_length, user_data->transferDir); + user_data->data = NULL; + ArrayList_Remove(list, transfer); + } +@@ -918,6 +921,9 @@ static int libusb_udev_os_feature_descriptor_request(IUDEVICE* idev, UINT32 Requ + UDEVICE* pdev = (UDEVICE*)idev; + BYTE ms_string_desc[0x13] = { 0 }; + int error = 0; ++ /* Do not leak uninitialized Buffer contents on transfer failure. */ ++ const UINT16 requestedSize = (UINT16)*BufferSize; ++ *BufferSize = 0; + /* + pdev->request_queue->register_request(pdev->request_queue, RequestId, NULL, 0); + */ +@@ -934,7 +940,7 @@ static int libusb_udev_os_feature_descriptor_request(IUDEVICE* idev, UINT32 Requ + error = libusb_control_transfer(pdev->libusb_handle, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | Recipient, + bMS_Vendorcode, (InterfaceNumber << 8) | Ms_PageIndex, +- Ms_featureDescIndex, Buffer, *BufferSize, Timeout); ++ Ms_featureDescIndex, Buffer, requestedSize, Timeout); + log_libusb_result(pdev->urbdrc->log, WLOG_DEBUG, "libusb_control_transfer", error); + + if (error >= 0) +@@ -1195,7 +1201,7 @@ static int libusb_udev_isoch_transfer(IUDEVICE* idev, URBDRC_CHANNEL_CALLBACK* c + UINT32 TransferFlags, UINT32 StartFrame, UINT32 ErrorCount, + BOOL NoAck, const BYTE* packetDescriptorData, + UINT32 NumberOfPackets, UINT32 BufferSize, const BYTE* Buffer, +- t_isoch_transfer_cb cb, UINT32 Timeout) ++ int transferDir, t_isoch_transfer_cb cb, UINT32 Timeout) + { + UINT32 iso_packet_size; + UDEVICE* pdev = (UDEVICE*)idev; +@@ -1210,7 +1216,7 @@ static int libusb_udev_isoch_transfer(IUDEVICE* idev, URBDRC_CHANNEL_CALLBACK* c + + urbdrc = pdev->urbdrc; + user_data = async_transfer_user_data_new(idev, MessageId, 48, BufferSize, Buffer, +- outSize + 1024, NoAck, cb, callback); ++ outSize + 1024, NoAck, transferDir, cb, callback); + + if (!user_data) + return -1; +@@ -1272,7 +1278,10 @@ static BOOL libusb_udev_control_transfer(IUDEVICE* idev, UINT32 RequestId, UINT3 + if (status >= 0) + *BufferSize = (UINT32)status; + else ++ { ++ *BufferSize = 0; + log_libusb_result(pdev->urbdrc->log, WLOG_ERROR, "libusb_control_transfer", status); ++ } + + if (!func_set_usbd_status(pdev->urbdrc, pdev, UrbdStatus, status)) + return FALSE; +@@ -1284,7 +1293,8 @@ static int libusb_udev_bulk_or_interrupt_transfer(IUDEVICE* idev, URBDRC_CHANNEL + UINT32 MessageId, UINT32 RequestId, + UINT32 EndpointAddress, UINT32 TransferFlags, + BOOL NoAck, UINT32 BufferSize, const BYTE* data, +- t_isoch_transfer_cb cb, UINT32 Timeout) ++ int transferDir, t_isoch_transfer_cb cb, ++ UINT32 Timeout) + { + UINT32 transfer_type; + UDEVICE* pdev = (UDEVICE*)idev; +@@ -1298,8 +1308,8 @@ static int libusb_udev_bulk_or_interrupt_transfer(IUDEVICE* idev, URBDRC_CHANNEL + return -1; + + urbdrc = pdev->urbdrc; +- user_data = +- async_transfer_user_data_new(idev, MessageId, 36, BufferSize, data, 0, NoAck, cb, callback); ++ user_data = async_transfer_user_data_new(idev, MessageId, 36, BufferSize, data, 0, NoAck, ++ transferDir, cb, callback); + + if (!user_data) + return -1; +diff --git a/channels/urbdrc/client/urbdrc_main.h b/channels/urbdrc/client/urbdrc_main.h +index 8335714..108a335 100644 +--- a/channels/urbdrc/client/urbdrc_main.h ++++ b/channels/urbdrc/client/urbdrc_main.h +@@ -114,7 +114,8 @@ struct _TRANSFER_DATA + typedef void (*t_isoch_transfer_cb)(IUDEVICE* idev, URBDRC_CHANNEL_CALLBACK* callback, wStream* out, + UINT32 InterfaceId, BOOL noAck, UINT32 MessageId, + UINT32 RequestId, UINT32 NumberOfPackets, UINT32 status, +- UINT32 StartFrame, UINT32 ErrorCount, UINT32 OutputBufferSize); ++ UINT32 StartFrame, UINT32 ErrorCount, UINT32 OutputBufferSize, ++ int transferDir); + + struct _IUDEVICE + { +@@ -123,8 +124,8 @@ struct _IUDEVICE + UINT32 RequestId, UINT32 EndpointAddress, UINT32 TransferFlags, + UINT32 StartFrame, UINT32 ErrorCount, BOOL NoAck, + const BYTE* packetDescriptorData, UINT32 NumberOfPackets, +- UINT32 BufferSize, const BYTE* Buffer, t_isoch_transfer_cb cb, +- UINT32 Timeout); ++ UINT32 BufferSize, const BYTE* Buffer, int transferDir, ++ t_isoch_transfer_cb cb, UINT32 Timeout); + + BOOL(*control_transfer) + (IUDEVICE* idev, UINT32 RequestId, UINT32 EndpointAddress, UINT32 TransferFlags, +@@ -134,7 +135,8 @@ struct _IUDEVICE + int (*bulk_or_interrupt_transfer)(IUDEVICE* idev, URBDRC_CHANNEL_CALLBACK* callback, + UINT32 MessageId, UINT32 RequestId, UINT32 EndpointAddress, + UINT32 TransferFlags, BOOL NoAck, UINT32 BufferSize, +- const BYTE* data, t_isoch_transfer_cb cb, UINT32 Timeout); ++ const BYTE* data, int transferDir, t_isoch_transfer_cb cb, ++ UINT32 Timeout); + + int (*select_configuration)(IUDEVICE* idev, UINT32 bConfigurationValue); + diff --git a/freerdp-2.11.6-CVE-2026-91964.patch b/freerdp-2.11.6-CVE-2026-91964.patch new file mode 100644 index 0000000..8391ede --- /dev/null +++ b/freerdp-2.11.6-CVE-2026-91964.patch @@ -0,0 +1,119 @@ +From 70d05577a3a161e1fa2666ab7dadb01d14661e94 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Wed, 19 Aug 2026 09:13:23 +0200 +Subject: [PATCH] [core,nego] fix capacity checks + +--- + libfreerdp/core/nego.c | 44 +++++++++++++++++++++++------------------- + 1 file changed, 24 insertions(+), 20 deletions(-) +Adapted-by: PkgAgent/deepseek-v4 (modified to adapt to opencloudos-stream) +diff --git a/libfreerdp/core/nego.c b/libfreerdp/core/nego.c +index 5c3c54a..2e519d3 100644 +--- a/libfreerdp/core/nego.c ++++ b/libfreerdp/core/nego.c +@@ -850,12 +850,7 @@ void nego_send(rdpNego* nego) + BOOL nego_send_negotiation_request(rdpNego* nego) + { + BOOL rc = FALSE; +- wStream* s; +- size_t length; +- size_t bm, em; +- BYTE flags = 0; +- size_t cookie_length; +- s = Stream_New(NULL, 512); ++ wStream* s = Stream_New(NULL, 512); + + if (!s) + { +@@ -863,12 +858,15 @@ BOOL nego_send_negotiation_request(rdpNego* nego) + return FALSE; + } + +- length = TPDU_CONNECTION_REQUEST_LENGTH; +- bm = Stream_GetPosition(s); +- Stream_Seek(s, length); ++ const size_t bm = Stream_GetPosition(s); ++ if (!Stream_SafeSeek(s, TPDU_CONNECTION_REQUEST_LENGTH)) ++ return FALSE; + + if (nego->RoutingToken) + { ++ if (!Stream_EnsureRemainingCapacity(s, nego->RoutingTokenLength)) ++ return FALSE; ++ + Stream_Write(s, nego->RoutingToken, nego->RoutingTokenLength); + + /* Ensure Routing Token is correctly terminated - may already be present in string */ +@@ -878,52 +876,63 @@ BOOL nego_send_negotiation_request(rdpNego* nego) + (nego->RoutingToken[nego->RoutingTokenLength - 1] == 0x0A)) + { + WLog_DBG(TAG, "Routing token looks correctly terminated - use verbatim"); +- length += nego->RoutingTokenLength; + } + else + { + WLog_DBG(TAG, "Adding terminating CRLF to routing token"); ++ if (!Stream_EnsureRemainingCapacity(s, 2)) ++ return FALSE; ++ + Stream_Write_UINT8(s, 0x0D); /* CR */ + Stream_Write_UINT8(s, 0x0A); /* LF */ +- length += nego->RoutingTokenLength + 2; + } + } + else if (nego->cookie) + { +- cookie_length = strlen(nego->cookie); ++ size_t cookie_length = strlen(nego->cookie); + + if (cookie_length > nego->CookieMaxLength) + cookie_length = nego->CookieMaxLength; + ++ if (!Stream_EnsureRemainingCapacity(s, 17)) ++ return FALSE; + Stream_Write(s, "Cookie: mstshash=", 17); ++ if (!Stream_EnsureRemainingCapacity(s, cookie_length)) ++ return FALSE; + Stream_Write(s, (BYTE*)nego->cookie, cookie_length); ++ if (!Stream_EnsureRemainingCapacity(s, 2)) ++ return FALSE; + Stream_Write_UINT8(s, 0x0D); /* CR */ + Stream_Write_UINT8(s, 0x0A); /* LF */ +- length += cookie_length + 19; + } + + WLog_DBG(TAG, "RequestedProtocols: %" PRIu32 "", nego->RequestedProtocols); + + if ((nego->RequestedProtocols > PROTOCOL_RDP) || (nego->sendNegoData)) + { ++ UINT8 flags = 0; ++ + /* RDP_NEG_DATA must be present for TLS and NLA */ + if (nego->RestrictedAdminModeRequired) + flags |= RESTRICTED_ADMIN_MODE_REQUIRED; + ++ if (!Stream_EnsureRemainingCapacity(s, 8)) ++ return FALSE; ++ + Stream_Write_UINT8(s, TYPE_RDP_NEG_REQ); + Stream_Write_UINT8(s, flags); + Stream_Write_UINT16(s, 8); /* RDP_NEG_DATA length (8) */ + Stream_Write_UINT32(s, nego->RequestedProtocols); /* requestedProtocols */ +- length += 8; + } + +- if (length > UINT16_MAX) ++ const size_t em = Stream_GetPosition(s); ++ if ((em < 5) || (em > UINT16_MAX)) + goto fail; + +- em = Stream_GetPosition(s); + Stream_SetPosition(s, bm); +- tpkt_write_header(s, (UINT16)length); +- tpdu_write_connection_request(s, (UINT16)length - 5); ++ if (!tpkt_write_header(s, (UINT16)em)) ++ goto fail; ++ tpdu_write_connection_request(s, (UINT16)em - 5); + Stream_SetPosition(s, em); + Stream_SealLength(s); + rc = (transport_write(nego->transport, s) >= 0); diff --git a/freerdp.spec b/freerdp.spec index a32cb40..a2b933d 100644 --- a/freerdp.spec +++ b/freerdp.spec @@ -17,7 +17,7 @@ Summary: Free implementation of the Remote Desktop Protocol (RDP) Name: freerdp Version: 2.11.6 -Release: 19%{?dist} +Release: 20%{?dist} License: ASL 2.0 URL: http://www.freerdp.com/ Source0: https://github.com/FreeRDP/FreeRDP/archive/%{version}/%{name}-%{version}.tar.gz @@ -55,6 +55,9 @@ Patch0031: freerdp-2.11.6-CVE-2026-55191.patch Patch0032: freerdp-2.11.6-CVE-2026-55194.patch Patch0033: freerdp-2.11.6-CVE-2026-85089-1.patch Patch0034: freerdp-2.11.6-CVE-2026-85089-2.patch +Patch0035: freerdp-2.11.6-CVE-2026-91964.patch +Patch0036: freerdp-2.11.6-CVE-2026-91963.patch +Patch0037: freerdp-2.11.6-CVE-2026-91947.patch BuildRequires: gcc gcc-c++ alsa-lib-devel cmake cups-devel gsm-devel lame-devel libicu-devel libjpeg-turbo-devel BuildRequires: libX11-devel libXcursor-devel libXdamage-devel libXext-devel libXi-devel libXinerama-devel @@ -254,6 +257,10 @@ find %{buildroot} -name "*.a" -delete %{_libdir}/pkgconfig/winpr-tools2.pc %changelog +* Wed Sep 16 2026 PkgAgent Robot - 2.11.6-20 +- [Type] security +- [DESC] Fix CVE-2026-91964, CVE-2026-91953, CVE-2026-91963, CVE-2026-91947 + * Thu Sep 03 2026 PkgAgent Robot - 2.11.6-19 - [Type] security - [DESC] Fix CVE-2026-85089: zero reserved padding bytes in Save Session Info PDU writers (Stream_Seek to Stream_Zero) and zero-initialize wStream buffers -- Gitee