Compare commits

...

No commits in common. "c8" and "c9-beta" have entirely different histories.
c8 ... c9-beta

28 changed files with 551 additions and 3475 deletions

View File

@ -1,560 +0,0 @@
From a441e68fccc8ef137c9e8e667fed6adaab34ba37 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 1 Oct 2024 15:43:07 +0200
Subject: [PATCH] Revert "Moved clipboard utils to core library, fixes #6760
(#7752)"
This reverts commit 26a83e6ccde272c1bbc2b2591325dc7a493811bc.
---
channels/cliprdr/client/cliprdr_format.c | 195 +++++++++++++++++++
include/freerdp/channels/cliprdr.h | 12 +-
include/freerdp/utils/cliprdr_utils.h | 48 -----
libfreerdp/utils/CMakeLists.txt | 1 -
libfreerdp/utils/cliprdr_utils.c | 235 -----------------------
5 files changed, 206 insertions(+), 285 deletions(-)
delete mode 100644 include/freerdp/utils/cliprdr_utils.h
delete mode 100644 libfreerdp/utils/cliprdr_utils.c
diff --git a/channels/cliprdr/client/cliprdr_format.c b/channels/cliprdr/client/cliprdr_format.c
index 0b6111b96..4c31a1b08 100644
--- a/channels/cliprdr/client/cliprdr_format.c
+++ b/channels/cliprdr/client/cliprdr_format.c
@@ -173,3 +173,198 @@ UINT cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UI
return error;
}
+
+static UINT64 filetime_to_uint64(FILETIME value)
+{
+ UINT64 converted = 0;
+ converted |= (UINT32)value.dwHighDateTime;
+ converted <<= 32;
+ converted |= (UINT32)value.dwLowDateTime;
+ return converted;
+}
+
+static FILETIME uint64_to_filetime(UINT64 value)
+{
+ FILETIME converted;
+ converted.dwLowDateTime = (UINT32)(value >> 0);
+ converted.dwHighDateTime = (UINT32)(value >> 32);
+ return converted;
+}
+
+#define CLIPRDR_FILEDESCRIPTOR_SIZE (4 + 32 + 4 + 16 + 8 + 8 + 520)
+
+/**
+ * Parse a packed file list.
+ *
+ * The resulting array must be freed with the `free()` function.
+ *
+ * @param [in] format_data packed `CLIPRDR_FILELIST` to parse.
+ * @param [in] format_data_length length of `format_data` in bytes.
+ * @param [out] file_descriptor_array parsed array of `FILEDESCRIPTOR` structs.
+ * @param [out] file_descriptor_count number of elements in `file_descriptor_array`.
+ *
+ * @returns 0 on success, otherwise a Win32 error code.
+ */
+UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length,
+ FILEDESCRIPTORW** file_descriptor_array, UINT32* file_descriptor_count)
+{
+ UINT result = NO_ERROR;
+ UINT32 i;
+ UINT32 count = 0;
+ wStream* s = NULL;
+
+ if (!format_data || !file_descriptor_array || !file_descriptor_count)
+ return ERROR_BAD_ARGUMENTS;
+
+ s = Stream_New((BYTE*)format_data, format_data_length);
+ if (!s)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ if (Stream_GetRemainingLength(s) < 4)
+ {
+ WLog_ERR(TAG, "invalid packed file list");
+
+ result = ERROR_INCORRECT_SIZE;
+ goto out;
+ }
+
+ Stream_Read_UINT32(s, count); /* cItems (4 bytes) */
+
+ if (Stream_GetRemainingLength(s) / CLIPRDR_FILEDESCRIPTOR_SIZE < count)
+ {
+ WLog_ERR(TAG, "packed file list is too short: expected %" PRIuz ", have %" PRIuz,
+ ((size_t)count) * CLIPRDR_FILEDESCRIPTOR_SIZE, Stream_GetRemainingLength(s));
+
+ result = ERROR_INCORRECT_SIZE;
+ goto out;
+ }
+
+ *file_descriptor_count = count;
+ *file_descriptor_array = calloc(count, sizeof(FILEDESCRIPTORW));
+ if (!*file_descriptor_array)
+ {
+ result = ERROR_NOT_ENOUGH_MEMORY;
+ goto out;
+ }
+
+ for (i = 0; i < count; i++)
+ {
+ int c;
+ UINT64 lastWriteTime;
+ FILEDESCRIPTORW* file = &((*file_descriptor_array)[i]);
+
+ Stream_Read_UINT32(s, file->dwFlags); /* flags (4 bytes) */
+ Stream_Seek(s, 32); /* reserved1 (32 bytes) */
+ Stream_Read_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
+ Stream_Seek(s, 16); /* reserved2 (16 bytes) */
+ Stream_Read_UINT64(s, lastWriteTime); /* lastWriteTime (8 bytes) */
+ file->ftLastWriteTime = uint64_to_filetime(lastWriteTime);
+ Stream_Read_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
+ Stream_Read_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
+ for (c = 0; c < 260; c++) /* cFileName (520 bytes) */
+ Stream_Read_UINT16(s, file->cFileName[c]);
+ }
+
+ if (Stream_GetRemainingLength(s) > 0)
+ WLog_WARN(TAG, "packed file list has %" PRIuz " excess bytes",
+ Stream_GetRemainingLength(s));
+out:
+ Stream_Free(s, FALSE);
+
+ return result;
+}
+
+#define CLIPRDR_MAX_FILE_SIZE (2U * 1024 * 1024 * 1024)
+
+/**
+ * Serialize a packed file list.
+ *
+ * The resulting format data must be freed with the `free()` function.
+ *
+ * @param [in] file_descriptor_array array of `FILEDESCRIPTOR` structs to serialize.
+ * @param [in] file_descriptor_count number of elements in `file_descriptor_array`.
+ * @param [out] format_data serialized CLIPRDR_FILELIST.
+ * @param [out] format_data_length length of `format_data` in bytes.
+ *
+ * @returns 0 on success, otherwise a Win32 error code.
+ */
+UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array,
+ UINT32 file_descriptor_count, BYTE** format_data,
+ UINT32* format_data_length)
+{
+ return cliprdr_serialize_file_list_ex(CB_STREAM_FILECLIP_ENABLED, file_descriptor_array,
+ file_descriptor_count, format_data, format_data_length);
+}
+
+UINT cliprdr_serialize_file_list_ex(UINT32 flags, const FILEDESCRIPTORW* file_descriptor_array,
+ UINT32 file_descriptor_count, BYTE** format_data,
+ UINT32* format_data_length)
+{
+ UINT result = NO_ERROR;
+ UINT32 i;
+ wStream* s = NULL;
+
+ if (!file_descriptor_array || !format_data || !format_data_length)
+ return ERROR_BAD_ARGUMENTS;
+
+ if ((flags & CB_STREAM_FILECLIP_ENABLED) == 0)
+ {
+ WLog_WARN(TAG, "No file clipboard support annouonced!");
+ return ERROR_BAD_ARGUMENTS;
+ }
+
+ s = Stream_New(NULL, 4 + file_descriptor_count * CLIPRDR_FILEDESCRIPTOR_SIZE);
+ if (!s)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ Stream_Write_UINT32(s, file_descriptor_count); /* cItems (4 bytes) */
+
+ for (i = 0; i < file_descriptor_count; i++)
+ {
+ int c;
+ UINT64 lastWriteTime;
+ const FILEDESCRIPTORW* file = &file_descriptor_array[i];
+
+ /*
+ * There is a known issue with Windows server getting stuck in
+ * an infinite loop when downloading files that are larger than
+ * 2 gigabytes. Do not allow clients to send such file lists.
+ *
+ * https://support.microsoft.com/en-us/help/2258090
+ */
+ if ((flags & CB_HUGE_FILE_SUPPORT_ENABLED) == 0)
+ {
+ if ((file->nFileSizeHigh > 0) || (file->nFileSizeLow >= CLIPRDR_MAX_FILE_SIZE))
+ {
+ WLog_ERR(TAG, "cliprdr does not support files over 2 GB");
+ result = ERROR_FILE_TOO_LARGE;
+ goto error;
+ }
+ }
+
+ Stream_Write_UINT32(s, file->dwFlags); /* flags (4 bytes) */
+ Stream_Zero(s, 32); /* reserved1 (32 bytes) */
+ Stream_Write_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
+ Stream_Zero(s, 16); /* reserved2 (16 bytes) */
+ lastWriteTime = filetime_to_uint64(file->ftLastWriteTime);
+ Stream_Write_UINT64(s, lastWriteTime); /* lastWriteTime (8 bytes) */
+ Stream_Write_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
+ Stream_Write_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
+ for (c = 0; c < 260; c++) /* cFileName (520 bytes) */
+ Stream_Write_UINT16(s, file->cFileName[c]);
+ }
+
+ Stream_SealLength(s);
+
+ Stream_GetBuffer(s, *format_data);
+ Stream_GetLength(s, *format_data_length);
+
+ Stream_Free(s, FALSE);
+
+ return result;
+
+error:
+ Stream_Free(s, TRUE);
+
+ return result;
+}
diff --git a/include/freerdp/channels/cliprdr.h b/include/freerdp/channels/cliprdr.h
index fbf23f6e5..86fc65890 100644
--- a/include/freerdp/channels/cliprdr.h
+++ b/include/freerdp/channels/cliprdr.h
@@ -22,7 +22,6 @@
#include <freerdp/api.h>
#include <freerdp/types.h>
-#include <freerdp/utils/cliprdr_utils.h>
#include <winpr/shell.h>
@@ -94,6 +93,17 @@ extern "C"
{
#endif
+ FREERDP_API UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length,
+ FILEDESCRIPTORW** file_descriptor_array,
+ UINT32* file_descriptor_count);
+ FREERDP_API UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array,
+ UINT32 file_descriptor_count, BYTE** format_data,
+ UINT32* format_data_length);
+ FREERDP_API UINT cliprdr_serialize_file_list_ex(UINT32 flags,
+ const FILEDESCRIPTORW* file_descriptor_array,
+ UINT32 file_descriptor_count,
+ BYTE** format_data, UINT32* format_data_length);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/freerdp/utils/cliprdr_utils.h b/include/freerdp/utils/cliprdr_utils.h
deleted file mode 100644
index 59ecf848f..000000000
--- a/include/freerdp/utils/cliprdr_utils.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * FreeRDP: A Remote Desktop Protocol Implementation
- * RDPDR utility functions
- *
- * Copyright 2022 Armin Novak <armin.novak@thincast.com>
- * Copyright 2022 Thincast Technologies GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef FREERDP_UTILS_CLIPRDR_H
-#define FREERDP_UTILS_CLIPRDR_H
-
-#include <winpr/wtypes.h>
-#include <winpr/shell.h>
-#include <freerdp/api.h>
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
- FREERDP_API UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length,
- FILEDESCRIPTORW** file_descriptor_array,
- UINT32* file_descriptor_count);
- FREERDP_API UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array,
- UINT32 file_descriptor_count, BYTE** format_data,
- UINT32* format_data_length);
- FREERDP_API UINT cliprdr_serialize_file_list_ex(UINT32 flags,
- const FILEDESCRIPTORW* file_descriptor_array,
- UINT32 file_descriptor_count,
- BYTE** format_data, UINT32* format_data_length);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/libfreerdp/utils/CMakeLists.txt b/libfreerdp/utils/CMakeLists.txt
index 2ec561d33..7bff6736a 100644
--- a/libfreerdp/utils/CMakeLists.txt
+++ b/libfreerdp/utils/CMakeLists.txt
@@ -20,7 +20,6 @@ set(MODULE_PREFIX "FREERDP_UTILS")
set(${MODULE_PREFIX}_SRCS
passphrase.c
- cliprdr_utils.c
pcap.c
profiler.c
ringbuffer.c
diff --git a/libfreerdp/utils/cliprdr_utils.c b/libfreerdp/utils/cliprdr_utils.c
deleted file mode 100644
index 7fb99d63a..000000000
--- a/libfreerdp/utils/cliprdr_utils.c
+++ /dev/null
@@ -1,235 +0,0 @@
-/**
- * FreeRDP: A Remote Desktop Protocol Implementation
- * Clipboard Virtual Channel Extension
- *
- * Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
- * Copyright 2022 Armin Novak <anovak@thincast.com
- * Copyright 2022 Thincast Technologies GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <winpr/stream.h>
-#include <freerdp/utils/cliprdr_utils.h>
-#include <freerdp/channels/cliprdr.h>
-
-#include <freerdp/log.h>
-#define TAG FREERDP_TAG("utils." CLIPRDR_SVC_CHANNEL_NAME)
-
-#define CLIPRDR_FILEDESCRIPTOR_SIZE (4 + 32 + 4 + 16 + 8 + 8 + 520)
-#define CLIPRDR_MAX_FILE_SIZE (2U * 1024 * 1024 * 1024)
-
-static UINT64 filetime_to_uint64(FILETIME value)
-{
- UINT64 converted = 0;
- converted |= (UINT32)value.dwHighDateTime;
- converted <<= 32;
- converted |= (UINT32)value.dwLowDateTime;
- return converted;
-}
-
-static FILETIME uint64_to_filetime(UINT64 value)
-{
- FILETIME converted;
- converted.dwLowDateTime = (UINT32)(value >> 0);
- converted.dwHighDateTime = (UINT32)(value >> 32);
- return converted;
-}
-
-/**
- * Parse a packed file list.
- *
- * The resulting array must be freed with the `free()` function.
- *
- * @param [in] format_data packed `CLIPRDR_FILELIST` to parse.
- * @param [in] format_data_length length of `format_data` in bytes.
- * @param [out] file_descriptor_array parsed array of `FILEDESCRIPTOR` structs.
- * @param [out] file_descriptor_count number of elements in `file_descriptor_array`.
- *
- * @returns 0 on success, otherwise a Win32 error code.
- */
-UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length,
- FILEDESCRIPTORW** file_descriptor_array, UINT32* file_descriptor_count)
-{
- UINT result = NO_ERROR;
- UINT32 i;
- UINT32 count = 0;
- wStream sbuffer;
- wStream* s = &sbuffer;
-
- if (!format_data || !file_descriptor_array || !file_descriptor_count)
- return ERROR_BAD_ARGUMENTS;
-
- Stream_StaticInit(&sbuffer, format_data, format_data_length);
- if (!s)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- if (Stream_GetRemainingLength(s) < 4)
- {
- WLog_ERR(TAG, "invalid packed file list");
-
- result = ERROR_INCORRECT_SIZE;
- goto out;
- }
-
- Stream_Read_UINT32(s, count); /* cItems (4 bytes) */
-
- if (Stream_GetRemainingLength(s) / CLIPRDR_FILEDESCRIPTOR_SIZE < count)
- {
- WLog_ERR(TAG, "packed file list is too short: expected %" PRIuz ", have %" PRIuz,
- ((size_t)count) * CLIPRDR_FILEDESCRIPTOR_SIZE, Stream_GetRemainingLength(s));
-
- result = ERROR_INCORRECT_SIZE;
- goto out;
- }
-
- *file_descriptor_count = count;
- *file_descriptor_array = calloc(count, sizeof(FILEDESCRIPTORW));
- if (!*file_descriptor_array)
- {
- result = ERROR_NOT_ENOUGH_MEMORY;
- goto out;
- }
-
- for (i = 0; i < count; i++)
- {
- UINT64 tmp;
- FILEDESCRIPTORW* file = &((*file_descriptor_array)[i]);
-
- Stream_Read_UINT32(s, file->dwFlags); /* flags (4 bytes) */
- Stream_Read_UINT32(s, file->clsid.Data1);
- Stream_Read_UINT16(s, file->clsid.Data2);
- Stream_Read_UINT16(s, file->clsid.Data3);
- Stream_Read(s, &file->clsid.Data4, sizeof(file->clsid.Data4));
- Stream_Read_INT32(s, file->sizel.cx);
- Stream_Read_INT32(s, file->sizel.cy);
- Stream_Read_INT32(s, file->pointl.x);
- Stream_Read_INT32(s, file->pointl.y);
- Stream_Read_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
- Stream_Read_UINT64(s, tmp); /* ftCreationTime (8 bytes) */
- file->ftCreationTime = uint64_to_filetime(tmp);
- Stream_Read_UINT64(s, tmp); /* ftLastAccessTime (8 bytes) */
- file->ftLastAccessTime = uint64_to_filetime(tmp);
- Stream_Read_UINT64(s, tmp); /* lastWriteTime (8 bytes) */
- file->ftLastWriteTime = uint64_to_filetime(tmp);
- Stream_Read_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
- Stream_Read_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
- Stream_Read_UTF16_String(s, file->cFileName,
- ARRAYSIZE(file->cFileName)); /* cFileName (520 bytes) */
- }
-
- if (Stream_GetRemainingLength(s) > 0)
- WLog_WARN(TAG, "packed file list has %" PRIuz " excess bytes",
- Stream_GetRemainingLength(s));
-out:
-
- return result;
-}
-
-/**
- * Serialize a packed file list.
- *
- * The resulting format data must be freed with the `free()` function.
- *
- * @param [in] file_descriptor_array array of `FILEDESCRIPTOR` structs to serialize.
- * @param [in] file_descriptor_count number of elements in `file_descriptor_array`.
- * @param [out] format_data serialized CLIPRDR_FILELIST.
- * @param [out] format_data_length length of `format_data` in bytes.
- *
- * @returns 0 on success, otherwise a Win32 error code.
- */
-UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array,
- UINT32 file_descriptor_count, BYTE** format_data,
- UINT32* format_data_length)
-{
- return cliprdr_serialize_file_list_ex(CB_STREAM_FILECLIP_ENABLED, file_descriptor_array,
- file_descriptor_count, format_data, format_data_length);
-}
-
-UINT cliprdr_serialize_file_list_ex(UINT32 flags, const FILEDESCRIPTORW* file_descriptor_array,
- UINT32 file_descriptor_count, BYTE** format_data,
- UINT32* format_data_length)
-{
- UINT result = NO_ERROR;
- UINT32 i;
- size_t len;
- wStream* s = NULL;
-
- if (!file_descriptor_array || !format_data || !format_data_length)
- return ERROR_BAD_ARGUMENTS;
-
- if ((flags & CB_STREAM_FILECLIP_ENABLED) == 0)
- {
- WLog_WARN(TAG, "No file clipboard support annouonced!");
- return ERROR_BAD_ARGUMENTS;
- }
-
- s = Stream_New(NULL, 4 + file_descriptor_count * CLIPRDR_FILEDESCRIPTOR_SIZE);
- if (!s)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- Stream_Write_UINT32(s, file_descriptor_count); /* cItems (4 bytes) */
-
- for (i = 0; i < file_descriptor_count; i++)
- {
- int c;
- UINT64 lastWriteTime;
- const FILEDESCRIPTORW* file = &file_descriptor_array[i];
-
- /*
- * There is a known issue with Windows server getting stuck in
- * an infinite loop when downloading files that are larger than
- * 2 gigabytes. Do not allow clients to send such file lists.
- *
- * https://support.microsoft.com/en-us/help/2258090
- */
- if ((flags & CB_HUGE_FILE_SUPPORT_ENABLED) == 0)
- {
- if ((file->nFileSizeHigh > 0) || (file->nFileSizeLow >= CLIPRDR_MAX_FILE_SIZE))
- {
- WLog_ERR(TAG, "cliprdr does not support files over 2 GB");
- result = ERROR_FILE_TOO_LARGE;
- goto error;
- }
- }
-
- Stream_Write_UINT32(s, file->dwFlags); /* flags (4 bytes) */
- Stream_Zero(s, 32); /* reserved1 (32 bytes) */
- Stream_Write_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
- Stream_Zero(s, 16); /* reserved2 (16 bytes) */
- lastWriteTime = filetime_to_uint64(file->ftLastWriteTime);
- Stream_Write_UINT64(s, lastWriteTime); /* lastWriteTime (8 bytes) */
- Stream_Write_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
- Stream_Write_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
- for (c = 0; c < 260; c++) /* cFileName (520 bytes) */
- Stream_Write_UINT16(s, file->cFileName[c]);
- }
-
- Stream_SealLength(s);
-
- Stream_GetBuffer(s, *format_data);
- Stream_GetLength(s, len);
- if (len > UINT32_MAX)
- goto error;
-
- *format_data_length = (UINT32)len;
-
- Stream_Free(s, FALSE);
-
- return result;
-
-error:
- Stream_Free(s, TRUE);
-
- return result;
-}
--
2.46.1

View File

@ -1,36 +0,0 @@
From 563a19a683d6e0e7b1218e4558a0fb75333b5b34 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 28 Apr 2026 04:14:23 +0000
Subject: [PATCH] [allocations] fix growth of preallocated buffers
Partial backport of commit 118afc0b954ba9d5632b7836ad24e454555ed113.
Only the `Stream_EnsureCapacity` fix is included. Other changes from
the upstream commit (`sizeof(WCHAR)` replacements, collection growth
patterns, environment block handling) are unrelated to CVE-2026-27951.
Made-with: Cursor
---
winpr/libwinpr/utils/stream.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/winpr/libwinpr/utils/stream.c b/winpr/libwinpr/utils/stream.c
index cc119c7..cb145b0 100644
--- a/winpr/libwinpr/utils/stream.c
+++ b/winpr/libwinpr/utils/stream.c
@@ -40,8 +40,10 @@ BOOL Stream_EnsureCapacity(wStream* s, size_t size)
do
{
- new_capacity *= 2;
- } while (new_capacity < size);
+ if (new_capacity > SIZE_MAX - 128ull)
+ return FALSE;
+ new_capacity += 128ull;
+ } while (new_capacity <= size);
position = Stream_GetPosition(s);
--
2.53.0

View File

@ -1,52 +0,0 @@
From 23320a6d5f2e1c8a9b7d6f4e3c2a1b0987654321 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 28 Apr 2026 04:25:58 +0000
Subject: [PATCH] [cache,bitmap] initialize overallocated bitmap cache extra
slot
Backport of commit 8270e0bb3d6726c947d57c93ba9caa92a052b557.
Adjusted hunk offsets for 2.11.7.
Made-with: Cursor
---
libfreerdp/cache/bitmap.c | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/libfreerdp/cache/bitmap.c b/libfreerdp/cache/bitmap.c
index b8a4f21..23320a6 100644
--- a/libfreerdp/cache/bitmap.c
+++ b/libfreerdp/cache/bitmap.c
@@ -303,6 +303,19 @@ rdpBitmapCache* bitmap_cache_new(rdpSettings* settings)
cell->number = nr;
}
+ /* initialize the overallocated extra slot for old RDP servers that send
+ * cacheId == maxCells; use a minimal allocation since no protocol-negotiated
+ * capacity exists for this slot */
+ {
+ BITMAP_V2_CELL* extra = &bitmapCache->cells[bitmapCache->maxCells];
+ /* allocate an extra entry for BITMAP_CACHE_WAITING_LIST_INDEX */
+ extra->entries = (rdpBitmap**)calloc(1, sizeof(rdpBitmap*));
+
+ if (!extra->entries)
+ goto fail;
+ extra->number = 0;
+ }
+
return bitmapCache;
fail:
@@ -315,7 +328,8 @@ void bitmap_cache_free(rdpBitmapCache* bitmapCache)
if (bitmapCache)
{
UINT32 i;
- for (i = 0; i < bitmapCache->maxCells; i++)
+ /* iterate through maxCells + 1 to also free the overallocated extra slot */
+ for (i = 0; i <= bitmapCache->maxCells; i++)
{
UINT32 j;
BITMAP_V2_CELL* cell = &bitmapCache->cells[i];
--
2.53.0

View File

@ -1,33 +0,0 @@
From 608c5d40f6ab4cabd4d5793b2d641f401e146233 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 28 Apr 2026 04:25:52 +0000
Subject: [PATCH] [cache,bitmap] overallocate bitmap cache
Backport of commit ffad58fd2b329efd81a3239e9d7e3c927b8e503f.
Adjusted hunk offsets for 2.11.7.
Made-with: Cursor
---
libfreerdp/cache/bitmap.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/libfreerdp/cache/bitmap.c b/libfreerdp/cache/bitmap.c
index 0ce2599..b8a4f21 100644
--- a/libfreerdp/cache/bitmap.c
+++ b/libfreerdp/cache/bitmap.c
@@ -281,8 +281,10 @@ rdpBitmapCache* bitmap_cache_new(rdpSettings* settings)
bitmapCache->settings = settings;
bitmapCache->update = ((freerdp*)settings->instance)->update;
bitmapCache->context = bitmapCache->update->context;
+
+ /* overallocate by 1. older RDP servers do send a off by 1 cache index. */
bitmapCache->cells =
- (BITMAP_V2_CELL*)calloc(settings->BitmapCacheV2NumCells, sizeof(BITMAP_V2_CELL));
+ (BITMAP_V2_CELL*)calloc(settings->BitmapCacheV2NumCells + 1ull, sizeof(BITMAP_V2_CELL));
if (!bitmapCache->cells)
goto fail;
--
2.53.0

View File

@ -1,37 +0,0 @@
From 97e9d90528690f6973398c9d7e4f8da52145f669 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Sun, 12 Jul 2026 17:37:48 +0200
Subject: [PATCH] [channels,audin] limit FramesPerPacket
---
channels/audin/client/audin_main.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/channels/audin/client/audin_main.c b/channels/audin/client/audin_main.c
index 332aef9da..186e356cd 100644
--- a/channels/audin/client/audin_main.c
+++ b/channels/audin/client/audin_main.c
@@ -488,7 +488,6 @@ static UINT audin_process_open(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* cal
Stream_Read_UINT32(s, initialFormat);
WLog_Print(audin->log, WLOG_DEBUG, "FramesPerPacket=%" PRIu32 " initialFormat=%" PRIu32 "",
FramesPerPacket, initialFormat);
- audin->FramesPerPacket = FramesPerPacket;
if (initialFormat >= callback->formats_count)
{
@@ -496,6 +496,15 @@ static UINT audin_process_open(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* cal
return ERROR_INVALID_DATA;
}
+ /* The RDP protocol field allows UINT32_MAX, but most backend API use INT32 as input parameter
+ * or do some math with it, so ensure that the value does not exceed reasonable limits */
+ if (FramesPerPacket >= INT32_MAX)
+ {
+ WLog_Print(audin->log, WLOG_ERROR, "invalid frames per packet %" PRIu32, FramesPerPacket);
+ return ERROR_INVALID_DATA;
+ }
+
+ audin->FramesPerPacket = FramesPerPacket;
audin->format = &callback->formats[initialFormat];
if (!audin_open_device(audin, callback))

View File

@ -1,65 +0,0 @@
From 20a3489c23763f94d31dcf8fd7ed880e07cbb2e8 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Thu, 2 Jul 2026 10:21:54 +0200
Subject: [PATCH] [client,common] deactivate cli parsing in RDP files
This feature was uncodumented and might have unintended side effects.
Deactivate it by default and (for the time being) keep an option to
enable it at compile time.
---
client/common/CMakeLists.txt | 5 +++++
client/common/file.c | 4 ++++
2 files changed, 9 insertions(+)
diff --git a/client/common/CMakeLists.txt b/client/common/CMakeLists.txt
index b465a6311..7bc7cf083 100644
--- a/client/common/CMakeLists.txt
+++ b/client/common/CMakeLists.txt
@@ -85,6 +85,11 @@ if (WITH_DEBUG_SYMBOLS AND MSVC AND BUILD_SHARED_LIBS)
install(FILES ${CMAKE_PDB_BINARY_DIR}/${OUTPUT_FILENAME}.pdb DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT symbols)
endif()
+option(WITH_EMBEDDED_CLI_IN_RDP_FILES "[dangrous] allow embedded cli arguments in rdp files" OFF)
+if(WITH_EMBEDDED_CLI_IN_RDP_FILES)
+ add_compile_definitions(WITH_EMBEDDED_CLI_IN_RDP_FILES)
+endif()
+
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Client/Common")
if(BUILD_TESTING)
diff --git a/client/common/file.c b/client/common/file.c
index b56fe6f06..bff94bad0 100644
--- a/client/common/file.c
+++ b/client/common/file.c
@@ -26,6 +26,7 @@
#include <winpr/file.h>
+
#include <freerdp/client/file.h>
#include <freerdp/client/cmdline.h>
@@ -661,6 +662,7 @@ static BOOL trim_strings(rdpFile* file)
return FALSE;
if (!trim(&file->KdcProxyName))
return FALSE;
+
return TRUE;
}
@@ -721,6 +723,7 @@ BOOL freerdp_client_parse_rdp_file_buffer_ex(rdpFile* file, const BYTE* buffer,
if (freerdp_client_parse_rdp_file_add_line(file, line, index) == -1)
goto fail;
+#if defined(WITH_EMBEDDED_CLI_IN_RDP_FILES)
if (beg[0] == '/')
{
if (!freerdp_client_parse_rdp_file_option(file, line, index))
@@ -728,6 +731,7 @@ BOOL freerdp_client_parse_rdp_file_buffer_ex(rdpFile* file, const BYTE* buffer,
goto next_line; /* FreeRDP option */
}
+#endif
d1 = strchr(line, ':');

View File

@ -1,170 +0,0 @@
From a25a6b8c53602d2023dd0ad685000dc006179e94 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Mon, 27 Apr 2026 20:12:42 +0000
Subject: [PATCH] [client,x11] fix deadlock on output expose
Defer screen update from xf_event_Expose to after X11 lock is released,
preventing a deadlock between X11 lock and railWindows lock.
Backport of commit a278ff74117444c635c50ffa5084ecf517171f5a.
Adjusted hunk offsets for 2.11.7.
Made-with: Cursor
---
client/X11/xf_client.c | 3 ++
client/X11/xf_event.c | 77 ++++++++++++++++++++++++++++--------------
client/X11/xf_event.h | 2 ++
client/X11/xfreerdp.h | 4 +++
4 files changed, 60 insertions(+), 26 deletions(-)
diff --git a/client/X11/xf_client.c b/client/X11/xf_client.c
index bd3eb0d..f14cb55 100644
--- a/client/X11/xf_client.c
+++ b/client/X11/xf_client.c
@@ -516,6 +516,9 @@ static BOOL xf_process_x_events(freerdp* instance)
xf_unlock_x11(xfc);
if (!status)
break;
+ status = xf_event_update_screen(instance);
+ if (!status)
+ break;
}
return status;
diff --git a/client/X11/xf_event.c b/client/X11/xf_event.c
index e1421ad..524e5e3 100644
--- a/client/X11/xf_event.c
+++ b/client/X11/xf_event.c
@@ -324,45 +324,34 @@ void xf_event_adjust_coordinates(xfContext* xfc, int* x, int* y)
}
static BOOL xf_event_Expose(xfContext* xfc, const XExposeEvent* event, BOOL app)
{
- int x, y;
- int w, h;
+ WINPR_ASSERT(xfc);
+ WINPR_ASSERT(event);
+
rdpSettings* settings = xfc->context.settings;
+ WINPR_ASSERT(settings);
if (!app && (settings->SmartSizing || settings->MultiTouchGestures))
{
- x = 0;
- y = 0;
- w = settings->DesktopWidth;
- h = settings->DesktopHeight;
+ xfc->exposedArea.x = 0;
+ xfc->exposedArea.y = 0;
+ xfc->exposedArea.w = settings->DesktopWidth;
+ xfc->exposedArea.h = settings->DesktopHeight;
}
else
{
- x = event->x;
- y = event->y;
- w = event->width;
- h = event->height;
+ xfc->exposedArea.x = event->x;
+ xfc->exposedArea.y = event->y;
+ xfc->exposedArea.w = event->width;
+ xfc->exposedArea.h = event->height;
}
- if (!app)
- {
- if (xfc->context.gdi->gfx)
- {
- xf_OutputExpose(xfc, x, y, w, h);
- return TRUE;
- }
- xf_draw_screen(xfc, x, y, w, h);
- }
- else
- {
- xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
- if (appWindow)
- xf_UpdateWindowArea(xfc, appWindow, x, y, w, h);
- xf_rail_return_window(appWindow, FALSE);
- }
+ xfc->exposedWindow = event->window;
+ xfc->exposeRequested = TRUE;
return TRUE;
}
+
static BOOL xf_event_VisibilityNotify(xfContext* xfc, const XVisibilityEvent* event, BOOL app)
{
WINPR_UNUSED(app);
@@ -1180,3 +1169,39 @@ BOOL xf_event_process(freerdp* instance, const XEvent* event)
XSync(xfc->display, FALSE);
return status;
}
+
+BOOL xf_event_update_screen(freerdp* instance)
+{
+ WINPR_ASSERT(instance);
+
+ xfContext* xfc = (xfContext*)instance->context;
+ WINPR_ASSERT(xfc);
+
+ rdpSettings* settings = xfc->context.settings;
+ WINPR_ASSERT(settings);
+
+ if (!xfc->exposeRequested)
+ return TRUE;
+ xfc->exposeRequested = FALSE;
+
+ if (!xfc->remote_app)
+ {
+ if (xfc->context.gdi->gfx)
+ {
+ xf_OutputExpose(xfc, xfc->exposedArea.x, xfc->exposedArea.y,
+ xfc->exposedArea.w, xfc->exposedArea.h);
+ return TRUE;
+ }
+ xf_draw_screen(xfc, xfc->exposedArea.x, xfc->exposedArea.y, xfc->exposedArea.w,
+ xfc->exposedArea.h);
+ }
+ else
+ {
+ xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, xfc->exposedWindow);
+ if (appWindow)
+ xf_UpdateWindowArea(xfc, appWindow, xfc->exposedArea.x, xfc->exposedArea.y,
+ xfc->exposedArea.w, xfc->exposedArea.h);
+ xf_rail_return_window(appWindow, FALSE);
+ }
+ return TRUE;
+}
diff --git a/client/X11/xf_event.h b/client/X11/xf_event.h
index 2269d3e..e025d19 100644
--- a/client/X11/xf_event.h
+++ b/client/X11/xf_event.h
@@ -29,6 +29,8 @@ BOOL xf_event_action_script_init(xfContext* xfc);
void xf_event_action_script_free(xfContext* xfc);
BOOL xf_event_process(freerdp* instance, const XEvent* event);
+
+BOOL xf_event_update_screen(freerdp* instance);
void xf_event_SendClientEvent(xfContext* xfc, xfWindow* window, Atom atom, unsigned int numArgs,
...);
diff --git a/client/X11/xfreerdp.h b/client/X11/xfreerdp.h
index 636e60a..a1a33b2 100644
--- a/client/X11/xfreerdp.h
+++ b/client/X11/xfreerdp.h
@@ -261,6 +261,10 @@ struct xf_context
wHashTable* railWindows;
xfRailIconCache* railIconCache;
+ BOOL exposeRequested;
+ GDI_RGN exposedArea;
+ Window exposedWindow;
+
BOOL xkbAvailable;
BOOL xrenderAvailable;
--
2.53.0

View File

@ -1,45 +0,0 @@
From 562cc4b33257074d1d50535138998bc81796d447 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Mon, 27 Apr 2026 19:00:00 +0000
Subject: [PATCH] [client,x11] fix xf_rail_window_common cleanup
Backport of commit b4f0f0a18fe53aa8d47d062f91471f4e9c5e0d51.
Adjusted hunk offsets for 2.11.7.
Made-with: Cursor
---
client/X11/xf_rail.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c
index 242c54b..4c4f47d 100644
--- a/client/X11/xf_rail.c
+++ b/client/X11/xf_rail.c
@@ -260,11 +260,10 @@ static void window_state_log_style(wLog* log, const WINDOW_STATE_ORDER* windowSt
static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
const WINDOW_STATE_ORDER* windowState)
{
- xfAppWindow* appWindow = NULL;
xfContext* xfc = (xfContext*)context;
UINT32 fieldFlags = orderInfo->fieldFlags;
BOOL position_or_size_updated = FALSE;
- appWindow = xf_rail_get_window(xfc, orderInfo->windowId);
+ xfAppWindow* appWindow = xf_rail_get_window(xfc, orderInfo->windowId);
if (fieldFlags & WINDOW_ORDER_STATE_NEW)
{
@@ -310,10 +309,7 @@ static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO*
}
if (!appWindow->title)
- {
- free(appWindow);
return FALSE;
- }
xf_AppWindowInit(xfc, appWindow);
}
--
2.53.0

View File

@ -1,331 +0,0 @@
From e6edabe690ad8de63af327403e8371b6ef1319e2 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Mon, 27 Apr 2026 19:38:47 +0000
Subject: [PATCH] [client,x11] improve rails window locking
Backport of commit 78fd7f580d5f9e6d9d582d82e5ea96003844fbdf.
Adapted for 2.11.7: C89 declaration style, simplified
`xfAppWindowsLockFrom`/`UnlockFrom` signatures (no
`WINPR_ATTR_UNUSED`), `XSetTransientForHint` inline error handling.
Made-with: Cursor
---
client/X11/xf_event.c | 7 ++----
client/X11/xf_graphics.c | 4 +--
client/X11/xf_rail.c | 53 +++++++++++++++++++--------------------
client/X11/xf_rail.h | 10 ++++++--
client/X11/xf_window.c | 54 ++++++++++++++++++++++++++++++++--------
client/X11/xf_window.h | 11 ++++++--
6 files changed, 89 insertions(+), 50 deletions(-)
diff --git a/client/X11/xf_event.c b/client/X11/xf_event.c
index 8bebcd4..8801fbd 100644
--- a/client/X11/xf_event.c
+++ b/client/X11/xf_event.c
@@ -354,13 +354,10 @@ static BOOL xf_event_Expose(xfContext* xfc, const XExposeEvent* event, BOOL app)
}
else
{
- xfAppWindow* appWindow;
- appWindow = xf_AppWindowFromX11Window(xfc, event->window);
-
+ xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
if (appWindow)
- {
xf_UpdateWindowArea(xfc, appWindow, x, y, w, h);
- }
+ xf_rail_return_window(appWindow);
}
return TRUE;
diff --git a/client/X11/xf_graphics.c b/client/X11/xf_graphics.c
index 70979bd..f5b4f3d 100644
--- a/client/X11/xf_graphics.c
+++ b/client/X11/xf_graphics.c
@@ -372,12 +372,12 @@ static Window xf_Pointer_get_window(xfContext* xfc)
if (xfc->remote_app)
{
Window w = 0;
- EnterCriticalSection(&xfc->railWindows->lock);
+ xf_AppWindowsLock(xfc);
if (!xfc->appWindow)
WLog_WARN(TAG, "xf_Pointer: Invalid appWindow");
else
w = xfc->appWindow->handle;
- LeaveCriticalSection(&xfc->railWindows->lock);
+ xf_AppWindowsUnlock(xfc);
return w;
}
else
diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c
index 1938bdd..1e57046 100644
--- a/client/X11/xf_rail.c
+++ b/client/X11/xf_rail.c
@@ -101,9 +101,10 @@ void xf_rail_send_activate(xfContext* xfc, Window xwindow, BOOL enabled)
xf_SetWindowStyle(xfc, appWindow, 0, 0);
activate.windowId = appWindow->windowId;
+ xf_rail_return_window(appWindow);
+
activate.enabled = enabled;
xfc->rail->ClientActivate(xfc->rail, &activate);
- xf_rail_return_window(appWindow);
}
void xf_rail_send_client_system_command(xfContext* xfc, UINT32 windowId, UINT16 command)
@@ -260,6 +261,7 @@ void xf_rail_paint(xfContext* xfc, INT32 uleft, INT32 utop, UINT32 uright, UINT3
static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
const WINDOW_STATE_ORDER* windowState)
{
+ BOOL rc = FALSE;
xfContext* xfc = (xfContext*)context;
UINT32 fieldFlags = orderInfo->fieldFlags;
BOOL position_or_size_updated = FALSE;
@@ -379,14 +381,14 @@ static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO*
if (!(title = _strdup("")))
{
WLog_ERR(TAG, "failed to duplicate empty window title string");
- return FALSE;
+ goto fail;
}
}
else if (ConvertFromUnicode(CP_UTF8, 0, (WCHAR*)windowState->titleInfo.string,
windowState->titleInfo.length / 2, &title, 0, NULL, NULL) < 1)
{
WLog_ERR(TAG, "failed to convert window title");
- return FALSE;
+ goto fail;
}
free(appWindow->title);
@@ -427,7 +429,7 @@ static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO*
(RECTANGLE_16*)calloc(appWindow->numWindowRects, sizeof(RECTANGLE_16));
if (!appWindow->windowRects)
- return FALSE;
+ goto fail;
CopyMemory(appWindow->windowRects, windowState->windowRects,
appWindow->numWindowRects * sizeof(RECTANGLE_16));
@@ -456,7 +458,7 @@ static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO*
(RECTANGLE_16*)calloc(appWindow->numVisibilityRects, sizeof(RECTANGLE_16));
if (!appWindow->visibilityRects)
- return FALSE;
+ goto fail;
CopyMemory(appWindow->visibilityRects, windowState->visibilityRects,
appWindow->numVisibilityRects * sizeof(RECTANGLE_16));
@@ -522,7 +524,10 @@ static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO*
{
xf_SetWindowRects(xfc, appWindow, appWindow->windowRects, appWindow->numWindowRects);
}*/
- return TRUE;
+ rc = TRUE;
+fail:
+ xf_rail_return_window(appWindow);
+ return rc;
}
static BOOL xf_rail_window_delete(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo)
@@ -1000,14 +1005,14 @@ static UINT xf_rail_server_local_move_size(RailClientContext* context,
x = localMoveSize->posX;
y = localMoveSize->posY;
/* FIXME: local keyboard moves not working */
- return CHANNEL_RC_OK;
+ break;
case RAIL_WMSZ_KEYSIZE:
direction = _NET_WM_MOVERESIZE_SIZE_KEYBOARD;
x = localMoveSize->posX;
y = localMoveSize->posY;
/* FIXME: local keyboard moves not working */
- return CHANNEL_RC_OK;
+ break;
}
if (localMoveSize->isMoveSizeStart)
@@ -1173,9 +1178,17 @@ xfAppWindow* xf_rail_add_window(xfContext* xfc, UINT64 id, UINT32 x, UINT32 y, U
appWindow->y = y;
appWindow->width = width;
appWindow->height = height;
- xf_AppWindowCreate(xfc, appWindow);
- HashTable_Add(xfc->railWindows, &appWindow->windowId, (void*)appWindow);
+ xf_AppWindowsLock(xfc);
+ if (xf_AppWindowCreate(xfc, appWindow) < 0)
+ goto fail;
+
+ if (HashTable_Add(xfc->railWindows, &appWindow->windowId, (void*)appWindow) < 0)
+ goto fail;
return appWindow;
+fail:
+ free(appWindow);
+ xf_AppWindowsUnlock(xfc);
+ return NULL;
}
BOOL xf_rail_del_window(xfContext* xfc, UINT64 id)
@@ -1189,26 +1202,10 @@ BOOL xf_rail_del_window(xfContext* xfc, UINT64 id)
return HashTable_Remove(xfc->railWindows, &id);
}
-xfAppWindow* xf_rail_get_window(xfContext* xfc, UINT64 id)
-{
- if (!xfc)
- return NULL;
-
- if (!xfc->railWindows)
- return NULL;
-
- EnterCriticalSection(&xfc->railWindows->lock);
- xfAppWindow* window = HashTable_GetItemValue(xfc->railWindows, &id);
- if (!window)
- LeaveCriticalSection(&xfc->railWindows->lock);
-
- return window;
-}
-
-void xf_rail_return_window(xfAppWindow* window)
+void xf_rail_return_windowFrom(xfAppWindow* window, const char* file, const char* fkt, size_t line)
{
if (!window)
return;
- LeaveCriticalSection(&window->xfc->railWindows->lock);
+ xfAppWindowsUnlockFrom(window->xfc, file, fkt, line);
}
diff --git a/client/X11/xf_rail.h b/client/X11/xf_rail.h
index 5fbc316..83d5fff 100644
--- a/client/X11/xf_rail.h
+++ b/client/X11/xf_rail.h
@@ -36,9 +36,15 @@ void xf_rail_disable_remoteapp_mode(xfContext* xfc);
xfAppWindow* xf_rail_add_window(xfContext* xfc, UINT64 id, UINT32 x, UINT32 y, UINT32 width,
UINT32 height, UINT32 surfaceId);
-void xf_rail_return_window(xfAppWindow* window);
+#define xf_rail_return_window(window) \
+ xf_rail_return_windowFrom((window), __FILE__, __func__, __LINE__)
+void xf_rail_return_windowFrom(xfAppWindow* window, const char* file, const char* fkt, size_t line);
-xfAppWindow* xf_rail_get_window(xfContext* xfc, UINT64 id);
+#define xf_rail_get_window(xfc, id) \
+ xf_rail_get_windowFrom((xfc), (id), __FILE__, __func__, __LINE__)
+
+xfAppWindow* xf_rail_get_windowFrom(xfContext* xfc, UINT64 id, const char* file, const char* fkt,
+ size_t line);
BOOL xf_rail_del_window(xfContext* xfc, UINT64 id);
diff --git a/client/X11/xf_window.c b/client/X11/xf_window.c
index 7e936fe..09b4a81 100644
--- a/client/X11/xf_window.c
+++ b/client/X11/xf_window.c
@@ -1116,39 +1116,71 @@ void xf_DestroyWindow(xfContext* xfc, xfAppWindow* appWindow)
free(appWindow);
}
-xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd)
+static xfAppWindow* get_windowUnlocked(xfContext* xfc, UINT64 id)
+{
+ WINPR_ASSERT(xfc);
+ return HashTable_GetItemValue(xfc->railWindows, &id);
+}
+
+xfAppWindow* xf_rail_get_windowFrom(xfContext* xfc, UINT64 id, const char* file, const char* fkt,
+ size_t line)
+{
+ if (!xfc)
+ return NULL;
+
+ if (!xfc->railWindows)
+ return NULL;
+
+ xfAppWindowsLockFrom(xfc, file, fkt, line);
+ xfAppWindow* window = get_windowUnlocked(xfc, id);
+ if (!window)
+ xfAppWindowsUnlockFrom(xfc, file, fkt, line);
+
+ return window;
+}
+
+xfAppWindow* xf_AppWindowFromX11WindowFrom(xfContext* xfc, Window wnd, const char* file,
+ const char* fkt, size_t line)
{
- int index;
- int count;
ULONG_PTR* pKeys = NULL;
- xfAppWindow* appWindow;
if (!xfc->railWindows)
return NULL;
- EnterCriticalSection(&xfc->railWindows->lock);
- count = HashTable_GetKeys(xfc->railWindows, &pKeys);
+ xfAppWindowsLockFrom(xfc, file, fkt, line);
+ size_t count = HashTable_GetKeys(xfc->railWindows, &pKeys);
- for (index = 0; index < count; index++)
+ for (size_t index = 0; index < count; index++)
{
- appWindow = HashTable_GetItemValue(xfc->railWindows, (void*)pKeys[index]);
+ xfAppWindow* appWindow = get_windowUnlocked(xfc, *(UINT64*)pKeys[index]);
if (!appWindow)
{
- LeaveCriticalSection(&xfc->railWindows->lock);
+ xfAppWindowsUnlockFrom(xfc, file, fkt, line);
free(pKeys);
return NULL;
}
if (appWindow->handle == wnd)
{
- LeaveCriticalSection(&xfc->railWindows->lock);
free(pKeys);
return appWindow;
}
}
- LeaveCriticalSection(&xfc->railWindows->lock);
+ xfAppWindowsUnlockFrom(xfc, file, fkt, line);
free(pKeys);
return NULL;
}
+
+void xfAppWindowsLockFrom(xfContext* xfc, const char* file, const char* fkt, size_t line)
+{
+ WINPR_ASSERT(xfc);
+ EnterCriticalSection(&xfc->railWindows->lock);
+}
+
+void xfAppWindowsUnlockFrom(xfContext* xfc, const char* file, const char* fkt, size_t line)
+{
+ WINPR_ASSERT(xfc);
+ LeaveCriticalSection(&xfc->railWindows->lock);
+}
diff --git a/client/X11/xf_window.h b/client/X11/xf_window.h
index c7702ed..45d40bb 100644
--- a/client/X11/xf_window.h
+++ b/client/X11/xf_window.h
@@ -187,8 +187,15 @@ void xf_SetWindowMinMaxInfo(xfContext* xfc, xfAppWindow* appWindow, int maxWidth
int maxTrackWidth, int maxTrackHeight);
void xf_StartLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow, int direction, int x, int y);
void xf_EndLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow);
-void xf_rail_return_window(xfAppWindow* window);
+#define xf_AppWindowFromX11Window(xfc, wnd) \
+ xf_AppWindowFromX11WindowFrom((xfc), (wnd), __FILE__, __func__, __LINE__)
+xfAppWindow* xf_AppWindowFromX11WindowFrom(xfContext* xfc, Window wnd, const char* file,
+ const char* fkt, size_t line);
-xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd);
+#define xf_AppWindowsLock(xfc) xfAppWindowsLockFrom((xfc), __FILE__, __func__, __LINE__)
+void xfAppWindowsLockFrom(xfContext* xfc, const char* file, const char* fkt, size_t line);
+
+#define xf_AppWindowsUnlock(xfc) xfAppWindowsUnlockFrom((xfc), __FILE__, __func__, __LINE__)
+void xfAppWindowsUnlockFrom(xfContext* xfc, const char* file, const char* fkt, size_t line);
#endif /* FREERDP_CLIENT_X11_WINDOW_H */
--
2.53.0

View File

@ -1,404 +0,0 @@
From 8152c347e96c1e3af15cb2551d4efb6740fe9f87 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Mon, 27 Apr 2026 19:37:40 +0000
Subject: [PATCH] [client,x11] lock appWindow
Backport of commit 1994e9844212a6dfe0ff12309fef520e888986b5.
Adapted for 2.11.7: C89 declaration style, `(Atom)` cast for
`xfc->_NET_WM_STATE` comparison, `xf_rail_send_client_system_command`
return value handling adapted (no direct return), `HashTable_GetKeys`
variable pre-declared, `xf_AppWindowFromX11Window` loop adapted.
Made-with: Cursor
---
client/X11/xf_event.c | 46 ++++++++++++++++-----------
client/X11/xf_graphics.c | 10 +++---
client/X11/xf_rail.c | 68 +++++++++++++++++++++++++---------------
client/X11/xf_rail.h | 3 ++
client/X11/xf_window.c | 13 +++++++-
client/X11/xf_window.h | 2 ++
6 files changed, 94 insertions(+), 48 deletions(-)
diff --git a/client/X11/xf_event.c b/client/X11/xf_event.c
index eb50ef1..8bebcd4 100644
--- a/client/X11/xf_event.c
+++ b/client/X11/xf_event.c
@@ -388,7 +388,9 @@ BOOL xf_generic_MotionNotify(xfContext* xfc, int x, int y, int state, Window win
if (app)
{
/* make sure window exists */
- if (!xf_AppWindowFromX11Window(xfc, window))
+ xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, window);
+ xf_rail_return_window(appWindow);
+ if (!appWindow)
return TRUE;
/* Translate to desktop coordinates */
@@ -465,7 +467,9 @@ BOOL xf_generic_ButtonEvent(xfContext* xfc, int x, int y, int button, Window win
if (app)
{
/* make sure window exists */
- if (!xf_AppWindowFromX11Window(xfc, window))
+ xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, window);
+ xf_rail_return_window(appWindow);
+ if (!appWindow)
return TRUE;
/* Translate to desktop coordinates */
@@ -574,9 +578,8 @@ static BOOL xf_event_FocusIn(xfContext* xfc, const XFocusInEvent* event, BOOL ap
/* Update the server with any window changes that occurred while the window was not focused.
*/
if (appWindow)
- {
xf_rail_adjust_position(xfc, appWindow);
- }
+ xf_rail_return_window(appWindow);
}
xf_keyboard_focus_in(xfc);
@@ -623,15 +626,13 @@ static BOOL xf_event_ClientMessage(xfContext* xfc, const XClientMessageEvent* ev
{
if (app)
{
- xfAppWindow* appWindow;
- appWindow = xf_AppWindowFromX11Window(xfc, event->window);
+ BOOL rc = TRUE;
+ xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
if (appWindow)
- {
xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_CLOSE);
- }
-
- return TRUE;
+ xf_rail_return_window(appWindow);
+ return rc;
}
else
{
@@ -665,6 +666,7 @@ static BOOL xf_event_EnterNotify(xfContext* xfc, const XEnterWindowEvent* event,
/* keep track of which window has focus so that we can apply pointer updates */
xfc->appWindow = appWindow;
+ xf_rail_return_window(appWindow);
}
return TRUE;
@@ -684,6 +686,7 @@ static BOOL xf_event_LeaveNotify(xfContext* xfc, const XLeaveWindowEvent* event,
/* keep track of which window has focus so that we can apply pointer updates */
if (xfc->appWindow == appWindow)
xfc->appWindow = NULL;
+ xf_rail_return_window(appWindow);
}
return TRUE;
}
@@ -774,6 +777,7 @@ static BOOL xf_event_ConfigureNotify(xfContext* xfc, const XConfigureEvent* even
xf_rail_adjust_position(xfc, appWindow);
}
}
+ xf_rail_return_window(appWindow);
}
return xf_pointer_update_scale(xfc);
}
@@ -798,6 +802,7 @@ static BOOL xf_event_MapNotify(xfContext* xfc, const XMapEvent* event, BOOL app)
// xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE);
appWindow->is_mapped = TRUE;
}
+ xf_rail_return_window(appWindow);
}
return TRUE;
@@ -813,15 +818,14 @@ static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL
xf_keyboard_release_all_keypress(xfc);
if (!app)
- gdi_send_suppress_output(xfc->context.gdi, TRUE);
- else
+ return gdi_send_suppress_output(xfc->context.gdi, TRUE);
+
{
appWindow = xf_AppWindowFromX11Window(xfc, event->window);
if (appWindow)
- {
appWindow->is_mapped = FALSE;
- }
+ xf_rail_return_window(appWindow);
}
return TRUE;
@@ -829,6 +833,7 @@ static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL
static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, BOOL app)
{
+ BOOL rc = TRUE;
WINPR_ASSERT(xfc);
WINPR_ASSERT(event);
@@ -856,7 +861,7 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event,
appWindow = xf_AppWindowFromX11Window(xfc, event->window);
if (!appWindow)
- return TRUE;
+ goto fail;
}
if ((Atom)event->atom == xfc->_NET_WM_STATE)
@@ -949,10 +954,13 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event,
}
}
else if (minimizedChanged)
- gdi_send_suppress_output(xfc->context.gdi, minimized);
+ rc = gdi_send_suppress_output(xfc->context.gdi, minimized);
+
+ fail:
+ xf_rail_return_window(appWindow);
}
- return TRUE;
+ return rc;
}
static BOOL xf_event_suppress_events(xfContext* xfc, xfAppWindow* appWindow, const XEvent* event)
@@ -1060,7 +1068,9 @@ BOOL xf_event_process(freerdp* instance, const XEvent* event)
/* Update "current" window for cursor change orders */
xfc->appWindow = appWindow;
- if (xf_event_suppress_events(xfc, appWindow, event))
+ BOOL suppress = xf_event_suppress_events(xfc, appWindow, event);
+ xf_rail_return_window(appWindow);
+ if (suppress)
return TRUE;
}
}
diff --git a/client/X11/xf_graphics.c b/client/X11/xf_graphics.c
index d596b23..70979bd 100644
--- a/client/X11/xf_graphics.c
+++ b/client/X11/xf_graphics.c
@@ -371,12 +371,14 @@ static Window xf_Pointer_get_window(xfContext* xfc)
}
if (xfc->remote_app)
{
+ Window w = 0;
+ EnterCriticalSection(&xfc->railWindows->lock);
if (!xfc->appWindow)
- {
WLog_WARN(TAG, "xf_Pointer: Invalid appWindow");
- return 0;
- }
- return xfc->appWindow->handle;
+ else
+ w = xfc->appWindow->handle;
+ LeaveCriticalSection(&xfc->railWindows->lock);
+ return w;
}
else
{
diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c
index fdc2244..1938bdd 100644
--- a/client/X11/xf_rail.c
+++ b/client/X11/xf_rail.c
@@ -103,6 +103,7 @@ void xf_rail_send_activate(xfContext* xfc, Window xwindow, BOOL enabled)
activate.windowId = appWindow->windowId;
activate.enabled = enabled;
xfc->rail->ClientActivate(xfc->rail, &activate);
+ xf_rail_return_window(appWindow);
}
void xf_rail_send_client_system_command(xfContext* xfc, UINT32 windowId, UINT16 command)
@@ -658,61 +659,63 @@ static void xf_rail_set_window_icon(xfContext* xfc, xfAppWindow* railWindow, xfR
static BOOL xf_rail_window_icon(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
const WINDOW_ICON_ORDER* windowIcon)
{
+ BOOL rc = FALSE;
xfContext* xfc = (xfContext*)context;
- xfAppWindow* railWindow;
- xfRailIcon* icon;
- BOOL replaceIcon;
- railWindow = xf_rail_get_window(xfc, orderInfo->windowId);
+ BOOL replaceIcon = 0;
+ xfAppWindow* railWindow = xf_rail_get_window(xfc, orderInfo->windowId);
if (!railWindow)
return TRUE;
- icon = RailIconCache_Lookup(xfc->railIconCache, windowIcon->iconInfo->cacheId,
+ xfRailIcon* icon = RailIconCache_Lookup(xfc->railIconCache, windowIcon->iconInfo->cacheId,
windowIcon->iconInfo->cacheEntry);
if (!icon)
{
WLog_WARN(TAG, "failed to get icon from cache %02X:%04X", windowIcon->iconInfo->cacheId,
windowIcon->iconInfo->cacheEntry);
- return FALSE;
}
-
- if (!convert_rail_icon(windowIcon->iconInfo, icon))
+ else if (!convert_rail_icon(windowIcon->iconInfo, icon))
{
WLog_WARN(TAG, "failed to convert icon for window %08X", orderInfo->windowId);
- return FALSE;
}
-
- replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW);
- xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon);
- return TRUE;
+ else
+ {
+ replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW);
+ xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon);
+ rc = TRUE;
+ }
+ xf_rail_return_window(railWindow);
+ return rc;
}
static BOOL xf_rail_window_cached_icon(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
const WINDOW_CACHED_ICON_ORDER* windowCachedIcon)
{
+ BOOL rc = FALSE;
xfContext* xfc = (xfContext*)context;
- xfAppWindow* railWindow;
- xfRailIcon* icon;
- BOOL replaceIcon;
- railWindow = xf_rail_get_window(xfc, orderInfo->windowId);
+ BOOL replaceIcon = 0;
+ xfAppWindow* railWindow = xf_rail_get_window(xfc, orderInfo->windowId);
if (!railWindow)
return TRUE;
- icon = RailIconCache_Lookup(xfc->railIconCache, windowCachedIcon->cachedIcon.cacheId,
+ xfRailIcon* icon = RailIconCache_Lookup(xfc->railIconCache, windowCachedIcon->cachedIcon.cacheId,
windowCachedIcon->cachedIcon.cacheEntry);
if (!icon)
{
WLog_WARN(TAG, "failed to get icon from cache %02X:%04X",
windowCachedIcon->cachedIcon.cacheId, windowCachedIcon->cachedIcon.cacheEntry);
- return FALSE;
}
-
- replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW);
- xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon);
- return TRUE;
+ else
+ {
+ replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW);
+ xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon);
+ rc = TRUE;
+ }
+ xf_rail_return_window(railWindow);
+ return rc;
}
static BOOL xf_rail_notify_icon_common(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
@@ -1012,6 +1015,7 @@ static UINT xf_rail_server_local_move_size(RailClientContext* context,
else
xf_EndLocalMoveSize(xfc, appWindow);
+ xf_rail_return_window(appWindow);
return CHANNEL_RC_OK;
}
@@ -1033,6 +1037,7 @@ static UINT xf_rail_server_min_max_info(RailClientContext* context,
minMaxInfo->minTrackHeight, minMaxInfo->maxTrackWidth,
minMaxInfo->maxTrackHeight);
}
+ xf_rail_return_window(appWindow);
return CHANNEL_RC_OK;
}
@@ -1190,7 +1195,20 @@ xfAppWindow* xf_rail_get_window(xfContext* xfc, UINT64 id)
return NULL;
if (!xfc->railWindows)
- return FALSE;
+ return NULL;
+
+ EnterCriticalSection(&xfc->railWindows->lock);
+ xfAppWindow* window = HashTable_GetItemValue(xfc->railWindows, &id);
+ if (!window)
+ LeaveCriticalSection(&xfc->railWindows->lock);
+
+ return window;
+}
+
+void xf_rail_return_window(xfAppWindow* window)
+{
+ if (!window)
+ return;
- return HashTable_GetItemValue(xfc->railWindows, &id);
+ LeaveCriticalSection(&window->xfc->railWindows->lock);
}
diff --git a/client/X11/xf_rail.h b/client/X11/xf_rail.h
index c99ed70..5fbc316 100644
--- a/client/X11/xf_rail.h
+++ b/client/X11/xf_rail.h
@@ -35,6 +35,9 @@ void xf_rail_disable_remoteapp_mode(xfContext* xfc);
xfAppWindow* xf_rail_add_window(xfContext* xfc, UINT64 id, UINT32 x, UINT32 y, UINT32 width,
UINT32 height, UINT32 surfaceId);
+
+void xf_rail_return_window(xfAppWindow* window);
+
xfAppWindow* xf_rail_get_window(xfContext* xfc, UINT64 id);
BOOL xf_rail_del_window(xfContext* xfc, UINT64 id);
diff --git a/client/X11/xf_window.c b/client/X11/xf_window.c
index 9b5b1c4..7e936fe 100644
--- a/client/X11/xf_window.c
+++ b/client/X11/xf_window.c
@@ -1122,22 +1122,33 @@ xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd)
int count;
ULONG_PTR* pKeys = NULL;
xfAppWindow* appWindow;
+
+ if (!xfc->railWindows)
+ return NULL;
+
+ EnterCriticalSection(&xfc->railWindows->lock);
count = HashTable_GetKeys(xfc->railWindows, &pKeys);
for (index = 0; index < count; index++)
{
- appWindow = xf_rail_get_window(xfc, *(UINT64*)pKeys[index]);
+ appWindow = HashTable_GetItemValue(xfc->railWindows, (void*)pKeys[index]);
if (!appWindow)
+ {
+ LeaveCriticalSection(&xfc->railWindows->lock);
+ free(pKeys);
return NULL;
+ }
if (appWindow->handle == wnd)
{
+ LeaveCriticalSection(&xfc->railWindows->lock);
free(pKeys);
return appWindow;
}
}
+ LeaveCriticalSection(&xfc->railWindows->lock);
free(pKeys);
return NULL;
}
diff --git a/client/X11/xf_window.h b/client/X11/xf_window.h
index 0f85af1..c7702ed 100644
--- a/client/X11/xf_window.h
+++ b/client/X11/xf_window.h
@@ -187,6 +187,8 @@ void xf_SetWindowMinMaxInfo(xfContext* xfc, xfAppWindow* appWindow, int maxWidth
int maxTrackWidth, int maxTrackHeight);
void xf_StartLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow, int direction, int x, int y);
void xf_EndLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow);
+void xf_rail_return_window(xfAppWindow* window);
+
xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd);
#endif /* FREERDP_CLIENT_X11_WINDOW_H */
--
2.53.0

View File

@ -1,360 +0,0 @@
From eeb38c3565473c46da5dea6b43f597064e89b717 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Mon, 27 Apr 2026 20:12:11 +0000
Subject: [PATCH] [client,x11] refactor locking
X11 and railWindows lock must be held at the same time to avoid
deadlocking.
Backport of commit 4ff57b68c2960fa414d03c78ff0e0660be1cc5bd.
Adapted for 2.11.7: C89 declaration style, direct X11 calls instead
of `LogDynAnd*` wrappers, `WINPR_ATTR_UNUSED` removed from
`xfAppWindowsLockFrom`/`UnlockFrom`.
Made-with: Cursor
---
client/X11/xf_event.c | 24 ++++++++++++------------
client/X11/xf_rail.c | 35 +++++++++++++++++++++--------------
client/X11/xf_rail.h | 14 ++++++++------
client/X11/xf_window.c | 16 +++++++++-------
4 files changed, 50 insertions(+), 39 deletions(-)
diff --git a/client/X11/xf_event.c b/client/X11/xf_event.c
index 8801fbd..e1421ad 100644
--- a/client/X11/xf_event.c
+++ b/client/X11/xf_event.c
@@ -357,7 +357,7 @@ static BOOL xf_event_Expose(xfContext* xfc, const XExposeEvent* event, BOOL app)
xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
if (appWindow)
xf_UpdateWindowArea(xfc, appWindow, x, y, w, h);
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
}
return TRUE;
@@ -386,7 +386,7 @@ BOOL xf_generic_MotionNotify(xfContext* xfc, int x, int y, int state, Window win
{
/* make sure window exists */
xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, window);
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
if (!appWindow)
return TRUE;
@@ -465,7 +465,7 @@ BOOL xf_generic_ButtonEvent(xfContext* xfc, int x, int y, int button, Window win
{
/* make sure window exists */
xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, window);
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
if (!appWindow)
return TRUE;
@@ -576,7 +576,7 @@ static BOOL xf_event_FocusIn(xfContext* xfc, const XFocusInEvent* event, BOOL ap
*/
if (appWindow)
xf_rail_adjust_position(xfc, appWindow);
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
}
xf_keyboard_focus_in(xfc);
@@ -628,7 +628,7 @@ static BOOL xf_event_ClientMessage(xfContext* xfc, const XClientMessageEvent* ev
if (appWindow)
xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_CLOSE);
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
return rc;
}
else
@@ -663,7 +663,7 @@ static BOOL xf_event_EnterNotify(xfContext* xfc, const XEnterWindowEvent* event,
/* keep track of which window has focus so that we can apply pointer updates */
xfc->appWindow = appWindow;
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
}
return TRUE;
@@ -683,7 +683,7 @@ static BOOL xf_event_LeaveNotify(xfContext* xfc, const XLeaveWindowEvent* event,
/* keep track of which window has focus so that we can apply pointer updates */
if (xfc->appWindow == appWindow)
xfc->appWindow = NULL;
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
}
return TRUE;
}
@@ -774,7 +774,7 @@ static BOOL xf_event_ConfigureNotify(xfContext* xfc, const XConfigureEvent* even
xf_rail_adjust_position(xfc, appWindow);
}
}
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
}
return xf_pointer_update_scale(xfc);
}
@@ -799,7 +799,7 @@ static BOOL xf_event_MapNotify(xfContext* xfc, const XMapEvent* event, BOOL app)
// xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE);
appWindow->is_mapped = TRUE;
}
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
}
return TRUE;
@@ -822,7 +822,7 @@ static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL
if (appWindow)
appWindow->is_mapped = FALSE;
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
}
return TRUE;
@@ -954,7 +954,7 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event,
rc = gdi_send_suppress_output(xfc->context.gdi, minimized);
fail:
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
}
return rc;
@@ -1066,7 +1066,7 @@ BOOL xf_event_process(freerdp* instance, const XEvent* event)
xfc->appWindow = appWindow;
BOOL suppress = xf_event_suppress_events(xfc, appWindow, event);
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
if (suppress)
return TRUE;
}
diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c
index 1e57046..d42b864 100644
--- a/client/X11/xf_rail.c
+++ b/client/X11/xf_rail.c
@@ -101,7 +101,7 @@ void xf_rail_send_activate(xfContext* xfc, Window xwindow, BOOL enabled)
xf_SetWindowStyle(xfc, appWindow, 0, 0);
activate.windowId = appWindow->windowId;
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
activate.enabled = enabled;
xfc->rail->ClientActivate(xfc->rail, &activate);
@@ -213,7 +213,7 @@ static void xf_rail_invalidate_region(xfContext* xfc, REGION16* invalidRegion)
for (index = 0; index < count; index++)
{
- appWindow = xf_rail_get_window(xfc, *(UINT64*)pKeys[index]);
+ appWindow = xf_rail_get_window(xfc, *(UINT64*)pKeys[index], FALSE);
if (appWindow)
{
@@ -265,7 +265,7 @@ static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO*
xfContext* xfc = (xfContext*)context;
UINT32 fieldFlags = orderInfo->fieldFlags;
BOOL position_or_size_updated = FALSE;
- xfAppWindow* appWindow = xf_rail_get_window(xfc, orderInfo->windowId);
+ xfAppWindow* appWindow = xf_rail_get_window(xfc, orderInfo->windowId, FALSE);
if (fieldFlags & WINDOW_ORDER_STATE_NEW)
{
@@ -526,7 +526,7 @@ static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO*
}*/
rc = TRUE;
fail:
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
return rc;
}
@@ -667,7 +667,7 @@ static BOOL xf_rail_window_icon(rdpContext* context, const WINDOW_ORDER_INFO* or
BOOL rc = FALSE;
xfContext* xfc = (xfContext*)context;
BOOL replaceIcon = 0;
- xfAppWindow* railWindow = xf_rail_get_window(xfc, orderInfo->windowId);
+ xfAppWindow* railWindow = xf_rail_get_window(xfc, orderInfo->windowId, FALSE);
if (!railWindow)
return TRUE;
@@ -690,7 +690,7 @@ static BOOL xf_rail_window_icon(rdpContext* context, const WINDOW_ORDER_INFO* or
xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon);
rc = TRUE;
}
- xf_rail_return_window(railWindow);
+ xf_rail_return_window(railWindow, FALSE);
return rc;
}
@@ -700,7 +700,7 @@ static BOOL xf_rail_window_cached_icon(rdpContext* context, const WINDOW_ORDER_I
BOOL rc = FALSE;
xfContext* xfc = (xfContext*)context;
BOOL replaceIcon = 0;
- xfAppWindow* railWindow = xf_rail_get_window(xfc, orderInfo->windowId);
+ xfAppWindow* railWindow = xf_rail_get_window(xfc, orderInfo->windowId, FALSE);
if (!railWindow)
return TRUE;
@@ -719,7 +719,7 @@ static BOOL xf_rail_window_cached_icon(rdpContext* context, const WINDOW_ORDER_I
xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon);
rc = TRUE;
}
- xf_rail_return_window(railWindow);
+ xf_rail_return_window(railWindow, FALSE);
return rc;
}
@@ -939,7 +939,7 @@ static UINT xf_rail_server_local_move_size(RailClientContext* context,
int direction = 0;
Window child_window;
xfContext* xfc = (xfContext*)context->custom;
- xfAppWindow* appWindow = xf_rail_get_window(xfc, localMoveSize->windowId);
+ xfAppWindow* appWindow = xf_rail_get_window(xfc, localMoveSize->windowId, FALSE);
if (!appWindow)
return ERROR_INTERNAL_ERROR;
@@ -1020,7 +1020,7 @@ static UINT xf_rail_server_local_move_size(RailClientContext* context,
else
xf_EndLocalMoveSize(xfc, appWindow);
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
return CHANNEL_RC_OK;
}
@@ -1033,7 +1033,7 @@ static UINT xf_rail_server_min_max_info(RailClientContext* context,
const RAIL_MINMAXINFO_ORDER* minMaxInfo)
{
xfContext* xfc = (xfContext*)context->custom;
- xfAppWindow* appWindow = xf_rail_get_window(xfc, minMaxInfo->windowId);
+ xfAppWindow* appWindow = xf_rail_get_window(xfc, minMaxInfo->windowId, FALSE);
if (appWindow)
{
@@ -1042,7 +1042,7 @@ static UINT xf_rail_server_min_max_info(RailClientContext* context,
minMaxInfo->minTrackHeight, minMaxInfo->maxTrackWidth,
minMaxInfo->maxTrackHeight);
}
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
return CHANNEL_RC_OK;
}
@@ -1199,13 +1199,20 @@ BOOL xf_rail_del_window(xfContext* xfc, UINT64 id)
if (!xfc->railWindows)
return FALSE;
- return HashTable_Remove(xfc->railWindows, &id);
+ xf_lock_x11(xfc);
+ const BOOL res = HashTable_Remove(xfc->railWindows, &id);
+ xf_unlock_x11(xfc);
+ return res;
}
-void xf_rail_return_windowFrom(xfAppWindow* window, const char* file, const char* fkt, size_t line)
+void xf_rail_return_windowFrom(xfAppWindow* window, BOOL alreadyLocked, const char* file,
+ const char* fkt, size_t line)
{
if (!window)
return;
+ if (alreadyLocked)
+ return;
+
xfAppWindowsUnlockFrom(window->xfc, file, fkt, line);
}
diff --git a/client/X11/xf_rail.h b/client/X11/xf_rail.h
index 83d5fff..a91c1bf 100644
--- a/client/X11/xf_rail.h
+++ b/client/X11/xf_rail.h
@@ -36,14 +36,16 @@ void xf_rail_disable_remoteapp_mode(xfContext* xfc);
xfAppWindow* xf_rail_add_window(xfContext* xfc, UINT64 id, UINT32 x, UINT32 y, UINT32 width,
UINT32 height, UINT32 surfaceId);
-#define xf_rail_return_window(window) \
- xf_rail_return_windowFrom((window), __FILE__, __func__, __LINE__)
-void xf_rail_return_windowFrom(xfAppWindow* window, const char* file, const char* fkt, size_t line);
+#define xf_rail_return_window(window, alreadyLocked) \
+ xf_rail_return_windowFrom((window), (alreadyLocked), __FILE__, __func__, __LINE__)
+void xf_rail_return_windowFrom(xfAppWindow* window, BOOL alreadyLocked, const char* file,
+ const char* fkt, size_t line);
-#define xf_rail_get_window(xfc, id) \
- xf_rail_get_windowFrom((xfc), (id), __FILE__, __func__, __LINE__)
+#define xf_rail_get_window(xfc, id, alreadyLocked) \
+ xf_rail_get_windowFrom((xfc), (id), (alreadyLocked), __FILE__, __func__, __LINE__)
-xfAppWindow* xf_rail_get_windowFrom(xfContext* xfc, UINT64 id, const char* file, const char* fkt,
+xfAppWindow* xf_rail_get_windowFrom(xfContext* xfc, UINT64 id, BOOL alreadyLocked, const char* file,
+ const char* fkt,
size_t line);
BOOL xf_rail_del_window(xfContext* xfc, UINT64 id);
diff --git a/client/X11/xf_window.c b/client/X11/xf_window.c
index 09b4a81..8d09ced 100644
--- a/client/X11/xf_window.c
+++ b/client/X11/xf_window.c
@@ -1070,8 +1070,6 @@ void xf_UpdateWindowArea(xfContext* xfc, xfAppWindow* appWindow, int x, int y, i
if (ay + height > appWindow->windowOffsetY + appWindow->height)
height = (appWindow->windowOffsetY + appWindow->height - 1) - ay;
- xf_lock_x11(xfc);
-
if (xfc->context.settings->SoftwareGdi)
{
XPutImage(xfc->display, xfc->primary, appWindow->gc, xfc->image, ax, ay, ax, ay, width,
@@ -1081,7 +1079,6 @@ void xf_UpdateWindowArea(xfContext* xfc, xfAppWindow* appWindow, int x, int y, i
XCopyArea(xfc->display, xfc->primary, appWindow->handle, appWindow->gc, ax, ay, width, height,
x, y);
XFlush(xfc->display);
- xf_unlock_x11(xfc);
}
void xf_DestroyWindow(xfContext* xfc, xfAppWindow* appWindow)
@@ -1122,8 +1119,8 @@ static xfAppWindow* get_windowUnlocked(xfContext* xfc, UINT64 id)
return HashTable_GetItemValue(xfc->railWindows, &id);
}
-xfAppWindow* xf_rail_get_windowFrom(xfContext* xfc, UINT64 id, const char* file, const char* fkt,
- size_t line)
+xfAppWindow* xf_rail_get_windowFrom(xfContext* xfc, UINT64 id, BOOL alreadyLocked, const char* file,
+ const char* fkt, size_t line)
{
if (!xfc)
return NULL;
@@ -1131,9 +1128,12 @@ xfAppWindow* xf_rail_get_windowFrom(xfContext* xfc, UINT64 id, const char* file,
if (!xfc->railWindows)
return NULL;
- xfAppWindowsLockFrom(xfc, file, fkt, line);
+ if (!alreadyLocked)
+ xfAppWindowsLockFrom(xfc, file, fkt, line);
+
xfAppWindow* window = get_windowUnlocked(xfc, id);
- if (!window)
+
+ if (!window && !alreadyLocked)
xfAppWindowsUnlockFrom(xfc, file, fkt, line);
return window;
@@ -1176,6 +1176,7 @@ xfAppWindow* xf_AppWindowFromX11WindowFrom(xfContext* xfc, Window wnd, const cha
void xfAppWindowsLockFrom(xfContext* xfc, const char* file, const char* fkt, size_t line)
{
WINPR_ASSERT(xfc);
+ xf_lock_x11(xfc);
EnterCriticalSection(&xfc->railWindows->lock);
}
@@ -1183,4 +1184,5 @@ void xfAppWindowsUnlockFrom(xfContext* xfc, const char* file, const char* fkt, s
{
WINPR_ASSERT(xfc);
LeaveCriticalSection(&xfc->railWindows->lock);
+ xf_unlock_x11(xfc);
}
--
2.53.0

View File

@ -1,35 +0,0 @@
From a2dde6d9832cb032e8cf12cab3da84dafbab9006 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Fri, 10 Apr 2026 11:32:09 +0200
Subject: [PATCH] [codec,clear] update CLEAR_VBAR_ENTRY::size after alloc
Backport of commit a2dde6d9832cb032e8cf12cab3da84dafbab9006.
Made-with: Cursor
---
libfreerdp/codec/clear.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libfreerdp/codec/clear.c b/libfreerdp/codec/clear.c
index e38fa0d..eda30ad 100644
--- a/libfreerdp/codec/clear.c
+++ b/libfreerdp/codec/clear.c
@@ -565,7 +565,6 @@ static BOOL resize_vbar_entry(CLEAR_CONTEXT* clear, CLEAR_VBAR_ENTRY* vBarEntry)
const UINT32 oldPos = vBarEntry->size * bpp;
const UINT32 diffSize = (vBarEntry->count - vBarEntry->size) * bpp;
BYTE* tmp;
- vBarEntry->size = vBarEntry->count;
tmp = (BYTE*)realloc(vBarEntry->pixels, 1ull * vBarEntry->count * bpp);
if (!tmp)
@@ -576,6 +575,7 @@ static BOOL resize_vbar_entry(CLEAR_CONTEXT* clear, CLEAR_VBAR_ENTRY* vBarEntry)
memset(&tmp[oldPos], 0, diffSize);
vBarEntry->pixels = tmp;
+ vBarEntry->size = vBarEntry->count;
}
if (!vBarEntry->pixels && vBarEntry->size)
--
2.49.0

View File

@ -1,57 +0,0 @@
From 947feeadfddf01d30dda5aa16ebc335bfcc23ae0 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 29 Apr 2026 13:42:00 +0000
Subject: [PATCH] [codec,clear] Update CLEAR_GLYPH_ENTRY::count after alloc
Backport of commit c49d1ad43b8c7b32794d0250f2623c2dccd7ef25.
Adapted for 2.11.7: uses GetBytesPerPixel instead of FreeRDPGetBytesPerPixel,
plain realloc instead of winpr_aligned_recalloc; overflow checks match upstream
(size_t count, hlimit / exceeded logging); glyphEntry->count set via (UINT32)cast
after successful realloc.
Made-with: Cursor
---
libfreerdp/codec/clear.c | 22 +++++++++++++++++-----
1 file changed, 17 insertions(+), 5 deletions(-)
diff --git a/libfreerdp/codec/clear.c b/libfreerdp/codec/clear.c
--- a/libfreerdp/codec/clear.c
+++ b/libfreerdp/codec/clear.c
@@ -979,20 +979,31 @@
{
const UINT32 bpp = GetBytesPerPixel(clear->format);
CLEAR_GLYPH_ENTRY* glyphEntry = &(clear->GlyphCache[glyphIndex]);
- glyphEntry->count = nWidth * nHeight;
+ const size_t count = 1ull * nWidth * nHeight;
+ const size_t hlimit = SIZE_MAX / ((nWidth > 0) ? nWidth : 1);
+ if ((nWidth == 0) || (nHeight == 0) || (hlimit < nHeight))
+ {
+ const char* exceeded = (hlimit < nHeight) ? "within" : "outside";
+ WLog_ERR(TAG,
+ "CLEARCODEC_FLAG_GLYPH_INDEX: nWidth=%" PRIu32 ", nHeight=%" PRIu32
+ ", nWidth * nHeight is %s allowed range",
+ nWidth, nHeight, exceeded);
+ return FALSE;
+ }
- if (glyphEntry->count > glyphEntry->size)
+ if (count > glyphEntry->size)
{
BYTE* tmp;
- tmp = realloc(glyphEntry->pixels, 1ull * glyphEntry->count * bpp);
+ tmp = realloc(glyphEntry->pixels, 1ull * count * bpp);
if (!tmp)
{
- WLog_ERR(TAG, "glyphEntry->pixels realloc %" PRIu32 " failed!",
- glyphEntry->count * bpp);
+ WLog_ERR(TAG, "glyphEntry->pixels realloc %" PRIuz " failed!",
+ count * bpp);
return FALSE;
}
+ glyphEntry->count = (UINT32)count;
glyphEntry->size = glyphEntry->count;
glyphEntry->pixels = (UINT32*)tmp;
}

View File

@ -1,218 +0,0 @@
From 74ff8a097c3606710327e3af2863aa52d2dac79e Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 29 Apr 2026 15:10:00 +0000
Subject: [PATCH] [codec,dsp] add format checks
Backport of commit 03b48b3601d867afccac1cdc6081de7a275edce7.
Adapted for 2.11.x: uses `context->format` instead of
`context->common.format`, no `WINPR_RESTRICT`, added
`#include <winpr/assert.h>`. Kept C89 declaration style in
`freerdp_dsp_decode_mp3` and `freerdp_dsp_encode_mp3`. Omitted
LAME/mp3 variable modernization. DVI_ADPCM block in
`freerdp_dsp_context_reset` omits FramesPerPacket sizing from upstream 3.x
but adds `Stream_SetPosition(context->buffer, 0)` after validation (same
intent as upstream `Stream_ResetPosition`). `freerdp_dsp_decode_ima_adpcm`
matches upstream order: validate format before locals; `out_size` uses
`size * 4ull`. `Stream_BufferAs` replaced with
`Stream_Buffer` cast. `nullptr` replaced with `NULL`.
Made-with: Cursor
---
libfreerdp/codec/dsp.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 93 insertions(+), 4 deletions(-)
diff --git a/libfreerdp/codec/dsp.c b/libfreerdp/codec/dsp.c
index a18d044..e79911d 100644
--- a/libfreerdp/codec/dsp.c
+++ b/libfreerdp/codec/dsp.c
@@ -27,6 +27,7 @@
#include <string.h>
#include <winpr/crt.h>
+#include <winpr/assert.h>
#include <freerdp/types.h>
#include <freerdp/log.h>
@@ -329,13 +330,30 @@
return (UINT16)d;
}
+static BOOL valid_ima_adpcm_format(const FREERDP_DSP_CONTEXT* context)
+{
+ WINPR_ASSERT(context);
+ if (context->format.wFormatTag != WAVE_FORMAT_DVI_ADPCM)
+ return FALSE;
+ if (context->format.nBlockAlign <= 4ULL)
+ return FALSE;
+ if (context->format.nChannels < 1)
+ return FALSE;
+ if (context->format.wBitsPerSample == 0)
+ return FALSE;
+ return TRUE;
+}
+
static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size,
wStream* out)
{
+ if (!valid_ima_adpcm_format(context))
+ return FALSE;
+
BYTE* dst;
BYTE sample;
UINT16 decoded;
- size_t out_size = size * 4;
+ size_t out_size = size * 4ull;
UINT32 channel;
const UINT32 block_size = context->format.nBlockAlign;
const UINT32 channels = context->format.nChannels;
@@ -464,6 +482,20 @@
#endif
#if defined(WITH_LAME)
+static BOOL valid_mp3_format(const FREERDP_DSP_CONTEXT* context)
+{
+ WINPR_ASSERT(context);
+ if (context->format.wFormatTag != WAVE_FORMAT_MPEGLAYER3)
+ return FALSE;
+ if (context->format.nChannels < 1)
+ return FALSE;
+ if (context->format.wBitsPerSample == 0)
+ return FALSE;
+ if (context->format.nSamplesPerSec == 0)
+ return FALSE;
+ return TRUE;
+}
+
static BOOL freerdp_dsp_decode_mp3(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size,
wStream* out)
{
@@ -474,6 +506,8 @@
if (!context || !src || !out)
return FALSE;
+ if (!valid_mp3_format(context))
+ return FALSE;
buffer_size = 2 * context->format.nChannels * context->format.nSamplesPerSec;
@@ -509,6 +543,9 @@
if (!context || !src || !out)
return FALSE;
+ if (!valid_mp3_format(context))
+ return FALSE;
+
samples_per_channel = size / context->format.nChannels / context->format.wBitsPerSample / 8;
/* Ensure worst case buffer size for mp3 stream taken from LAME header */
@@ -716,6 +753,10 @@
BYTE encoded;
size_t out_size;
size_t align;
+
+ if (!valid_ima_adpcm_format(context))
+ return FALSE;
+
out_size = size / 2;
if (!Stream_EnsureRemainingCapacity(out, size))
@@ -788,6 +829,20 @@
static const INT32 ms_adpcm_coeffs2[7] = { 0, -256, 0, 64, 0, -208, -232 };
+static BOOL valid_ms_adpcm_format(const FREERDP_DSP_CONTEXT* context)
+{
+ WINPR_ASSERT(context);
+ if (context->format.wFormatTag != WAVE_FORMAT_ADPCM)
+ return FALSE;
+ if (context->format.nBlockAlign <= 4ULL)
+ return FALSE;
+ if (context->format.nChannels < 1)
+ return FALSE;
+ if (context->format.wBitsPerSample == 0)
+ return FALSE;
+ return TRUE;
+}
+
static INLINE INT16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* adpcm, BYTE sample, int channel)
{
INT8 nibble;
@@ -819,6 +874,9 @@
BYTE* dst;
BYTE sample;
const size_t out_size = size * 4;
+
+ if (!valid_ms_adpcm_format(context))
+ return FALSE;
const UINT32 channels = context->format.nChannels;
const UINT32 block_size = context->format.nBlockAlign;
@@ -947,6 +1005,10 @@
INT32 sample;
size_t out_size;
const size_t step = 8 + ((context->format.nChannels > 1) ? 4 : 0);
+
+ if (!valid_ms_adpcm_format(context))
+ return FALSE;
+
out_size = size / 2;
if (!Stream_EnsureRemainingCapacity(out, size))
@@ -1308,6 +1370,28 @@
return FALSE;
context->format = *targetFormat;
+
+ switch (context->format.wFormatTag)
+ {
+#if defined(WITH_LAME)
+ case WAVE_FORMAT_MPEGLAYER3:
+ if (!valid_mp3_format(context))
+ return FALSE;
+ break;
+#endif
+ case WAVE_FORMAT_ADPCM:
+ if (!valid_ms_adpcm_format(context))
+ return FALSE;
+ break;
+ case WAVE_FORMAT_DVI_ADPCM:
+ if (!valid_ima_adpcm_format(context))
+ return FALSE;
+ Stream_SetPosition(context->buffer, 0);
+ break;
+ default:
+ break;
+ }
+
#if defined(WITH_FAAD2)
context->faadSetup = FALSE;
#endif
@@ -1328,19 +1412,24 @@
cfg = faacEncGetCurrentConfiguration(context->faac);
cfg->bitRate = 10000;
- faacEncSetConfiguration(context->faac, cfg);
+ {
+ const int frc = faacEncSetConfiguration(context->faac, cfg);
+ if (frc <= 0)
+ return FALSE;
+ }
}
#endif
#if defined(WITH_SOXR)
{
soxr_io_spec_t iospec = soxr_io_spec(SOXR_INT16, SOXR_INT16);
- soxr_error_t error;
+ soxr_error_t error = NULL;
+
soxr_delete(context->sox);
context->sox = soxr_create(context->format.nSamplesPerSec, targetFormat->nSamplesPerSec,
targetFormat->nChannels, &error, &iospec, NULL, NULL);
- if (!context->sox || (error != 0))
+ if (!context->sox || (error != NULL))
return FALSE;
}
#endif

View File

@ -1,237 +0,0 @@
From aaf333862fb54d4cfc19a1866b76500d86679719 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 28 Apr 2026 04:41:15 +0000
Subject: [PATCH] [codec,dsp] fix array bounds checks
Backport of commit 16df2300e1e3f5a51f68fb1626429e58b531b7c8.
Adapted for 2.11.x: uses C89 declaration style (variables declared
at top of scope, new variables wrapped in blocks), no
`WINPR_RESTRICT`, kept `int channel` in decode function,
`dsp_encode_ima_adpcm_sample` keeps `int channel`.
Made-with: Cursor
---
libfreerdp/codec/dsp.c | 91 +++++++++++++++++++++++++++++++++++++-----
1 file changed, 82 insertions(+), 9 deletions(-)
diff --git a/libfreerdp/codec/dsp.c b/libfreerdp/codec/dsp.c
index e79911d..6d4f780 100644
--- a/libfreerdp/codec/dsp.c
+++ b/libfreerdp/codec/dsp.c
@@ -297,7 +297,16 @@ static UINT16 dsp_decode_ima_adpcm_sample(ADPCM* adpcm, unsigned int channel, BY
{
INT32 ss;
INT32 d;
- ss = ima_step_size_table[adpcm->ima.last_step[channel]];
+ INT16 offset;
+
+ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_step));
+ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_sample));
+
+ offset = adpcm->ima.last_step[channel];
+ WINPR_ASSERT(offset >= 0);
+ WINPR_ASSERT(offset < ARRAYSIZE(ima_step_size_table));
+
+ ss = ima_step_size_table[offset];
d = (ss >> 3);
if (sample & 1)
@@ -320,6 +329,8 @@ static UINT16 dsp_decode_ima_adpcm_sample(ADPCM* adpcm, unsigned int channel, BY
d = 32767;
adpcm->ima.last_sample[channel] = (INT16)d;
+
+ WINPR_ASSERT(sample < ARRAYSIZE(ima_step_index_table));
adpcm->ima.last_step[channel] += ima_step_index_table[sample];
if (adpcm->ima.last_step[channel] < 0)
@@ -368,6 +379,9 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, const BYT
{
if (size % block_size == 0)
{
+ if (size < 4)
+ return FALSE;
+
context->adpcm.ima.last_sample[0] =
(INT16)(((UINT16)(*src)) | (((UINT16)(*(src + 1))) << 8));
context->adpcm.ima.last_step[0] = (INT16)(*(src + 2));
@@ -377,6 +391,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, const BYT
if (channels > 1)
{
+ if (size < 4)
+ return FALSE;
context->adpcm.ima.last_sample[1] =
(INT16)(((UINT16)(*src)) | (((UINT16)(*(src + 1))) << 8));
context->adpcm.ima.last_step[1] = (INT16)(*(src + 2));
@@ -388,6 +404,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, const BYT
if (channels > 1)
{
+ if (size < 8)
+ return FALSE;
for (i = 0; i < 8; i++)
{
channel = (i < 4 ? 0 : 1);
@@ -407,6 +425,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, const BYT
}
else
{
+ if (size < 1)
+ return FALSE;
sample = ((*src) & 0x0f);
decoded = dsp_decode_ima_adpcm_sample(&context->adpcm, 0, sample);
*dst++ = (decoded & 0xFF);
@@ -687,7 +707,16 @@ static BYTE dsp_encode_ima_adpcm_sample(ADPCM* adpcm, int channel, INT16 sample)
INT32 ss;
BYTE enc;
INT32 diff;
- ss = ima_step_size_table[adpcm->ima.last_step[channel]];
+ INT16 offset;
+
+ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_step));
+ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_sample));
+
+ offset = adpcm->ima.last_step[channel];
+ WINPR_ASSERT(offset >= 0);
+ WINPR_ASSERT(offset < ARRAYSIZE(ima_step_size_table));
+
+ ss = ima_step_size_table[offset];
d = e = sample - adpcm->ima.last_sample[channel];
diff = ss >> 3;
enc = 0;
@@ -733,6 +762,8 @@ static BYTE dsp_encode_ima_adpcm_sample(ADPCM* adpcm, int channel, INT16 sample)
diff = 32767;
adpcm->ima.last_sample[channel] = (INT16)diff;
+
+ WINPR_ASSERT(enc < ARRAYSIZE(ima_step_index_table));
adpcm->ima.last_step[channel] += ima_step_index_table[enc];
if (adpcm->ima.last_step[channel] < 0)
@@ -847,10 +878,24 @@ static INLINE INT16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* adpcm, BYTE sample
{
INT8 nibble;
INT32 presample;
+ BYTE predictor;
+ INT32 coeff1 = 0;
+ INT32 coeff2 = 0;
+
+ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.sample1));
+ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.sample2));
+ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.delta));
+ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.predictor));
+
nibble = (sample & 0x08 ? (INT8)sample - 16 : (INT8)sample);
- presample = ((adpcm->ms.sample1[channel] * ms_adpcm_coeffs1[adpcm->ms.predictor[channel]]) +
- (adpcm->ms.sample2[channel] * ms_adpcm_coeffs2[adpcm->ms.predictor[channel]])) /
- 256;
+ predictor = adpcm->ms.predictor[channel];
+ if (predictor < ARRAYSIZE(ms_adpcm_coeffs1))
+ coeff1 = ms_adpcm_coeffs1[predictor];
+
+ if (predictor < ARRAYSIZE(ms_adpcm_coeffs2))
+ coeff2 = ms_adpcm_coeffs2[predictor];
+ presample =
+ ((adpcm->ms.sample1[channel] * coeff1) + (adpcm->ms.sample2[channel] * coeff2)) / 256;
presample += nibble * adpcm->ms.delta[channel];
if (presample > 32767)
@@ -860,7 +905,14 @@ static INLINE INT16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* adpcm, BYTE sample
adpcm->ms.sample2[channel] = adpcm->ms.sample1[channel];
adpcm->ms.sample1[channel] = presample;
- adpcm->ms.delta[channel] = adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[sample] / 256;
+
+ {
+ INT32 tableval = 0;
+ if (sample < ARRAYSIZE(ms_adpcm_adaptation_table))
+ tableval = ms_adpcm_adaptation_table[sample];
+
+ adpcm->ms.delta[channel] = adpcm->ms.delta[channel] * tableval / 256;
+ }
if (adpcm->ms.delta[channel] < 16)
adpcm->ms.delta[channel] = 16;
@@ -891,6 +943,9 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE
{
if (channels > 1)
{
+ if (size < 14)
+ return FALSE;
+
context->adpcm.ms.predictor[0] = *src++;
context->adpcm.ms.predictor[1] = *src++;
context->adpcm.ms.delta[0] = read_int16(src);
@@ -917,6 +972,9 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE
}
else
{
+ if (size < 7)
+ return FALSE;
+
context->adpcm.ms.predictor[0] = *src++;
context->adpcm.ms.delta[0] = read_int16(src);
src += 2;
@@ -934,12 +992,16 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE
if (channels > 1)
{
+ if (size < 1)
+ return FALSE;
sample = *src++;
size--;
write_int16(dst, freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0));
dst += 2;
write_int16(dst, freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample & 0x0F, 1));
dst += 2;
+ if (size < 1)
+ return FALSE;
sample = *src++;
size--;
write_int16(dst, freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0));
@@ -949,6 +1011,8 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE
}
else
{
+ if (size < 1)
+ return FALSE;
sample = *src++;
size--;
write_int16(dst, freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0));
@@ -962,10 +1026,16 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE
return TRUE;
}
-static BYTE freerdp_dsp_encode_ms_adpcm_sample(ADPCM* adpcm, INT32 sample, int channel)
+static BYTE freerdp_dsp_encode_ms_adpcm_sample(ADPCM* adpcm, INT32 sample, size_t channel)
{
INT32 presample;
INT32 errordelta;
+
+ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.sample1));
+ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.sample2));
+ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.delta));
+ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.predictor));
+
presample = ((adpcm->ms.sample1[channel] * ms_adpcm_coeffs1[adpcm->ms.predictor[channel]]) +
(adpcm->ms.sample2[channel] * ms_adpcm_coeffs2[adpcm->ms.predictor[channel]])) /
256;
@@ -988,8 +1058,11 @@ static BYTE freerdp_dsp_encode_ms_adpcm_sample(ADPCM* adpcm, INT32 sample, int c
adpcm->ms.sample2[channel] = adpcm->ms.sample1[channel];
adpcm->ms.sample1[channel] = presample;
- adpcm->ms.delta[channel] =
- adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[(((BYTE)errordelta) & 0x0F)] / 256;
+ {
+ const size_t offset = (((BYTE)errordelta) & 0x0F);
+ WINPR_ASSERT(offset < ARRAYSIZE(ms_adpcm_adaptation_table));
+ adpcm->ms.delta[channel] = adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[offset] / 256;
+ }
if (adpcm->ms.delta[channel] < 16)
adpcm->ms.delta[channel] = 16;
--
2.53.0

View File

@ -1,31 +0,0 @@
From 836d5b6f6d305ba6542fac8ba2d16b2e4e8b13a7 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Thu, 30 Apr 2026 15:03:21 +0200
Subject: [PATCH] [codec,planar] fix bounds checks
---
libfreerdp/codec/planar.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c
index 9b4d13057..67fc4212e 100644
--- a/libfreerdp/codec/planar.c
+++ b/libfreerdp/codec/planar.c
@@ -841,12 +841,12 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* planar, const BYTE* pSrcData, UINT
return FALSE;
}
- if ((nXDst + nSrcWidth) * bpp > nDstStep)
+ if ((nXDst + nSrcWidth) * bpp > nTempStep)
{
WLog_ERR(TAG,
"planar plane destination (X %" PRIu32 " + width %" PRIu32
") * bpp %" PRIu32 " exceeds stride %" PRIu32,
- nXDst, nSrcWidth, bpp, nDstStep);
+ nXDst, nSrcWidth, bpp, nTempStep);
return FALSE;
}
--
2.52.0

View File

@ -1,89 +0,0 @@
From 78188ab479c8e6eb9ba2475b3732c76b4bbe5425 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Mon, 13 Apr 2026 14:00:00 +0200
Subject: [PATCH] [codec,progressive] Fail progressive_rfx_quant_sub on invalid
values
Backport of commit 78188ab479c8e6eb9ba2475b3732c76b4bbe5425.
Made-with: Cursor
---
libfreerdp/codec/progressive.c | 42 ++++++++++++++++++++++++++++++----
1 file changed, 38 insertions(+), 4 deletions(-)
diff --git a/libfreerdp/codec/progressive.c b/libfreerdp/codec/progressive.c
index 8894b35..bbcc921 100644
--- a/libfreerdp/codec/progressive.c
+++ b/libfreerdp/codec/progressive.c
@@ -155,20 +155,51 @@ static INLINE void progressive_rfx_quant_lsub(RFX_COMPONENT_CODEC_QUANT* q, int
q->LL3 -= val; /* LL3 */
}
-static INLINE void progressive_rfx_quant_sub(const RFX_COMPONENT_CODEC_QUANT* q1,
+static INLINE BOOL progressive_rfx_quant_sub(const RFX_COMPONENT_CODEC_QUANT* q1,
const RFX_COMPONENT_CODEC_QUANT* q2,
RFX_COMPONENT_CODEC_QUANT* dst)
{
+ if (q1->HH1 < q2->HL1)
+ return FALSE;
dst->HL1 = q1->HL1 - q2->HL1; /* HL1 */
+
+ if (q1->LH1 < q2->LH1)
+ return FALSE;
dst->LH1 = q1->LH1 - q2->LH1; /* LH1 */
+
+ if (q1->HH1 < q2->HH1)
+ return FALSE;
dst->HH1 = q1->HH1 - q2->HH1; /* HH1 */
+
+ if (q1->HL2 < q2->HL2)
+ return FALSE;
dst->HL2 = q1->HL2 - q2->HL2; /* HL2 */
+
+ if (q1->LH2 < q2->LH2)
+ return FALSE;
dst->LH2 = q1->LH2 - q2->LH2; /* LH2 */
+
+ if (q1->HH2 < q2->HH2)
+ return FALSE;
dst->HH2 = q1->HH2 - q2->HH2; /* HH2 */
+
+ if (q1->HL3 < q2->HL3)
+ return FALSE;
dst->HL3 = q1->HL3 - q2->HL3; /* HL3 */
+
+ if (q1->LH3 < q2->LH3)
+ return FALSE;
dst->LH3 = q1->LH3 - q2->LH3; /* LH3 */
+
+ if (q1->HH3 < q2->HH3)
+ return FALSE;
dst->HH3 = q1->HH3 - q2->HH3; /* HH3 */
+
+ if (q1->LL3 < q2->LL3)
+ return FALSE;
dst->LL3 = q1->LL3 - q2->LL3; /* LL3 */
+
+ return TRUE;
}
static INLINE BOOL progressive_rfx_quant_lcmp_less_equal(const RFX_COMPONENT_CODEC_QUANT* q,
@@ -1433,9 +1464,12 @@ static INLINE int progressive_decompress_tile_upgrade(PROGRESSIVE_CONTEXT* progr
progressive_rfx_quant_add(quantY, quantProgY, &yBitPos);
progressive_rfx_quant_add(quantCb, quantProgCb, &cbBitPos);
progressive_rfx_quant_add(quantCr, quantProgCr, &crBitPos);
- progressive_rfx_quant_sub(&(tile->yBitPos), &yBitPos, &yNumBits);
- progressive_rfx_quant_sub(&(tile->cbBitPos), &cbBitPos, &cbNumBits);
- progressive_rfx_quant_sub(&(tile->crBitPos), &crBitPos, &crNumBits);
+ if (!progressive_rfx_quant_sub(&(tile->yBitPos), &yBitPos, &yNumBits))
+ goto fail;
+ if (!progressive_rfx_quant_sub(&(tile->cbBitPos), &cbBitPos, &cbNumBits))
+ goto fail;
+ if (!progressive_rfx_quant_sub(&(tile->crBitPos), &crBitPos, &crNumBits))
+ goto fail;
progressive_rfx_quant_add(quantY, quantProgY, &shiftY);
progressive_rfx_quant_lsub(&shiftY, 1); /* -6 + 5 = -1 */
progressive_rfx_quant_add(quantCb, quantProgCb, &shiftCb);
--
2.49.0

View File

@ -1,29 +0,0 @@
From 78677dc6e262f46937d00c3aa52381e4bb198fa5 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Mon, 13 Apr 2026 14:00:00 +0200
Subject: [PATCH] [codec,progressive] fix underflow guard in
progressive_rfx_quant_sub
Backport of commit 78677dc6e262f46937d00c3aa52381e4bb198fa5.
Made-with: Cursor
---
libfreerdp/codec/progressive.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libfreerdp/codec/progressive.c b/libfreerdp/codec/progressive.c
index bbcc921..1234567 100644
--- a/libfreerdp/codec/progressive.c
+++ b/libfreerdp/codec/progressive.c
@@ -158,7 +158,7 @@ static INLINE BOOL progressive_rfx_quant_sub(const RFX_COMPONENT_CODEC_QUANT* q1
const RFX_COMPONENT_CODEC_QUANT* q2,
RFX_COMPONENT_CODEC_QUANT* dst)
{
- if (q1->HH1 < q2->HL1)
+ if (q1->HL1 < q2->HL1)
return FALSE;
dst->HL1 = q1->HL1 - q2->HL1; /* HL1 */
--
2.49.0

View File

@ -1,37 +0,0 @@
From 9397fbeed4b2862ae68f9556950af415163c9398 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Tue, 7 Jul 2026 12:58:11 +0200
Subject: [PATCH] [core,message] fix update_message_WindowIcon
---
libfreerdp/core/message.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/libfreerdp/core/message.c b/libfreerdp/core/message.c
index 7af29e94d..1ab0d4e4e 100644
--- a/libfreerdp/core/message.c
+++ b/libfreerdp/core/message.c
@@ -1147,18 +1147,21 @@ static BOOL update_message_WindowIcon(rdpContext* context, const WINDOW_ORDER_IN
if (!wParam)
return FALSE;
- CopyMemory(wParam, orderInfo, sizeof(WINDOW_ORDER_INFO));
+ *wParam = *orderInfo;
+
lParam = (WINDOW_ICON_ORDER*)calloc(1, sizeof(WINDOW_ICON_ORDER));
if (!lParam)
goto out_fail;
+ *lParam = *windowIcon;
lParam->iconInfo = calloc(1, sizeof(ICON_INFO));
if (!lParam->iconInfo)
goto out_fail;
- CopyMemory(lParam, windowIcon, sizeof(WINDOW_ICON_ORDER));
+ *lParam->iconInfo = *windowIcon->iconInfo;
+
WLog_VRB(TAG, "update_message_WindowIcon");
if (windowIcon->iconInfo->cbBitsColor > 0)

View File

@ -1,163 +0,0 @@
From cdabc78fc50a5a1859faebc1e41a3dfbbd007341 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Thu, 9 Jul 2026 08:34:24 +0200
Subject: [PATCH] [core,redirection] check redirection values for validity
Add some basic validity checks for data provided by server.
---
libfreerdp/core/redirection.c | 25 ++++++++++++++++++++-----
libfreerdp/core/utils.c | 33 +++++++++++++++++++++++++++++++++
libfreerdp/core/utils.h | 2 ++
3 files changed, 55 insertions(+), 5 deletions(-)
diff --git a/libfreerdp/core/redirection.c b/libfreerdp/core/redirection.c
index 59c6dbcee..b4b222eb2 100644
--- a/libfreerdp/core/redirection.c
+++ b/libfreerdp/core/redirection.c
@@ -406,6 +406,8 @@ static int rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
{
if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetNetAddress), 80))
return -1;
+ if (!utils_is_valid_ip(redirection->TargetNetAddress))
+ return -1;
}
if (redirection->flags & LB_LOAD_BALANCE_INFO)
@@ -419,13 +421,17 @@ static int rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
if (!rdp_redirection_read_data(LB_LOAD_BALANCE_INFO, s, &redirection->LoadBalanceInfoLength,
&redirection->LoadBalanceInfo))
return -1;
+ if (!winpr_str_is_valid_urlN((const char*)redirection->LoadBalanceInfo,
+ redirection->LoadBalanceInfoLength))
+ return -1;
}
if (redirection->flags & LB_USERNAME)
{
if (!rdp_redirection_read_unicode_string(s, &(redirection->Username), 512))
return -1;
-
+ if (winpr_str_has_newlines(redirection->Username))
+ return -1;
WLog_DBG(TAG, "Username: %s", redirection->Username);
}
@@ -433,7 +439,8 @@ static int rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
{
if (!rdp_redirection_read_unicode_string(s, &(redirection->Domain), 52))
return -1;
-
+ if (winpr_str_has_newlines(redirection->Domain))
+ return -1;
WLog_DBG(TAG, "Domain: %s", redirection->Domain);
}
@@ -484,7 +491,8 @@ static int rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
{
if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetFQDN), 512))
return -1;
-
+ if (!winpr_str_is_valid_url(redirection->TargetFQDN))
+ return -1;
WLog_DBG(TAG, "TargetFQDN: %s", redirection->TargetFQDN);
}
@@ -492,7 +500,8 @@ static int rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
{
if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetNetBiosName), 32))
return -1;
-
+ if (!winpr_str_is_valid_url(redirection->TargetNetBiosName))
+ return -1;
WLog_DBG(TAG, "TargetNetBiosName: %s", redirection->TargetNetBiosName);
}
@@ -501,6 +510,8 @@ static int rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
if (!rdp_redirection_read_data(LB_CLIENT_TSV_URL, s, &redirection->TsvUrlLength,
&redirection->TsvUrl))
return -1;
+ if (!winpr_str_is_valid_urlN((const char*)redirection->TsvUrl, redirection->TsvUrlLength))
+ return -1;
}
if (redirection->flags & LB_REDIRECTION_GUID)
@@ -541,7 +552,8 @@ static int rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
{
if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetNetAddresses[i]), 80))
return -1;
-
+ if (!utils_is_valid_ip(redirection->TargetNetAddresses[i]))
+ return -1;
WLog_DBG(TAG, "TargetNetAddresses[%" PRIuz "]: %s", i,
redirection->TargetNetAddresses[i]);
}
@@ -569,7 +581,10 @@ int rdp_recv_enhanced_security_redirection_packet(rdpRdp* rdp, wStream* s)
status = rdp_recv_server_redirection_pdu(rdp, s);
if (status < 0)
+ {
+ WLog_ERR(TAG, "redirection packet invalid, aborting");
return status;
+ }
if (Stream_GetRemainingLength(s) >= 1)
{
diff --git a/libfreerdp/core/utils.c b/libfreerdp/core/utils.c
index 139c1a433..102819915 100644
--- a/libfreerdp/core/utils.c
+++ b/libfreerdp/core/utils.c
@@ -22,6 +22,16 @@
#include "config.h"
#endif
+#include <string.h>
+
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
#include <winpr/assert.h>
#include <freerdp/freerdp.h>
@@ -65,3 +75,26 @@ BOOL utils_str_is_empty(const char* str)
return TRUE;
return FALSE;
}
+
+static BOOL isValidIPv4(const char* ipAddress)
+{
+ struct sockaddr_in sa;
+ int result;
+ memset(&sa, 0, sizeof(sa));
+ result = inet_pton(AF_INET, ipAddress, &(sa.sin_addr));
+ return result != 0;
+}
+
+static BOOL isValidIPv6(const char* ipAddress)
+{
+ struct sockaddr_in6 sa;
+ int result;
+ memset(&sa, 0, sizeof(sa));
+ result = inet_pton(AF_INET6, ipAddress, &(sa.sin6_addr));
+ return result != 0;
+}
+
+BOOL utils_is_valid_ip(const char* ipAddress)
+{
+ return isValidIPv4(ipAddress) || isValidIPv6(ipAddress);
+}
diff --git a/libfreerdp/core/utils.h b/libfreerdp/core/utils.h
index 432951812..4b150c416 100644
--- a/libfreerdp/core/utils.h
+++ b/libfreerdp/core/utils.h
@@ -30,4 +30,6 @@ BOOL utils_abort_connect(rdpContext* context);
BOOL utils_str_is_empty(const char* str);
BOOL utils_str_copy(const char* value, char** dst);
+FREERDP_LOCAL BOOL utils_is_valid_ip(const char* ipAddress);
+
#endif /* FREERDP_LIB_CORE_UTILS_H */

View File

@ -1,34 +0,0 @@
From b3cff65a1adbbdeda478f035218eeadc72ca0032 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Tue, 14 Jul 2026 12:30:27 +0200
Subject: [PATCH] [core,redirection] relax checks
* TsvUrl is opaque, do not assume any format
* LoadBalanceInfo is opaque, do not assume any format
---
libfreerdp/core/redirection.c | 5 -----
1 file changed, 5 deletions(-)
diff --git a/libfreerdp/core/redirection.c b/libfreerdp/core/redirection.c
index b4b222eb2..773496603 100644
--- a/libfreerdp/core/redirection.c
+++ b/libfreerdp/core/redirection.c
@@ -421,9 +421,6 @@ static int rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
if (!rdp_redirection_read_data(LB_LOAD_BALANCE_INFO, s, &redirection->LoadBalanceInfoLength,
&redirection->LoadBalanceInfo))
return -1;
- if (!winpr_str_is_valid_urlN((const char*)redirection->LoadBalanceInfo,
- redirection->LoadBalanceInfoLength))
- return -1;
}
if (redirection->flags & LB_USERNAME)
@@ -510,8 +507,6 @@ static int rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
if (!rdp_redirection_read_data(LB_CLIENT_TSV_URL, s, &redirection->TsvUrlLength,
&redirection->TsvUrl))
return -1;
- if (!winpr_str_is_valid_urlN((const char*)redirection->TsvUrl, redirection->TsvUrlLength))
- return -1;
}
if (redirection->flags & LB_REDIRECTION_GUID)

View File

@ -0,0 +1,55 @@
From c3673aaa5b65e8670c218bdfb5916a4112b628c7 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Wed, 14 Jan 2026 13:29:34 +0100
Subject: [PATCH] [core,tcp] Don't ignore connect errors
Backport of commit 0bdd8da0993231216a7bb4d5e6e33e47d817a944.
Co-Authored-By: Claude <noreply@anthropic.com>
---
libfreerdp/core/tcp.c | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/libfreerdp/core/tcp.c b/libfreerdp/core/tcp.c
index 1d7eda92e..8a731f117 100644
--- a/libfreerdp/core/tcp.c
+++ b/libfreerdp/core/tcp.c
@@ -26,8 +26,11 @@
#include <errno.h>
#include <fcntl.h>
+#include <inttypes.h>
+
#include <winpr/crt.h>
#include <winpr/platform.h>
+#include <winpr/string.h>
#include <winpr/winsock.h>
#if !defined(_WIN32)
@@ -846,12 +849,19 @@ static BOOL freerdp_tcp_connect_timeout(rdpContext* context, int sockfd, struct
if (WAIT_OBJECT_0 != status)
goto fail;
- status = recv(sockfd, NULL, 0, 0);
-
- if (status == SOCKET_ERROR)
{
- if (WSAGetLastError() == WSAECONNRESET)
+ INT32 optval = 0;
+ socklen_t optlen = sizeof(optval);
+ if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0)
+ goto fail;
+
+ if (optval != 0)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_DBG(TAG, "connect failed with error: %s [%" PRId32 "]",
+ winpr_strerror(optval, ebuffer, sizeof(ebuffer)), optval);
goto fail;
+ }
}
status = WSAEventSelect(sockfd, handles[0], 0);
--
2.52.0

View File

@ -0,0 +1,102 @@
From 462b02de4107845ab235e1668f78a43a31eb11fc Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Wed, 14 Jan 2026 13:38:42 +0100
Subject: [PATCH] [core,tcp] Fix PreferIPv6OverIPv4 fallback to IPv4 addresses
Backport of commit 0bdd8da0993231216a7bb4d5e6e33e47d817a944.
Co-Authored-By: Claude <noreply@anthropic.com>
---
libfreerdp/core/tcp.c | 61 ++++++++++++++++++++++++++++++++++++-------
1 file changed, 51 insertions(+), 10 deletions(-)
diff --git a/libfreerdp/core/tcp.c b/libfreerdp/core/tcp.c
index 8a731f117..3cf24c160 100644
--- a/libfreerdp/core/tcp.c
+++ b/libfreerdp/core/tcp.c
@@ -1064,6 +1064,53 @@ static BOOL freerdp_tcp_set_keep_alive_mode(const rdpSettings* settings, int soc
return TRUE;
}
+static struct addrinfo* reorder_addrinfo_by_preference(rdpContext* context, struct addrinfo* addr)
+{
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(addr);
+
+ const BOOL preferIPv6 =
+ freerdp_settings_get_bool(context->settings, FreeRDP_PreferIPv6OverIPv4);
+ if (!preferIPv6)
+ return addr;
+
+ struct addrinfo* ipv6Head = NULL;
+ struct addrinfo* ipv6Tail = NULL;
+ struct addrinfo* otherHead = NULL;
+ struct addrinfo* otherTail = NULL;
+
+ /* Partition the list into IPv6 and other addresses */
+ while (addr)
+ {
+ struct addrinfo* next = addr->ai_next;
+ addr->ai_next = NULL;
+
+ if (addr->ai_family == AF_INET6)
+ {
+ if (!ipv6Head)
+ ipv6Head = addr;
+ else
+ ipv6Tail->ai_next = addr;
+ ipv6Tail = addr;
+ }
+ else
+ {
+ if (!otherHead)
+ otherHead = addr;
+ else
+ otherTail->ai_next = addr;
+ otherTail = addr;
+ }
+ addr = next;
+ }
+
+ /* Concatenate the lists */
+ if (ipv6Tail)
+ ipv6Tail->ai_next = otherHead;
+
+ return ipv6Head ? ipv6Head : otherHead;
+}
+
static int get_next_addrinfo(rdpContext* context, struct addrinfo* input, struct addrinfo** result,
UINT32 errorCode)
{
@@ -1074,14 +1121,6 @@ static int get_next_addrinfo(rdpContext* context, struct addrinfo* input, struct
if (!addr)
goto fail;
- if (freerdp_settings_get_bool(context->settings, FreeRDP_PreferIPv6OverIPv4))
- {
- while (addr && (addr->ai_family != AF_INET6))
- addr = addr->ai_next;
- if (!addr)
- addr = input;
- }
-
*result = addr;
return 0;
@@ -1161,9 +1200,11 @@ int freerdp_tcp_connect(rdpContext* context, rdpSettings* settings, const char*
freerdp_set_last_error_log(context, 0);
/*
- * If PreferIPv6OverIPv4 = TRUE we force to IPv6 if there
- * is such an address available, but fall back to first if not found
+ * If PreferIPv6OverIPv4 = TRUE we reorder addresses by preference:
+ * IPv6 addresses come first, then other addresses.
*/
+ result = reorder_addrinfo_by_preference(context, result);
+
const int rc =
get_next_addrinfo(context, result, &addr, FREERDP_ERROR_DNS_NAME_NOT_FOUND);
if (rc < 0)
--
2.52.0

View File

@ -0,0 +1,76 @@
From 051218feec6c3404e625637c9d812817b7d69c26 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Wed, 14 Jan 2026 13:28:18 +0100
Subject: [PATCH] [core,tcp] Try next DNS entry on connect failure
Backport of commit bd67348eb3380a66b544835346191bd2138a5ba4.
Co-Authored-By: Claude <noreply@anthropic.com>
---
libfreerdp/core/tcp.c | 41 ++++++++++++++++++++++-------------------
1 file changed, 22 insertions(+), 19 deletions(-)
diff --git a/libfreerdp/core/tcp.c b/libfreerdp/core/tcp.c
index efc2aec36..1d7eda92e 100644
--- a/libfreerdp/core/tcp.c
+++ b/libfreerdp/core/tcp.c
@@ -1162,34 +1162,37 @@ int freerdp_tcp_connect(rdpContext* context, rdpSettings* settings, const char*
do
{
sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
+ if (sockfd >= 0)
+ {
+ if ((peerAddress = freerdp_tcp_address_to_string(
+ (const struct sockaddr_storage*)addr->ai_addr, NULL)) != NULL)
+ {
+ WLog_DBG(TAG, "connecting to peer %s", peerAddress);
+ free(peerAddress);
+ }
+
+ if (!freerdp_tcp_connect_timeout(context, sockfd, addr->ai_addr,
+ addr->ai_addrlen, timeout))
+ {
+ close(sockfd);
+ sockfd = -1;
+ }
+ }
+
if (sockfd < 0)
{
const int rc = get_next_addrinfo(context, addr->ai_next, &addr,
FREERDP_ERROR_CONNECT_FAILED);
if (rc < 0)
+ {
+ freeaddrinfo(result);
+ freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_FAILED);
+ WLog_ERR(TAG, "failed to connect to %s", hostname);
return rc;
+ }
}
} while (sockfd < 0);
- if ((peerAddress = freerdp_tcp_address_to_string(
- (const struct sockaddr_storage*)addr->ai_addr, NULL)) != NULL)
- {
- WLog_DBG(TAG, "connecting to peer %s", peerAddress);
- free(peerAddress);
- }
-
- if (!freerdp_tcp_connect_timeout(context, sockfd, addr->ai_addr, addr->ai_addrlen,
- timeout))
- {
- freeaddrinfo(result);
- close(sockfd);
-
- freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_FAILED);
-
- WLog_ERR(TAG, "failed to connect to %s", hostname);
- return -1;
- }
-
freeaddrinfo(result);
}
}
--
2.52.0

View File

@ -0,0 +1,39 @@
From c13c873fcd3e95dcb21e5a811ac878c21ce38d80 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Wed, 14 Jan 2026 13:39:04 +0100
Subject: [PATCH] [core,tcp] fix double free in get_next_addrinfo
Backport of commit 48197426444b7b3587874b5eae175af1113beab8.
Co-Authored-By: Claude <noreply@anthropic.com>
---
libfreerdp/core/tcp.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/libfreerdp/core/tcp.c b/libfreerdp/core/tcp.c
index 3cf24c160..25632f882 100644
--- a/libfreerdp/core/tcp.c
+++ b/libfreerdp/core/tcp.c
@@ -1126,7 +1126,7 @@ static int get_next_addrinfo(rdpContext* context, struct addrinfo* input, struct
fail:
freerdp_set_last_error_if_not(context, errorCode);
- freeaddrinfo(input);
+ *result = NULL;
return -1;
}
@@ -1208,7 +1208,10 @@ int freerdp_tcp_connect(rdpContext* context, rdpSettings* settings, const char*
const int rc =
get_next_addrinfo(context, result, &addr, FREERDP_ERROR_DNS_NAME_NOT_FOUND);
if (rc < 0)
+ {
+ freeaddrinfo(result);
return rc;
+ }
do
{
--
2.52.0

View File

@ -0,0 +1,100 @@
From 84f0e60c998d7c497d141492f7933bc940f7b239 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 6 Jan 2026 12:38:34 +0100
Subject: [PATCH] [core,tcp] retry all DNS entries until success
Backport of commit 4286a4c16495916fbfa6b8a6764c35fc82e1c5ba.
Co-Authored-By: Claude <noreply@anthropic.com>
---
libfreerdp/core/tcp.c | 63 +++++++++++++++++++++++++++++--------------
1 file changed, 43 insertions(+), 20 deletions(-)
diff --git a/libfreerdp/core/tcp.c b/libfreerdp/core/tcp.c
index 0d0641b82..efc2aec36 100644
--- a/libfreerdp/core/tcp.c
+++ b/libfreerdp/core/tcp.c
@@ -1054,6 +1054,33 @@ static BOOL freerdp_tcp_set_keep_alive_mode(const rdpSettings* settings, int soc
return TRUE;
}
+static int get_next_addrinfo(rdpContext* context, struct addrinfo* input, struct addrinfo** result,
+ UINT32 errorCode)
+{
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(result);
+
+ struct addrinfo* addr = input;
+ if (!addr)
+ goto fail;
+
+ if (freerdp_settings_get_bool(context->settings, FreeRDP_PreferIPv6OverIPv4))
+ {
+ while (addr && (addr->ai_family != AF_INET6))
+ addr = addr->ai_next;
+ if (!addr)
+ addr = input;
+ }
+
+ *result = addr;
+ return 0;
+
+fail:
+ freerdp_set_last_error_if_not(context, errorCode);
+ freeaddrinfo(input);
+ return -1;
+}
+
int freerdp_tcp_connect(rdpContext* context, rdpSettings* settings, const char* hostname, int port,
DWORD timeout)
{
@@ -1123,30 +1150,26 @@ int freerdp_tcp_connect(rdpContext* context, rdpSettings* settings, const char*
}
freerdp_set_last_error_log(context, 0);
- addr = result;
+ /*
+ * If PreferIPv6OverIPv4 = TRUE we force to IPv6 if there
+ * is such an address available, but fall back to first if not found
+ */
+ const int rc =
+ get_next_addrinfo(context, result, &addr, FREERDP_ERROR_DNS_NAME_NOT_FOUND);
+ if (rc < 0)
+ return rc;
- if ((addr->ai_family == AF_INET6) && (addr->ai_next != 0) &&
- !settings->PreferIPv6OverIPv4)
+ do
{
- while ((addr = addr->ai_next))
+ sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
+ if (sockfd < 0)
{
- if (addr->ai_family == AF_INET)
- break;
+ const int rc = get_next_addrinfo(context, addr->ai_next, &addr,
+ FREERDP_ERROR_CONNECT_FAILED);
+ if (rc < 0)
+ return rc;
}
-
- if (!addr)
- addr = result;
- }
-
- sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
-
- if (sockfd < 0)
- {
- freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_FAILED);
-
- freeaddrinfo(result);
- return -1;
- }
+ } while (sockfd < 0);
if ((peerAddress = freerdp_tcp_address_to_string(
(const struct sockaddr_storage*)addr->ai_addr, NULL)) != NULL)
--
2.52.0

View File

@ -1,266 +0,0 @@
From a7a47bc0bdab723b674fb05f51e93ae007ed6412 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Thu, 9 Jul 2026 08:34:23 +0200
Subject: [PATCH] [winpr,crt] add functions to test string
* winpr_str_has_newlines to check for newlines in a string
* winpr_str_is_valid_url and winpr_str_is_valid_urlN to check if a
string is a valid url
---
winpr/include/winpr/string.h | 5 ++
winpr/libwinpr/crt/CMakeLists.txt | 8 +++
winpr/libwinpr/crt/string.c | 96 ++++++++++++++++++++++++++++
winpr/libwinpr/crt/test/TestString.c | 82 ++++++++++++++++++++++++
4 files changed, 191 insertions(+)
diff --git a/winpr/include/winpr/string.h b/winpr/include/winpr/string.h
index bb98dffdc..7de9f934f 100644
--- a/winpr/include/winpr/string.h
+++ b/winpr/include/winpr/string.h
@@ -31,6 +31,11 @@ extern "C"
{
#endif
+ WINPR_API BOOL winpr_str_is_valid_url(const char* str);
+ WINPR_API BOOL winpr_str_is_valid_urlN(const char* str, size_t len);
+ WINPR_API BOOL winpr_str_has_newlines(const char* str);
+
+
WINPR_API BOOL winpr_str_append(const char* what, char* buffer, size_t size,
const char* separator);
diff --git a/winpr/libwinpr/crt/CMakeLists.txt b/winpr/libwinpr/crt/CMakeLists.txt
index 5ee4bbd1e..538e74dcf 100644
--- a/winpr/libwinpr/crt/CMakeLists.txt
+++ b/winpr/libwinpr/crt/CMakeLists.txt
@@ -22,6 +22,14 @@ set (CRT_FILES alignment.c
unicode.c
string.c)
+include(CheckSymbolExists)
+check_symbol_exists(regcomp regex.h WINPR_HAVE_REGCOMP)
+check_symbol_exists(regexec regex.h WINPR_HAVE_REGEXEC)
+check_symbol_exists(regfree regex.h WINPR_HAVE_REGFREE)
+if(WINPR_HAVE_REGCOMP AND WINPR_HAVE_REGEXEC AND WINPR_HAVE_REGFREE)
+ winpr_definition_add(-DWINPR_HAVE_REGCOMP)
+endif()
+
if (NOT WITH_ICU)
set (CRT_FILES ${CRT_FILES}
utf.c
diff --git a/winpr/libwinpr/crt/string.c b/winpr/libwinpr/crt/string.c
index 6596b42f5..465b8d133 100644
--- a/winpr/libwinpr/crt/string.c
+++ b/winpr/libwinpr/crt/string.c
@@ -29,11 +29,71 @@
#include <winpr/assert.h>
#include <winpr/endian.h>
+#if defined(WINPR_HAVE_REGCOMP)
+#include <regex.h>
+#endif
+
/* String Manipulation (CRT): http://msdn.microsoft.com/en-us/library/f0151s4x.aspx */
#include "../log.h"
#define TAG WINPR_TAG("crt")
+BOOL winpr_str_is_valid_urlN(const char* str, size_t len)
+{
+ if (!str || (len == 0))
+ return FALSE;
+
+ char* url = strndup(str, len);
+ if (!url)
+ return FALSE;
+
+ const BOOL rc = winpr_str_is_valid_url(url);
+ free(url);
+ return rc;
+}
+
+#if defined(_WIN32)
+#include <shlwapi.h>
+
+BOOL winpr_str_is_valid_url(const char* str)
+{
+ if (!str)
+ return FALSE;
+ return PathIsURLA(str);
+}
+
+#elif defined(WINPR_HAVE_REGCOMP)
+BOOL winpr_str_is_valid_url(const char* str)
+{
+ regex_t regex;
+ const char pattern[] = "^([a-z]+://)?([a-zA-Z0-9.-]+)(:[0-9]+)?(/[a-zA-Z0-9./?=&%-]*)?$";
+ int ret;
+
+ if (!str)
+ return FALSE;
+ memset(&regex, 0, sizeof(regex));
+
+ ret = regcomp(&regex, pattern, REG_EXTENDED);
+ if (ret)
+ {
+ printf("Could not compile regex\n");
+ return FALSE;
+ }
+
+ ret = regexec(&regex, str, 0, NULL, 0);
+ regfree(&regex);
+ return ret == 0;
+}
+
+#else
+BOOL winpr_str_is_valid_url(const char* str)
+{
+ WINPR_UNUSED(str);
+ WLog_WARN(TAG, "URL validation not supported by your build, not performing check.");
+ return TRUE;
+}
+#endif
+
BOOL winpr_str_append(const char* what, char* buffer, size_t size, const char* separator)
{
const size_t used = strnlen(buffer, size);
@@ -636,3 +696,39 @@ INT64 GetLine(char** lineptr, size_t* size, FILE* stream)
return -1;
#endif
}
+
+BOOL winpr_str_has_newlines(const char* str)
+{
+ if (!str)
+ return FALSE;
+ do
+ {
+ char c = *str++;
+ switch (c)
+ {
+ case '\r':
+ case '\n':
+ case 0x0b: /* VT vertical tab */
+ case 0x0c: /* FF form feed */
+ case (char)0x85: /* NEL next line */
+ return TRUE;
+ case 0x20:
+ {
+ switch (*str)
+ {
+ case 0x28: /* LS line separator */
+ case 0x29: /* PS paragraph separator */
+ return TRUE;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case '\0':
+ return FALSE;
+ default:
+ break;
+ }
+ } while (1);
+}
diff --git a/winpr/libwinpr/crt/test/TestString.c b/winpr/libwinpr/crt/test/TestString.c
index df154fe1c..bca470bdd 100644
--- a/winpr/libwinpr/crt/test/TestString.c
+++ b/winpr/libwinpr/crt/test/TestString.c
@@ -27,6 +27,79 @@ static WCHAR testDelimiter[] = { '\r', '\n', '\0' };
#define testDelimiter_Length ((sizeof(testDelimiter) / sizeof(WCHAR)) - 1)
+static BOOL test_valid_url(void)
+{
+ struct test_t
+ {
+ char* string;
+ size_t len;
+ BOOL isUrl;
+ };
+
+ static const struct test_t tests[] = { { "foo.bar.blabla", 15, TRUE },
+ { "somehostname", 12, TRUE },
+ { "somehostname:1234", 17, TRUE },
+ { "tsv://somehostname", 18, TRUE },
+ { "tsv://somehostname/path/foo/gaga", 32, TRUE },
+ { "tsv://somehostname:1234", 23, TRUE },
+ { "tsv://somehostname:1234/path/foo", 33, TRUE },
+ { "lala\rgaga\n", 12, FALSE },
+ { "192.168.0.1", 11, TRUE },
+ //{ "192.168.0.1:1234", 16, FALSE },
+ //{ "192.168.0.1:1234/foo/", 21, FALSE },
+ { "192.168.0.1/bar/foo/", 20, TRUE },
+ { "[::1]", 5, FALSE },
+ { "[::1]:1234", 8, FALSE },
+ { "[2001:db8:3333:4444:5555:6666:7777:8888]", 40,
+ FALSE },
+ { "[2001:db8::1]", 13, FALSE } };
+ BOOL rc = TRUE;
+ for (size_t x = 0; x < ARRAYSIZE(tests); x++)
+ {
+ const struct test_t* cur = &tests[x];
+
+ const BOOL rc1 = winpr_str_is_valid_url(cur->string);
+ const BOOL rc2 = winpr_str_is_valid_urlN(cur->string, cur->len);
+
+#if defined(WINPR_HAVE_REGCOMP) || defined(WITH_URIPARSER)
+ if (rc1 != rc2)
+ rc = FALSE;
+ if (rc1 != cur->isUrl)
+ rc = FALSE;
+#else
+ fprintf(stderr, "[%s] TODO: !defined(WINPR_HAVE_REGCOMP) && !defined(WITH_URIPARSER)\n",
+ __func__);
+#endif
+ }
+
+ return rc;
+}
+
+static BOOL test_newline(void)
+{
+ struct test_t
+ {
+ char* string;
+ size_t len;
+ BOOL hasNewlines;
+ };
+
+ const struct test_t tests[] = { { "foo.bar.blabla", 15, FALSE },
+ { "somehostname", 12, FALSE },
+ { "lala\rgaga\n", 13, TRUE } };
+
+ for (size_t x = 0; x < ARRAYSIZE(tests); x++)
+ {
+ const struct test_t* cur = &tests[x];
+
+ const BOOL rc1 = winpr_str_has_newlines(cur->string);
+ if (rc1 != cur->hasNewlines)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
int TestString(int argc, char* argv[])
{
WCHAR* p;
@@ -34,6 +107,15 @@ int TestString(int argc, char* argv[])
size_t length;
WCHAR* context;
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+
+ if (!test_valid_url())
+ return -1;
+
+ if (!test_newline())
+ return -1;
+
#ifdef __BIG_ENDIAN__
/* Be sure that we always use LE encoded string */
ByteSwapUnicode(testStringW, testStringW_Length);

View File

@ -27,7 +27,7 @@
Name: freerdp
Version: 2.11.7
Release: 11%{?dist}
Release: 7%{?dist}
Epoch: 2
Summary: Free implementation of the Remote Desktop Protocol (RDP)
License: ASL 2.0
@ -35,185 +35,117 @@ URL: http://www.freerdp.com/
Source0: https://github.com/FreeRDP/FreeRDP/archive/%{version}/FreeRDP-%{version}.tar.gz
# Revert changes that break API
# https://issues.redhat.com/browse/RHEL-53081
Patch0: Revert-Moved-clipboard-utils-to-core-library-fixes-6.patch
# https://issues.redhat.com/browse/RHEL-113722
Patch: core-tcp-retry-all-DNS-entries-until-success.patch
Patch: core-tcp-Try-next-DNS-entry-on-connect-failure.patch
Patch: core-tcp-Don-t-ignore-connect-errors.patch
Patch: core-tcp-Fix-PreferIPv6OverIPv4-fallback-to-IPv4-add.patch
Patch: core-tcp-fix-double-free-in-get_next_addrinfo.patch
# https://github.com/FreeRDP/FreeRDP/commit/c4a7c371342edf0d307cea728f56d3302f0ab38c
Patch1: gdi-gfx-properly-clamp-SurfaceToSurface.patch
Patch: gdi-gfx-properly-clamp-SurfaceToSurface.patch
# https://github.com/FreeRDP/FreeRDP/commit/c4391827d7facfc874ca7f61a92afb82232a5748
Patch2: codec-clear-fix-clear_resize_buffer-checks.patch
Patch: codec-clear-fix-clear_resize_buffer-checks.patch
# https://github.com/FreeRDP/FreeRDP/commit/f8688b57f6cfad9a0b05475a6afbde355ffab720
Patch3: codec-clear-fix-off-by-one-length-check.patch
Patch: codec-clear-fix-off-by-one-length-check.patch
# https://github.com/FreeRDP/FreeRDP/commit/1bab198a2edd0d0e6e1627d21a433151ea190500
Patch4: codec-planar-fix-decoder-length-checks.patch
Patch: codec-planar-fix-decoder-length-checks.patch
# https://github.com/FreeRDP/FreeRDP/commit/243ecf804bb122e8e643a5c142ad5a49d7aa19ee
Patch5: codec-clear-check-clear_decomress-glyphData.patch
Patch: codec-clear-check-clear_decomress-glyphData.patch
# https://github.com/FreeRDP/FreeRDP/commit/0421b53fcb4a80c95f51342e4a2c40c68a4101d3
Patch6: client-x11-fix-double-free-in-case-of-invalid-pointe.patch
Patch: client-x11-fix-double-free-in-case-of-invalid-pointe.patch
# https://github.com/FreeRDP/FreeRDP/commit/52106a26726a2aba77aa6d86014d2eb3507f0783
Patch7: cache-offscreen-invalidate-bitmap-before-free.patch
Patch: cache-offscreen-invalidate-bitmap-before-free.patch
# CVE-2026-22855
# https://github.com/FreeRDP/FreeRDP/commit/57c5647d98c2a026de8b681159cb188ca0439ef8
Patch8: utils-smartcard-add-length-validity-checks.patch
Patch: utils-smartcard-add-length-validity-checks.patch
# CVE-2026-22858
# https://github.com/FreeRDP/FreeRDP/commit/62a9e787edb2cfce9858fa4ceda5461680efc590
Patch9: crypto-base64-ensure-char-is-singend.patch
Patch: crypto-base64-ensure-char-is-singend.patch
# CVE-2026-22859
# https://github.com/FreeRDP/FreeRDP/commit/7b7e6de8fe427a2f01d331056774aec69710590b
Patch10: channels-urbdrc-check-interface-indices-before-use.patch
Patch: channels-urbdrc-check-interface-indices-before-use.patch
# CVE-2026-26955
# https://github.com/FreeRDP/FreeRDP/commit/7d8fdce2d0ef337cb86cb37fc0c436c905e04d77
Patch11: codec-clear-fix-destination-checks.patch
Patch: codec-clear-fix-destination-checks.patch
# CVE-2026-26965
# https://github.com/FreeRDP/FreeRDP/commit/a0be5cb87d760bb1c803ad1bb835aa1e73e62abc
Patch12: codec-planar-fix-missing-destination-bounds-checks.patch
Patch: codec-planar-fix-missing-destination-bounds-checks.patch
# CVE-2026-22852
# https://github.com/FreeRDP/FreeRDP/commit/cd1ffa112cfbe1b40a9fd57e299a8ea12e23df0d
Patch13: channels-audin-free-up-old-audio-formats.patch
Patch: channels-audin-free-up-old-audio-formats.patch
# CVE-2026-22854
# https://github.com/FreeRDP/FreeRDP/commit/3da319570c8a6be0a79b3306f1ed354c4a943259
Patch14: channels-drive-fix-constant-type.patch
Patch: channels-drive-fix-constant-type.patch
# CVE-2026-22856
# https://github.com/FreeRDP/FreeRDP/commit/b35aa3614d32bff3fc1272cd7c4617f711fca1a4
# https://github.com/FreeRDP/FreeRDP/commit/675c20f08f32ca5ec06297108bdf30147d6e2cd9
Patch15: channels-serial-lock-list-dictionary.patch
Patch16: channels-serial-explicitly-lock-serial-IrpThreads.patch
Patch: channels-serial-lock-list-dictionary.patch
Patch: channels-serial-explicitly-lock-serial-IrpThreads.patch
# CVE-2026-23732
# https://github.com/FreeRDP/FreeRDP/commit/3bc1eeb4f63ceec9a696af194e4c1ea0e67ff60c
# https://github.com/FreeRDP/FreeRDP/commit/9f0eb3b7d43069a1e973464bcb43d1ef965ae65e
Patch17: codec-color-add-freerdp_glyph_convert_ex.patch
Patch18: gdi-graphics-Use-freerdp_glyph_convert_ex.patch
Patch: codec-color-add-freerdp_glyph_convert_ex.patch
Patch: gdi-graphics-Use-freerdp_glyph_convert_ex.patch
# CVE-2026-23948
# https://github.com/FreeRDP/FreeRDP/commit/4d44e3c097656a8b9ec696353647b0888ca45860
Patch19: core-info-fix-missing-NULL-check.patch
Patch: core-info-fix-missing-NULL-check.patch
# CVE-2026-24491
# https://github.com/FreeRDP/FreeRDP/commit/e02e052f6692550e539d10f99de9c35a23492db2
# https://github.com/FreeRDP/FreeRDP/commit/635ae3c8193256db01774fab5ff11bcae57aed6b
# https://github.com/FreeRDP/FreeRDP/commit/e01cd85c8003a245ef9778f0eda4b9235514c201
Patch20: channels-drdynvc-reset-channel_callback-before-close.patch
Patch21: channels-video-unify-error-handling.patch
Patch22: channels-video-fix-wrong-cast.patch
Patch: channels-drdynvc-reset-channel_callback-before-close.patch
Patch: channels-video-unify-error-handling.patch
Patch: channels-video-fix-wrong-cast.patch
# CVE-2026-24675
# https://github.com/FreeRDP/FreeRDP/commit/d676518809c319eec15911c705c13536036af2ae
Patch23: channels-urbdrc-do-not-free-MsConfig-on-failure.patch
Patch: channels-urbdrc-do-not-free-MsConfig-on-failure.patch
# CVE-2026-24676
# https://github.com/FreeRDP/FreeRDP/commit/026b81ae5831ac1598d8f7371e0d0996fac7db00
Patch24: channels-audin-reset-audin-format.patch
Patch: channels-audin-reset-audin-format.patch
# CVE-2026-24679
# https://github.com/FreeRDP/FreeRDP/commit/2d563a50be17c1b407ca448b1321378c0726dd31
Patch25: channels-urbdrc-ensure-InterfaceNumber-is-within-ran.patch
Patch: channels-urbdrc-ensure-InterfaceNumber-is-within-ran.patch
# CVE-2026-24681
# https://github.com/FreeRDP/FreeRDP/commit/414f701464929c217f2509bcbd6d2c1f00f7ed73
Patch26: channels-urbdrc-cancel-all-usb-transfers-on-channel-.patch
Patch: channels-urbdrc-cancel-all-usb-transfers-on-channel-.patch
# CVE-2026-24683
# https://github.com/FreeRDP/FreeRDP/commit/d9ca272dce7a776ab475e9b1a8e8c3d2968c8486
Patch27: channels-ainput-lock-context-when-updating-listener.patch
Patch: channels-ainput-lock-context-when-updating-listener.patch
# CVE-2026-24684
# https://github.com/FreeRDP/FreeRDP/commit/622bb7b4402491ca003f47472d0e478132673696
# https://github.com/FreeRDP/FreeRDP/commit/afa6851dc80835d3101e40fcef51b6c5c0f43ea5
Patch28: channels-rdpsnd-terminate-thread-before-free.patch
Patch29: channel-rdpsnd-only-clean-up-thread-before-free.patch
Patch: channels-rdpsnd-terminate-thread-before-free.patch
Patch: channel-rdpsnd-only-clean-up-thread-before-free.patch
# CVE-2026-31806
# https://github.com/FreeRDP/FreeRDP/commit/83d9aedea278a74af3e490ff5eeb889c016dbb2b
# https://github.com/FreeRDP/FreeRDP/commit/169971607cece48384cb94632b829bd57336af0f
Patch30: codec-nsc-limit-copy-area-in-nsc_process_message.patch
Patch31: codec-nsc-fix-use-of-nsc_process_message.patch
# CVE-2026-33984
# https://github.com/FreeRDP/FreeRDP/commit/a2dde6d9832cb032e8cf12cab3da84dafbab9006
Patch32: codec-clear-update-CLEAR_VBAR_ENTRY-size-after-alloc.patch
# CVE-2026-33983
# https://github.com/FreeRDP/FreeRDP/commit/78188ab479c8e6eb9ba2475b3732c76b4bbe5425
# https://github.com/FreeRDP/FreeRDP/commit/78677dc6e262f46937d00c3aa52381e4bb198fa5
Patch33: codec-progressive-fail-progressive_rfx_quant_sub-on-invalid-values.patch
Patch34: codec-progressive-fix-underflow-guard-in-progressive_rfx_quant_sub.patch
# CVE-2026-26986
# https://github.com/FreeRDP/FreeRDP/commit/b4f0f0a18fe53aa8d47d062f91471f4e9c5e0d51
Patch35: client-x11-fix-xf_rail_window_common-cleanup.patch
# CVE-2026-27951
# https://github.com/FreeRDP/FreeRDP/commit/118afc0b954ba9d5632b7836ad24e454555ed113
Patch36: allocations-fix-growth-of-preallocated-buffers.patch
# CVE-2026-29775
# https://github.com/FreeRDP/FreeRDP/commit/ffad58fd2b329efd81a3239e9d7e3c927b8e503f
# https://github.com/FreeRDP/FreeRDP/commit/8270e0bb3d6726c947d57c93ba9caa92a052b557
Patch37: cache-bitmap-overallocate-bitmap-cache.patch
Patch38: cache-bitmap-initialize-overallocated-bitmap-cache-extra-slot.patch
# CVE-2026-31884
# https://github.com/FreeRDP/FreeRDP/commit/03b48b3601d867afccac1cdc6081de7a275edce7
Patch39: codec-dsp-add-format-checks.patch
# CVE-2026-31883
# CVE-2026-31885
# https://github.com/FreeRDP/FreeRDP/commit/16df2300e1e3f5a51f68fb1626429e58b531b7c8
Patch40: codec-dsp-fix-array-bounds-checks.patch
# CVE-2026-33985
# https://github.com/FreeRDP/FreeRDP/commit/c49d1ad43b8c7b32794d0250f2623c2dccd7ef25
Patch41: codec-clear-update-clear_glyph_entry-count-after-alloc.patch
# CVE-2026-25952
# https://github.com/FreeRDP/FreeRDP/commit/1994e9844212a6dfe0ff12309fef520e888986b5
# https://github.com/FreeRDP/FreeRDP/commit/78fd7f580d5f9e6d9d582d82e5ea96003844fbdf
# https://github.com/FreeRDP/FreeRDP/commit/4ff57b68c2960fa414d03c78ff0e0660be1cc5bd
# https://github.com/FreeRDP/FreeRDP/commit/a278ff74117444c635c50ffa5084ecf517171f5a
Patch42: client-x11-lock-appwindow.patch
Patch43: client-x11-improve-rails-window-locking.patch
Patch44: client-x11-refactor-locking.patch
Patch45: client-x11-fix-deadlock-on-output-expose.patch
# CVE-2026-45700
# https://github.com/FreeRDP/FreeRDP/commit/4a065a941ae134a0433d1497c03b3c3eb91b9f85
Patch46: codec-planar-fix-bounds-checks.patch
# CVE-2026-64624
# https://github.com/FreeRDP/FreeRDP/commit/22c5deea52404f51a13276b3abda44e1e60704cf
Patch47: client-common-deactivate-cli-parsing-in-rdp-files.patch
# CVE-2026-67289
# https://github.com/FreeRDP/FreeRDP/commit/f163b2d2080d922cb725800781f43f16bc2882c0
# https://github.com/FreeRDP/FreeRDP/commit/ab5905cbada0d34cf85ffd32881cacaf8275744f
# https://github.com/FreeRDP/FreeRDP/commit/1db16b4b121a1221c0454f7d223096cac87bf244
Patch48: winpr-crt-add-functions-to-test-string.patch
Patch49: core-redirection-check-redirection-values-for-validity.patch
Patch50: core-redirection-relax-checks.patch
# CVE-2026-68580
# https://github.com/FreeRDP/FreeRDP/commit/3bf07d4470bbe42faf4ef8197c8fe1b6b759b00f
Patch51: channels-audin-limit-FramesPerPacket.patch
# CVE-2026-67299
# https://github.com/FreeRDP/FreeRDP/commit/172edbddd24f33033b9eafae6ea1fff0641a24f4
Patch52: core-message-fix-update_message_WindowIcon.patch
Patch: codec-nsc-limit-copy-area-in-nsc_process_message.patch
Patch: codec-nsc-fix-use-of-nsc_process_message.patch
BuildRequires: gcc
BuildRequires: gcc-c++
@ -472,110 +404,171 @@ find %{buildroot} -name "*.a" -delete
%{_libdir}/pkgconfig/winpr-tools2.pc
%changelog
* Tue Aug 04 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2:2.11.7-11
- Backport several CVE fixes (CVE-2026-64624, CVE-2026-67289, CVE-2026-67299,
CVE-2026-68580)
Resolves: RHEL-213173, RHEL-222796, RHEL-222972, RHEL-223606
* Wed Jun 24 2026 RHEL Packaging Agent <rhel-se-jotnar@redhat.com> - 2:2.11.7-10
- Fix incorrect bounds checks in planar codec (CVE-2026-45700)
Resolves: RHEL-186983
* Tue May 05 2026 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-9
- Lock appWindow to fix use-after-free in RAIL mode (CVE-2026-25952)
Resolves: RHEL-159850
* Tue Apr 28 2026 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-8
- Fix double free in xf_rail_window_common cleanup (CVE-2026-26986)
- Fix growth of preallocated buffers (CVE-2026-27951)
- Fix heap-buffer-overflow in bitmap_cache_put (CVE-2026-29775)
- Add DSP format checks (CVE-2026-31884)
- Fix DSP array bounds checks (CVE-2026-31883)
- Fix DSP array bounds checks (CVE-2026-31885)
- Update CLEAR_GLYPH_ENTRY::count after alloc (CVE-2026-33985)
Resolves: RHEL-159806, RHEL-155468, RHEL-161037, RHEL-161472
Resolves: RHEL-161508, RHEL-161075, RHEL-167794
* Fri Apr 10 2026 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-7
- Update CLEAR_VBAR_ENTRY size after alloc (CVE-2026-33984)
- Fail progressive_rfx_quant_sub on invalid values (CVE-2026-33983)
Resolves: RHEL-162949, RHEL-162965
* Tue Mar 31 2026 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-6
* Tue Mar 31 2026 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-7
- Fix use of nsc_process_message
Resolves: RHEL-155984
Resolves: RHEL-155994
* Fri Mar 27 2026 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-5
* Fri Mar 27 2026 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-6
- Backport several CVE fixes
Resolves: RHEL-147954, RHEL-147955, RHEL-147970, RHEL-147977, RHEL-147980
Resolves: RHEL-148002, RHEL-148014, RHEL-148031, RHEL-148906, RHEL-148996
Resolves: RHEL-149007, RHEL-149056, RHEL-155984
Resolves: RHEL-148052, RHEL-148053, RHEL-148056, RHEL-148075, RHEL-148081
Resolves: RHEL-148098, RHEL-148105, RHEL-148106, RHEL-148941, RHEL-149032
Resolves: RHEL-149043, RHEL-149066, RHEL-155994
* Wed Mar 25 2026 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-4
* Wed Mar 25 2026 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-5
- Backport several CVE fixes
Resolves: RHEL-151979, RHEL-152206
Resolves: RHEL-151989, RHEL-152216
* Tue Feb 17 2026 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-3
* Tue Feb 17 2026 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-4
- Backport several CVE fixes
Resolves: RHEL-148825, RHEL-148865, RHEL-148982
Resolves: RHEL-148849, RHEL-148891, RHEL-149030
* Tue Jan 27 2026 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-2
* Tue Jan 27 2026 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-3
- Backport several CVE fixes
Resolves: RHEL-142417, RHEL-142401, RHEL-142385, RHEL-142369, RHEL-142353
Resolves: RHEL-142337, RHEL-142321
Resolves: RHEL-142427, RHEL-142411, RHEL-142395, RHEL-142379, RHEL-142363
Resolves: RHEL-142348, RHEL-142332
* Tue Oct 01 2024 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-1
- Update to 2.11.7 (RHEL-53081)
* Fri Jan 16 2026 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-2
- Try next DNS entry on connect failure
Resolves: RHEL-113722
* Tue Dec 13 2022 Ondrej Holy <oholy@redhat.com> - 2:2.2.0-10
- Fix "implicit declaration of function" errors (#2136153, #2145139)
* Thu May 09 2024 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-1
- Update to 2.11.7 (CVE-2024-32039, CVE-2024-32040, CVE-2024-32041,
CVE-2024-32458, CVE-2024-32459, CVE-2024-32460, CVE-2024-32658,
CVE-2024-32659, CVE-2024-32660, CVE-2024-32661, CVE-2024-32662)
* Thu Dec 08 2022 Ondrej Holy <oholy@redhat.com> - - 2:2.2.0-9
- CVE-2022-39282: Fix length checks in parallel driver (#2136151)
- CVE-2022-39283: Add missing length check in video channel (#2136153)
- CVE-2022-39316, CVE-2022-39317: Add missing length checks in zgfx (#2145139)
- CVE-2022-39318: Fix division by zero in urbdrc channel (#2145139)
- CVE-2022-39319: Add missing length checks in urbdrc channel (#2145139)
- CVE-2022-39320: Ensure urb_create_iocompletion uses size_t (#2145139)
- CVE-2022-39347: Fix path validation in drive channel (#2145139)
- CVE-2022-41877: Add missing length check in drive channel (#2145139)
* Tue Mar 12 2024 Ondrej Holy <oholy@redhat.com> - 2:2.11.2-2
- CVE-2024-22211: Check codec resolution for overflow (RHEL-22244)
* Thu Aug 11 2022 Ondrej Holy <oholy@redhat.com> - 2:2.2.0-8
- Fix /monitor-list output (rhbz#2108866)
* Fri Nov 10 2023 Ondrej Holy <oholy@redhat.com> - 2:2.11.2-1
- Update to 2.11.2 (RHEL-4290, RHEL-4292, RHEL-4296, RHEL-4298, RHEL-4300,
RHEL-4302, RHEL-4304, RHEL-4306, RHEL-4308, RHEL-4310, RHEL-4312,
RHEL-10060)
* Wed Nov 10 2021 Ondrej Holy <oholy@redhat.com> - 2:2.2.0-4
- Refactored RPC gateway parser (rhbz#2017949)
* Tue Dec 13 2022 Ondrej Holy <oholy@redhat.com> - 2:2.4.1-5
- Fix "implicit declaration of function" errors (#2136155, #2145140)
* Fri Nov 05 2021 Felipe Borges <feborges@redhat.com> - 2:2.2.0-3
- Add checks for bitmap and glyph width and heigth values (rhbz#2017956)
* Thu Dec 08 2022 Ondrej Holy <oholy@redhat.com> - - 2:2.4.1-4
- CVE-2022-39282: Fix length checks in parallel driver (#2136152)
- CVE-2022-39283: Add missing length check in video channel (#2136154)
- CVE-2022-39316, CVE-2022-39317: Add missing length checks in zgfx (#2145140)
- CVE-2022-39318: Fix division by zero in urbdrc channel (#2145140)
- CVE-2022-39319: Add missing length checks in urbdrc channel (#2145140)
- CVE-2022-39320: Ensure urb_create_iocompletion uses size_t (#2145140)
- CVE-2022-39347: Fix path validation in drive channel (#2145140)
- CVE-2022-41877: Add missing length check in drive channel (#2145140)
* Wed Apr 28 2021 Ondrej Holy <oholy@redhat.com> - 2:2.2.0-2
- Fix exit codes for /help and similar options (rhbz#1910029)
* Wed Jun 22 2022 Ondrej Holy <oholy@redhat.com> - - 2:2.4.1-3
- Fix gateway functionality with OpenSSL 3.0 (#2023262)
* Fri Nov 20 2020 Ondrej Holy <oholy@redhat.com> - 2:2.2.0-1
- Update to 2.2.0 (rhbz#1881971)
* Fri Nov 26 2021 Ondrej Holy <oholy@redhat.com> - 2:2.4.1-2
- Fix datatype mismatch / big-endian breakage
- Load legacy provider when initializing OpenSSL 3.0
* Mon May 25 2020 Ondrej Holy <oholy@redhat.com> - 2:2.1.1-1
- Update to 2.1.1 (rhbz#1834287).
* Wed Nov 10 2021 Ondrej Holy <oholy@redhat.com> - 2:2.4.1-1
- Update to 2.4.1 (CVE-2021-41159, CVE-2021-41160).
* Fri Apr 17 2020 Ondrej Holy <oholy@redhat.com> - 2:2.0.0-47.rc4
- Fix SCARD_INSUFFICIENT_BUFFER error (rhbz#1803054)
- Do not advertise /usb in help output (rhbz#1761144)
* Mon Aug 09 2021 Mohan Boddu <mboddu@redhat.com> - 2:2.4.0-3
- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags
Related: rhbz#1991688
* Wed Nov 28 2018 Ondrej Holy <oholy@redhat.com> - 2:2.0.0-46.rc4
- Update to 2.0.0-rc4 (#1624340)
* Tue Aug 03 2021 Ondrej Holy <oholy@redhat.com> - 2:2.4.0-2
- Load legacy provider to fix rc4 with OpenSSL 3.0 (#1988443).
* Mon Oct 15 2018 Ondrej Holy <oholy@redhat.com> - 2:2.0.0-45.rc3
- Disable server support in RHEL (#1639165)
* Thu Jul 29 2021 Ondrej Holy <oholy@redhat.com> - 2:2.4.0-1
- Update to 2.4.0.
* Wed Oct 10 2018 Ondrej Holy <oholy@redhat.com> - 2:2.0.0-44.rc3
- Fix packaging issues found by rpmdiff (#1637487)
* Wed Jun 16 2021 Mohan Boddu <mboddu@redhat.com> - 2:2.3.2-2
- Rebuilt for RHEL 9 BETA for openssl 3.0
Related: rhbz#1971065
* Tue Sep 25 2018 Ondrej Holy <oholy@redhat.com> - 2:2.0.0-43.rc3
- Fix important defects found by covscan (#1602500)
* Mon May 17 2021 Ondrej Holy <oholy@redhat.com> - 2:2.3.2-1
- Update to 2.3.2 (#1951123).
* Thu Sep 06 2018 Ondrej Holy <oholy@redhat.com> - 2:2.0.0-42.rc3
- Update to 2.0.0-rc3 (#1624340)
* Mon May 17 2021 Ondrej Holy <oholy@redhat.com> - 2:2.2.0-8
- Fix build with OpenSSL 3.0 (#1952937).
* Thu Apr 15 2021 Mohan Boddu <mboddu@redhat.com> - 2:2.2.0-7
- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937
* Tue Mar 23 2021 Simone Caronni <negativo17@gmail.com> - 2:2.2.0-6
- Explicitly enable Cairo support (#1938393).
* Tue Jan 26 2021 Fedora Release Engineering <releng@fedoraproject.org> - 2:2.2.0-5
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
* Tue Aug 11 2020 Ondrej Holy <oholy@redhat.com> - 2:2.2.0-4
- Use %%cmake_ macros to fix out-of-source builds (#1863586)
* Sat Aug 01 2020 Fedora Release Engineering <releng@fedoraproject.org> - 2:2.2.0-3
- Second attempt - Rebuilt for
https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
* Mon Jul 27 2020 Fedora Release Engineering <releng@fedoraproject.org> - 2:2.2.0-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
* Thu Jul 23 2020 Simone Caronni <negativo17@gmail.com> - 2:2.2.0-1
- Update to 2.2.0.
* Tue Jun 30 2020 Simone Caronni <negativo17@gmail.com> - 2:2.1.2-1
- Update to 2.1.2.
* Thu May 21 2020 Ondrej Holy <oholy@redhat.com> - 2:2.1.1-1
- Update to 2.1.1.
* Fri May 15 2020 Ondrej Holy <oholy@redhat.com> - 2:2.1.0-1
- Update to 2.1.0 (#1833540).
* Fri May 15 2020 Pete Walter <pwalter@fedoraproject.org> - 2:2.0.0-57.20200207git245fc60
- Rebuild for ICU 67
* Fri Feb 07 2020 Simone Caronni <negativo17@gmail.com> - 2:2.0.0-56.20200207git245fc60
- Update to latest snapshot.
* Tue Jan 28 2020 Fedora Release Engineering <releng@fedoraproject.org> - 2:2.0.0-55.20190820git6015229
- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
* Fri Nov 01 2019 Pete Walter <pwalter@fedoraproject.org> - 2:2.0.0-54.20190820git6015229
- Rebuild for ICU 65
* Tue Aug 20 2019 Simone Caronni <negativo17@gmail.com> - 2:2.0.0-53.20190820git6015229
- Update to latest snapshot.
* Thu Jul 25 2019 Fedora Release Engineering <releng@fedoraproject.org> - 2:2.0.0-52.20190918git5e672d4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
* Sun Jul 21 2019 Simone Caronni <negativo17@gmail.com> - 2:2.0.0-51.20190918git5e672d4
- Update to latest snapshot.
* Sat May 18 2019 Simone Caronni <negativo17@gmail.com> - 2:2.0.0-50.20190517gitb907324
- Update to latest snapshot.
* Wed Mar 06 2019 Simone Caronni <negativo17@gmail.com> - 2:2.0.0-49.20190304git435872b
- Fix for GFX color depth (Windows 10).
* Thu Feb 28 2019 Simone Caronni <negativo17@gmail.com> - 2:2.0.0-48.20190228gitce386c8
- Update to latest snapshot post rc4.
- CVE-2018-1000852 (#1661642).
* Thu Jan 31 2019 Fedora Release Engineering <releng@fedoraproject.org> - 2:2.0.0-47.rc4.1
- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
* Thu Nov 29 2018 Ondrej Holy <oholy@redhat.com> - 2:2.0.0-47.rc4
- Update to 2.0.0-rc4
* Mon Oct 15 2018 Simone Caronni <negativo17@gmail.com> - 2:2.0.0-46.20181008git00af869
- Enable Xtest option (#1559606).
* Mon Oct 15 2018 Simone Caronni <negativo17@gmail.com> - 2:2.0.0-45.20181008git00af869
- Update to last snapshot post 2.0.0-rc3.
* Mon Aug 20 2018 Simone Caronni <negativo17@gmail.com> - 2:2.0.0-44.rc3
- Update SPEC file.
* Sat Aug 04 2018 Mike DePaulo <mikedep333@fedoraproject.org> - 2:2.0.0-43.20180801.rc3
- Update to 2.0.0-rc3
* Fri Jul 13 2018 Fedora Release Engineering <releng@fedoraproject.org> - 2:2.0.0-42.20180405gita9ecd6a
- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
* Mon Apr 09 2018 Simone Caronni <negativo17@gmail.com> - 2:2.0.0-41.20180405gita9ecd6a
- Update to latest snapshot.