mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-07-03 22:04:59 +03:00
fix(clipboard): make CLIPRDR format-map growth checked (#15493)
* fix(clipboard): make CLIPRDR format-map growth checked The Windows CLIPRDR format-list handler relies on map_ensure_capacity() while processing peer-provided formats. The previous helper only attempted growth: if realloc() failed, it returned silently and the caller continued processing. A later iteration could then index past the allocated format_mappings array. Make format-map growth a checked operation. The handler now validates the peer-provided format count, ensures the mapping array is large enough before writing entries, and aborts processing if growth fails. Newly allocated slots are zeroed so existing cleanup can safely run after partial processing. Also bound remote format names before measuring/converting them. The chosen limits follow Windows clipboard/atom constraints: - registered clipboard format IDs use 0xC000..0xFFFF - string atom names are limited to 255 bytes Signed-off-by: fufesou <linlong1266@gmail.com> * fix(clipboard): reject invalid remote format-list entries Signed-off-by: fufesou <linlong1266@gmail.com> --------- Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
@@ -41,6 +41,14 @@
|
||||
|
||||
/* Maximum number of clipboard streams accepted from a remote peer (integer overflow / DoS guard) */
|
||||
#define WF_CLIPRDR_MAX_STREAMS 16384
|
||||
/* Registered clipboard formats use IDs 0xC000 through 0xFFFF.
|
||||
* https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerclipboardformatw */
|
||||
#define WF_CLIPRDR_MAX_FORMATS 0x4000u
|
||||
/* Registered format names are string atoms; cap the converted WCHAR name.
|
||||
* https://learn.microsoft.com/en-us/windows/win32/dataxchg/about-atom-tables */
|
||||
#define WF_CLIPRDR_MAX_FORMAT_NAME_WCHARS 255u
|
||||
/* Bound the peer-provided UTF-8 scan separately from the converted Windows name. */
|
||||
#define WF_CLIPRDR_MAX_FORMAT_NAME_UTF8_BYTES (WF_CLIPRDR_MAX_FORMAT_NAME_WCHARS * 4u)
|
||||
|
||||
/* Validates the remote descriptor array size after cItems has been read safely. */
|
||||
static BOOL wf_cliprdr_file_group_descriptor_size_valid(SIZE_T size, UINT count)
|
||||
@@ -61,6 +69,25 @@ static BOOL wf_cliprdr_file_group_descriptor_size_valid(SIZE_T size, UINT count)
|
||||
return size >= descriptors_size;
|
||||
}
|
||||
|
||||
static BOOL wf_cliprdr_bounded_strlen(const char *value, size_t max_len, size_t *len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (!value || !len)
|
||||
return FALSE;
|
||||
|
||||
for (i = 0; i <= max_len; i++)
|
||||
{
|
||||
if (value[i] == '\0')
|
||||
{
|
||||
*len = i;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clipboard Formats
|
||||
*/
|
||||
@@ -1406,25 +1433,35 @@ static UINT32 get_remote_format_id(wfClipboard *clipboard, UINT32 local_format)
|
||||
return local_format;
|
||||
}
|
||||
|
||||
static void map_ensure_capacity(wfClipboard *clipboard)
|
||||
static BOOL map_ensure_capacity(wfClipboard *clipboard, size_t capacity)
|
||||
{
|
||||
size_t old_size;
|
||||
formatMapping *new_map;
|
||||
|
||||
if (!clipboard)
|
||||
return;
|
||||
return FALSE;
|
||||
|
||||
if (clipboard->map_size >= clipboard->map_capacity)
|
||||
{
|
||||
size_t new_size;
|
||||
formatMapping *new_map;
|
||||
new_size = clipboard->map_capacity * 2;
|
||||
new_map =
|
||||
(formatMapping *)realloc(clipboard->format_mappings, sizeof(formatMapping) * new_size);
|
||||
if (!clipboard->format_mappings)
|
||||
return FALSE;
|
||||
|
||||
if (!new_map)
|
||||
return;
|
||||
if (capacity <= clipboard->map_capacity)
|
||||
return TRUE;
|
||||
|
||||
clipboard->format_mappings = new_map;
|
||||
clipboard->map_capacity = new_size;
|
||||
}
|
||||
if (capacity > WF_CLIPRDR_MAX_FORMATS ||
|
||||
capacity > ((size_t)-1) / sizeof(formatMapping))
|
||||
return FALSE;
|
||||
|
||||
old_size = clipboard->map_capacity;
|
||||
new_map =
|
||||
(formatMapping *)realloc(clipboard->format_mappings, sizeof(formatMapping) * capacity);
|
||||
|
||||
if (!new_map)
|
||||
return FALSE;
|
||||
|
||||
memset(new_map + old_size, 0, sizeof(formatMapping) * (capacity - old_size));
|
||||
clipboard->format_mappings = new_map;
|
||||
clipboard->map_capacity = capacity;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL clear_format_map(wfClipboard *clipboard)
|
||||
@@ -1451,6 +1488,13 @@ static BOOL clear_format_map(wfClipboard *clipboard)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static UINT wf_cliprdr_server_format_list_fail(wfClipboard *clipboard)
|
||||
{
|
||||
clear_format_map(clipboard);
|
||||
clipboard->copied = FALSE;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
static UINT cliprdr_send_tempdir(wfClipboard *clipboard)
|
||||
{
|
||||
CLIPRDR_TEMP_DIRECTORY tempDirectory;
|
||||
@@ -2443,6 +2487,16 @@ static UINT wf_cliprdr_server_format_list(CliprdrClientContext *context,
|
||||
|
||||
if (!clear_format_map(clipboard))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
clipboard->copied = FALSE;
|
||||
|
||||
if (formatList->numFormats > WF_CLIPRDR_MAX_FORMATS)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
if (formatList->numFormats > 0 && !formatList->formats)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
if (!map_ensure_capacity(clipboard, formatList->numFormats))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
clipboard->copied = TRUE;
|
||||
|
||||
@@ -2450,19 +2504,58 @@ static UINT wf_cliprdr_server_format_list(CliprdrClientContext *context,
|
||||
{
|
||||
format = &(formatList->formats[i]);
|
||||
mapping = &(clipboard->format_mappings[i]);
|
||||
/* Do not validate the peer-provided formatId as a Windows registered format.
|
||||
* It is only a remote protocol ID used when requesting data from the peer.
|
||||
* For named formats, RegisterClipboardFormatW creates the local Windows
|
||||
* clipboard ID below, and that local ID is checked before publishing. */
|
||||
mapping->remote_format_id = format->formatId;
|
||||
|
||||
if (format->formatName)
|
||||
{
|
||||
int size = MultiByteToWideChar(CP_UTF8, 0, format->formatName,
|
||||
strlen(format->formatName), NULL, 0);
|
||||
mapping->name = calloc(size + 1, sizeof(WCHAR));
|
||||
size_t name_len;
|
||||
int size;
|
||||
|
||||
if (mapping->name)
|
||||
if (!wf_cliprdr_bounded_strlen(format->formatName,
|
||||
WF_CLIPRDR_MAX_FORMAT_NAME_UTF8_BYTES, &name_len))
|
||||
{
|
||||
MultiByteToWideChar(CP_UTF8, 0, format->formatName, strlen(format->formatName),
|
||||
mapping->name, size);
|
||||
mapping->local_format_id = RegisterClipboardFormatW((LPWSTR)mapping->name);
|
||||
return wf_cliprdr_server_format_list_fail(clipboard);
|
||||
}
|
||||
|
||||
if (name_len == 0)
|
||||
{
|
||||
return wf_cliprdr_server_format_list_fail(clipboard);
|
||||
}
|
||||
|
||||
size = MultiByteToWideChar(CP_UTF8, 0, format->formatName, (int)name_len,
|
||||
NULL, 0);
|
||||
if (size <= 0)
|
||||
{
|
||||
return wf_cliprdr_server_format_list_fail(clipboard);
|
||||
}
|
||||
|
||||
if ((UINT)size > WF_CLIPRDR_MAX_FORMAT_NAME_WCHARS)
|
||||
{
|
||||
return wf_cliprdr_server_format_list_fail(clipboard);
|
||||
}
|
||||
|
||||
mapping->name = calloc((size_t)size + 1, sizeof(WCHAR));
|
||||
if (!mapping->name)
|
||||
{
|
||||
return wf_cliprdr_server_format_list_fail(clipboard);
|
||||
}
|
||||
|
||||
if (MultiByteToWideChar(CP_UTF8, 0, format->formatName, (int)name_len,
|
||||
mapping->name, size) != size)
|
||||
{
|
||||
free(mapping->name);
|
||||
mapping->name = NULL;
|
||||
return wf_cliprdr_server_format_list_fail(clipboard);
|
||||
}
|
||||
|
||||
mapping->local_format_id = RegisterClipboardFormatW((LPWSTR)mapping->name);
|
||||
if (mapping->local_format_id == 0)
|
||||
{
|
||||
return wf_cliprdr_server_format_list_fail(clipboard);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2472,7 +2565,6 @@ static UINT wf_cliprdr_server_format_list(CliprdrClientContext *context,
|
||||
}
|
||||
|
||||
clipboard->map_size++;
|
||||
map_ensure_capacity(clipboard);
|
||||
}
|
||||
|
||||
if (file_transferring(clipboard))
|
||||
|
||||
Reference in New Issue
Block a user