Skip to content

fix: remove premature delete_persistent_permissions before backend validation#2001

Open
herrschmidt wants to merge 1 commit into
flatpak:mainfrom
herrschmidt:fix/consume-once-token
Open

fix: remove premature delete_persistent_permissions before backend validation#2001
herrschmidt wants to merge 1 commit into
flatpak:mainfrom
herrschmidt:fix/consume-once-token

Conversation

@herrschmidt

@herrschmidt herrschmidt commented May 12, 2026

Copy link
Copy Markdown

Problem

restore_token with persist_mode=2 (PERSISTENT) fails after logout/login — the portal dialog reappears on every session restart. The token works within a session, but not across sessions.

Reported in:

Root Cause

In xdp_session_persistence_replace_restore_token_with_data(), the restore_token is deleted from the PermissionStore before the backend (e.g. gnome-screencast) validates the restore data. The critical code is in src/xdp-session-persistence.c:232-237.

Fix

Remove the premature delete_persistent_permissions() call (3 lines). The delete is unnecessary because:

  1. On successful restore, Start() generates a fresh token
  2. generate_and_save_restore_token() already handles cleanup
  3. The PermissionStore entry is overwritten on next Start() anyway

Change: 3 lines removed from src/xdp-session-persistence.c.

Tested

  • ✅ Ubuntu 26.04, GNOME/Wayland, RustDesk 1.4.6
  • ✅ Within session: no dialog on reconnect
  • ✅ Across session (logout → login): no dialog, instant connection
  • ✅ Build: 138/138 successful

Related

@Sodivad

Sodivad commented May 19, 2026

Copy link
Copy Markdown
Collaborator

Can you expand more on why things fail?

In xdp_session_persistence_replace_restore_token_with_data(), the restore_token is deleted from the PermissionStore before the backend (e.g. gnome-screencast) validates the restore data.

How does this cause restoration to fail?

…validation

The original code deleted the restore_token from the PermissionStore
in replace_restore_token_with_data() BEFORE the backend validated the
restore data. If the backend failed to restore (e.g. monitor mismatch
after logout/login on GNOME/Wayland), the token was already consumed
and no new token was generated — persist_mode=2 silently degraded.

This fix moves the deletion to after the backend response:

1. replace_restore_token_with_data(): Look up token but DON'T delete.
   Pass restore_data to the backend for validation.

2. replace_restore_data_with_token(): After backend responds:
   - If restore_data returned: generate_and_save_restore_token()
     overwrites the old token with the same or a new one.
   - If restore_data NOT returned (e.g. persist_mode=NONE):
     delete the persistent permission here (new else-branch cleanup).

Previously the else-branch only freed the pointer, relying on the
now-removed early delete for cleanup. The updated else-branch now
calls delete_persistent_permissions() to properly clean up when the
backend does not return restore_data.

Fixes: flatpak#1862
Related: rustdesk/rustdesk#9435, flatpak#1371
@herrschmidt
herrschmidt force-pushed the fix/consume-once-token branch from b606237 to b401f1b Compare May 21, 2026 07:11
@herrschmidt

herrschmidt commented May 21, 2026

Copy link
Copy Markdown
Author

Hi, this is the failure mechanism:

The bug is a two-stage consume-before-validate race:

Stage 1 — Token consumed prematurely:
When a client calls SelectSources(persist_mode=2, restore_token="<uuid>"), the function replace_restore_token_with_data() in src/xdp-session-persistence.c does three things:

  1. Looks up the token in the PermissionStore (GVDB database)
  2. Immediately deletes it (the delete_persistent_permissions() call on line 234)
  3. Passes the restore_data to the backend for validation

Stage 2 — Backend validation fails (silently):
The backend (e.g. xdg-desktop-portal-gnome's screencast implementation) calls restore_stream_from_data()find_monitor_by_string(). This function matches a monitor identifier string against currently connected monitors. After a logout/login cycle:

  • The GPU re-initializes
  • Display connectors are re-enumerated
  • Monitor identifier strings can change (different connector ID assignment, different EDID path)

So find_monitor_by_string("SAM:Q95A:0x01000e00") fails because the monitor is now "SAM:Q95A:0x01001100" or similar. The restore fails, the backend shows the "Select screen" dialog, and the user has to manually confirm.

Why this breaks persistence across sessions:

  • Token was deleted in Stage 1 ✓
  • Restore failed in Stage 2 ✗
  • A new token is only generated when the session is successfully started AND closed (generate_and_save_restore_token() runs on session.close())
  • Since the session wasn't successfully restored (dialog appeared instead), no new token is written
  • persist_mode=2 silently degrades to NONE

Within a single session, this works because the monitor is still connected with the same identifier — find_monitor_by_string() succeeds, the session is properly restored, a fresh token is generated, and the cycle continues.

The fix: The delete is moved to after the backend response. If the backend successfully restores, the old token entry is simply overwritten. If it fails (or persist_mode=NONE is used), the cleanup happens in replace_restore_data_with_token()'s else-branch. I've updated the PR with this approach, it now properly handles both paths.

@swick

swick commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

I think this change makes sense, /ping @whot @jadahl

@whot whot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Note on the commit message: xdp doesn't use conventional commits, better teach the agent that.

xdp_session_persistence_delete_persistent_permissions (session,
table,
*in_out_restore_token);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

ok, probably too niche to worry about but: if the restore_data comes from the transient permissions we now pass the token into persistent permissions to delete associated data. Assuming the token is unique enough this shouldn't be an issue but feels off anyway.

Also not immediately sure why the same issue doesn't affect transient permissions but I've paged much of this out by now.

@swick

swick commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Re-reading this, it also seems problematic that portal-gnome doesn't seem to handle the restore token correctly in find_monitor_by_string. Can you please report this as well?

Anyway, the commit message needs updating (right prefix, line length) and the comments are too verbose, the typical AI slop.

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.

4 participants