tawasuyu

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1572Shell 203Rust 109Markdown 993728 Sutichaykuna

🎯 portal-probe: omitir `persist_mode` MATA al portal — por eso Start no contestaba nunca

Causa raíz de que ScreenCast se quedara colgado en Start, medida en metal (OptiPlex 3060).
No era el EGL por software de QEMU: en metal, con iris por hardware, pasaba exactamente
lo mismo. Esa hipótesis queda falsificada.

Lo que pasa de verdad, con cuatro segfaults reproducibles (uno por intento, todos `at 0`
y todos en el MISMO offset de libc, 0x34f64 = `strlen`):

  1. el cliente no manda `persist_mode` ⇒ el portal asume PERSIST_MODE_NONE
  2. el backend de COSMIC devuelve `restore_data` de todos modos
  3. xdg-desktop-portal 1.18.4 entra por la rama NONE de
     xdp_session_persistence_generate_and_save_restore_token, que hace
     `g_clear_pointer(in_out_restore_token, g_free)` — token = NULL
  4. a la vuelta, el llamador hace `g_variant_new_string(*in_out_restore_token)` SIN
     comprobar nada ⇒ glib llama strlen(NULL) ⇒ SIGSEGV

El portal muere JUSTO ANTES de emitir el Response. Desde el cliente eso se ve como «Start
aceptado y nunca contesta», que es indistinguible de un backend que se cuelga — y nos
mandó a buscar el problema en la GPU durante meses.

Con persist_mode >= 1 el token se genera (uuid) y no hay nulo. Es un fallo de aguas
arriba; esto lo esquiva desde el cliente sin tocar el portal. Por defecto 1 (transitorio),
que es lo que quiere cualquiera que sólo va a capturar una vez.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

a5c959b6186acef13260cf6b42aa1cbfc1d56f34 · ruwaq sergio · (2 killa ñawpaq) · tayta 8182e459b · kay commitpi sach'ata qhaway

Semántico sayk'uy · 2

tools/portal-probe/portal-probe.c

1 qillqakuna · +17 −5

diff --git a/tools/portal-probe/portal-probe.c b/tools/portal-probe/portal-probe.c
index cf2e5a7f..f817e3bd 100644
--- a/tools/portal-probe/portal-probe.c
+++ b/tools/portal-probe/portal-probe.c
@@ -26,7 +26,7 @@
  * el path para recién ahí hacer `AddMatch`, el portal puede haber emitido ya la señal y se pierde
  * para siempre. Es una carrera real, no teórica.
  *
- * Uso:  portal-probe screencast [--multiple] [--window] [--wait N]
+ * Uso:  portal-probe screencast [--multiple] [--window] [--wait N] [--persist 0|1|2]
  *       portal-probe filechooser [--save]
  *       portal-probe screenshot [--wait N]
  *       portal-probe version           (qué interfaces sirve el portal, y en qué versión)
@@ -246,7 +246,7 @@ static int g_wait_s = 60;
 
 /* ── ScreenCast: el handshake de CUATRO pasos ─────────────────────────────────────────────────── */
 
-static int do_screencast(DBusConnection *bus, int multiple, int window)
+static int do_screencast(DBusConnection *bus, int multiple, int window, int persist)
 {
     const char *IF = "org.freedesktop.portal.ScreenCast";
     char tok[64], path[512], session[256] = {0};
@@ -286,6 +286,17 @@ static int do_screencast(DBusConnection *bus, int multiple, int window)
         opt_u32(&arr, "types", window ? 2 : 1);
         opt_bool(&arr, "multiple", multiple ? TRUE : FALSE);
         opt_u32(&arr, "cursor_mode", 2);   /* 1=oculto 2=embebido 4=metadatos */
+        /* persist_mode NO es un lujo: OMITIRLO CUELGA EL PORTAL. Medido en metal el 2026-08-06.
+         * Sin esta opción el portal asume PERSIST_MODE_NONE; el backend de COSMIC devuelve
+         * `restore_data` igual, y xdg-desktop-portal 1.18.4 entra por la rama NONE de
+         * xdp_session_persistence_generate_and_save_restore_token, que hace
+         * `g_clear_pointer(in_out_restore_token, g_free)` — deja el token en NULL — y a la vuelta
+         * el llamador hace `g_variant_new_string(*in_out_restore_token)` SIN comprobarlo.
+         * glib llama strlen(NULL) y el portal muere con SIGSEGV justo antes de emitir el Response.
+         * Desde el cliente se ve como «Start aceptado y nunca contesta»: el proceso que debía
+         * responder ya no existe. Con persist_mode >= 1 el token se genera (uuid) y no hay nulo.
+         * Es un fallo de aguas arriba; esto lo esquiva sin tocar el portal. */
+        opt_u32(&arr, "persist_mode", (dbus_uint32_t)persist);
         dbus_message_iter_close_container(&it, &arr);
         if (!call_request(bus, m, "SelectSources")) return 1;
     }
@@ -413,12 +424,13 @@ int main(int argc, char **argv)
     setvbuf(stdout, NULL, _IOLBF, 0);
 
     const char *cmd = argc > 1 ? argv[1] : "version";
-    int multiple = 0, window = 0, save = 0;
+    int multiple = 0, window = 0, save = 0, persist = 1;  /* 0=none 1=transitorio 2=persistente */
     for (int i = 2; i < argc; i++) {
         if (!strcmp(argv[i], "--multiple")) multiple = 1;
         else if (!strcmp(argv[i], "--window")) window = 1;
         else if (!strcmp(argv[i], "--save")) save = 1;
         else if (!strcmp(argv[i], "--wait") && i + 1 < argc) g_wait_s = atoi(argv[++i]);
+        else if (!strcmp(argv[i], "--persist") && i + 1 < argc) persist = atoi(argv[++i]);
     }
 
     DBusError err; dbus_error_init(&err);
@@ -472,11 +484,11 @@ int main(int argc, char **argv)
         probe_version(bus, "org.freedesktop.portal.Access");
         probe_version(bus, "org.freedesktop.portal.RemoteDesktop");
         rc = 0;
-    } else if (!strcmp(cmd, "screencast"))   rc = do_screencast(bus, multiple, window);
+    } else if (!strcmp(cmd, "screencast"))   rc = do_screencast(bus, multiple, window, persist);
     else if (!strcmp(cmd, "filechooser"))    rc = do_filechooser(bus, save);
     else if (!strcmp(cmd, "screenshot"))     rc = do_screenshot(bus);
     else {
-        fprintf(stderr, "uso: portal-probe {version|screencast|filechooser|screenshot} [--multiple|--window|--save] [--wait N]\n");
+        fprintf(stderr, "uso: portal-probe {version|screencast|filechooser|screenshot} [--multiple|--window|--save] [--wait N] [--persist 0|1|2]\n");
         rc = 2;
     }
     printf("\n== portal-probe %s: %s ==\n", cmd, rc == 0 ? "OK" : "FALLÓ");