Skip to content

GUACAMOLE-2263: Harden CLIPRDR channel against FreeRDP 3.x clipboard state machine.#661

Open
escra wants to merge 1 commit into
apache:staging/1.6.1from
ESCRA-GmbH:fix/cliprdr-freerdp3-hardening
Open

GUACAMOLE-2263: Harden CLIPRDR channel against FreeRDP 3.x clipboard state machine.#661
escra wants to merge 1 commit into
apache:staging/1.6.1from
ESCRA-GmbH:fix/cliprdr-freerdp3-hardening

Conversation

@escra
Copy link
Copy Markdown

@escra escra commented Apr 14, 2026

Summary

Six fixes for the clipboard redirection (CLIPRDR) plugin that prevent RDP
session disconnects when clipboard operations are performed with FreeRDP 3.x
clients.

FreeRDP 3.x has a significantly stricter CLIPRDR state machine than 2.x.
Several patterns that were harmless with FreeRDP 2.x now cause session
teardowns:

  1. Overlapping Format Data Requests - FreeRDP 3.x tracks pending requests
    internally and disconnects with error 1359 if a second request arrives
    before the first is answered. Fix: add request_pending flag with
    request_lock mutex.

  2. CB_RESPONSE_FAIL not checked - If the server responds with a failure
    flag, the old code would attempt to process the (NULL/garbage) data,
    causing a crash. Fix: check msgFlags & CB_RESPONSE_FAIL early.

  3. No response for unsupported formats - When the server requests an
    unsupported clipboard format, the old code silently drops the request.
    FreeRDP 3.x times out after 10 seconds and tears down the session. Fix:
    send CB_RESPONSE_FAIL for unsupported formats.

  4. NULL/empty data guard - requestedFormatData can be NULL or
    dataLen can be zero in edge cases. Fix: guard before processing.

  5. Stale pending requests - When a new Format List arrives (server
    clipboard content changed), any pending request for old data becomes
    stale. Fix: clear request_pending when a new Format List is received.

  6. Non-OK return codes treated as fatal - FreeRDP 3.x treats non-OK
    returns from CLIPRDR callbacks as fatal channel errors. Fix: all callbacks
    now return CHANNEL_RC_OK and log errors instead.

Related

Affected versions

  • staging/1.6.1 - with FreeRDP 3.x (default in Docker image)
  • main - same patterns exist

Test plan

  • Connect from FreeRDP 3.x client, perform rapid copy/paste operations
  • Copy unsupported clipboard format (e.g., image) on server, verify no disconnect
  • Verify clipboard works normally with FreeRDP 2.x (no regression)
  • Stress test: rapid clipboard changes while switching windows
  • Verify no error 1359 disconnects under load

Five fixes for the clipboard redirection (CLIPRDR) plugin that prevent
RDP session disconnects when clipboard operations are performed with
FreeRDP 3.x:

1. Add request_pending flag with request_lock mutex to prevent
   overlapping Format Data Requests that desync FreeRDP's internal
   state tracking (error 1359).
2. Check CB_RESPONSE_FAIL in format_data_response before processing
   data to prevent NULL pointer dereference on failed server responses.
3. Send FAIL response for unsupported formats in format_data_request
   instead of silently dropping — FreeRDP 3.x times out and tears down
   the session if no response arrives.
4. Guard against NULL or empty requestedFormatData in
   format_data_response to prevent crash on malformed PDUs.
5. Clear stale pending requests when a new Format List arrives.
6. All CLIPRDR callbacks now always return CHANNEL_RC_OK — FreeRDP 3.x
   treats non-OK returns as fatal channel errors that disconnect the
   entire session.

Related: FreeRDP/FreeRDP#11847
@escra escra changed the title Harden CLIPRDR channel against FreeRDP 3.x clipboard state machine. GUACAMOLE-2263: Harden CLIPRDR channel against FreeRDP 3.x clipboard state machine. Apr 14, 2026
Copy link
Copy Markdown
Contributor

@necouchman necouchman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code itself looks pretty solid, but I have a few things to clear up and/or change. Overall:

  • I'm concerned that several of the changes, here, overlap with #659, particularly as it relates to allocation of clipboard space. If I'm wrong about that, just let me know, but otherwise please keep those changes distinct.
  • You've removed comments from the code in several places, with no clear indication as to why. I've tried to put my review comments at each of those places, but please leave comments in-line with the code, unless there's a specific reason they should be removed. If they're no longer accurate, I'd suggest updating them rather than removing them.
  • Please make sure to update function documentation for any of the functions where the return value is changing - a couple of the functions always return CHANNEL_RC_OK, now, so that should be noted in the function documentation with an explanation as to why that is the case.

Finally, while I understand the overall goal of this pull request, and while I'm overall okay with it, I think it's worth noting that, in at least some cases, this will allow a connection to continue without clipboard support, and with no indication to the user as to why that's the case. I think it's probably okay because that's how it currently works under FreeRDP 2.x - it silently ignores clipboard errors that FreeRDP 3.x now handles more properly; however, it seems like one of those situations that could be really frustrating from a user experience point of view. Instead of the user connection getting shut down and having to go find out from the admin why it isn't working, the connection will continue but every time the user tries to copy/paste something, it will silently and inexplicably fail. I don't know if there's really anything to do about that, but figure it's worth noting.

Comment on lines +183 to +184
guac_client_log(clipboard->client, GUAC_LOG_WARNING, "CLIPRDR: "
"Failed to send capabilities (error %d).", status);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand the goal of this - to continue the connection even if clipboard redirection fails to sync up capabilities with the server. That said, shouldn't we at least still return CHANNEL_RC_OK, here, and not try to send a format list if we've failed to send capabilities?

Comment on lines -295 to -297
/* FreeRDP-specific handlers for CLIPRDR are not assigned, and thus not
* callable, until after the relevant guac_rdp_clipboard structure is
* allocated and associated with the CliprdrClientContext */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comment incorrect?

.msgFlags = CB_RESPONSE_OK
#endif
};
/* Report successful processing of format list */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comment not accurate?

Comment on lines +398 to +400
int output_buf_size = clipboard->clipboard->available;
const char* input = clipboard->clipboard->buffer;
char* output = guac_mem_alloc(GUAC_COMMON_CLIPBOARD_MAX_LENGTH);
char* output = guac_mem_alloc(output_buf_size);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this overlaps with #659, or is related to the discussion on that thread regarding allocating available * 2 or available * 4? If this is an overlap, and is already covered by the changes in that change request, then it should probably not be, here.

Comment on lines -400 to -401
/* Send received clipboard data to the RDP server in the format
* requested */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this comment being removed?


guac_mem_free(start);
return result;
return CHANNEL_RC_OK;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation for this function should be updated to indicate either that it always returns CHANNEL_RC_OK despite failures, or the situations where failures are returned vs. CHANNEL_RC_OK.

Comment on lines -453 to -455
/* FreeRDP-specific handlers for CLIPRDR are not assigned, and thus not
* callable, until after the relevant guac_rdp_clipboard structure is
* allocated and associated with the CliprdrClientContext */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this comment being removed?


void guac_rdp_clipboard_free(guac_rdp_clipboard* clipboard) {

/* Do nothing if the clipboard is not actually allocated */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why has this comment been removed?

if (clipboard == NULL)
return;

/* Free clipboard and underlying storage */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why has this comment been removed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants