Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

Unix cache signals lose their structure argument and are rejected by AT-SPI

Chiusa
#818 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Una pull request collegata è già stata integrata.

  • #819 di @luccahuguet — integrata

Valutazione

Difficoltà
3/5
Tempo stimato
Mezza giornata
Idoneità per principianti
18/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
linux, rust
Ambito
desktop-dev

Direzione di ricerca

The bug is in the emit_cache_signal helper in adapters/unix/src/atspi/bus.rs, which passes the structure as the message body instead of wrapping it in a one-element tuple. The regression test from the issue, cache_signals_preserve_struct_arguments, needs a private bus, so run it with dbus-run-session -- cargo test --locked -p accesskit_unix ... -- --ignored. Done means the encoded SIGNATURE header matches the expected struct signatures. PR #819 already proposes this fix, so check it before starting.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

The Unix adapter emits AddAccessible and RemoveAccessible with flattened D-Bus signatures. Each signal should contain one structure argument, but the shared emit_cache_signal helper passes that structure directly as the message body.

Reproduced on upstream commit 466e24e252103f4d82bd6fbab98141b551e46e0c, using its unchanged lockfile: accesskit_unix 0.24.0, zbus 5.19.0, zvariant 5.15.0, and Rust 1.87.0 on x86_64 Linux.

Signal Expected wire signature Observed wire signature
AddAccessible ((so)(so)(so)iiassusau) (so)(so)(so)iiassusau
RemoveAccessible (so) so

GNOME AT-SPI 2.58.1 rejects the malformed AddAccessible signature; its RemoveAccessible handler likewise requires (so).

To reproduce, save the regression patch below as regression-only.patch and apply it to that upstream commit, then run:

dbus-run-session -- cargo test --locked -p accesskit_unix \
  cache_signals_preserve_struct_arguments -- --ignored

The test calls the production helper on a private bus and checks the encoded D-Bus SIGNATURE header and round-tripped values. It fails with the original helper and passes after the two-line correction below. Inspecting only zbus's parsed signature can hide the missing structure boundary.

The proposed correction in PR #819 is to pass &(body,) to emit_signal, with the generic bound changed from DynamicType to Type so the tuple can be serialized. No dependency or lockfile changes are needed.

We also verified the backported correction in our Venus terminal client on native Wayland with Orca 49.4 and AT-SPI 2.58.1. Previously Orca retained stale focus and reported “No selected text” after returning from scrollback despite a valid selection. With the correction it reported the exact selected text é 界 without a focus-repair action. Those native checks used accesskit_unix 0.22.1, zbus 5.18.0, and zvariant 5.13.1; current upstream was verified at the wire level.

regression-only.patch (test only; production helper unchanged)
diff --git a/adapters/unix/src/atspi/bus.rs b/adapters/unix/src/atspi/bus.rs
index 3368185..ca9857d 100644
--- a/adapters/unix/src/atspi/bus.rs
+++ b/adapters/unix/src/atspi/bus.rs
@@ -523,3 +523,74 @@ where
         Err(_) => Ok(default),
     }
 }
+
+#[cfg(all(test, not(feature = "tokio")))]
+mod tests {
+    use super::*;
+    use atspi::CacheItem;
+    use futures_lite::StreamExt;
+    use zbus::{MatchRule, MessageStream, message::Type};
+
+    fn assert_wire_signature(message: &zbus::Message, signature: &str) {
+        let bytes = message.data().bytes();
+        let length = bytes[12..16].try_into().unwrap();
+        let length = match bytes[0] {
+            b'l' => u32::from_le_bytes(length),
+            b'B' => u32::from_be_bytes(length),
+            _ => panic!("invalid D-Bus byte order"),
+        } as usize;
+        // Inspect the encoded header: zbus normalizes outer parentheses when reading it.
+        let mut field = vec![8, 1, b'g', 0, signature.len() as u8];
+        field.extend_from_slice(signature.as_bytes());
+        field.push(0);
+        assert!(
+            bytes[16..16 + length]
+                .windows(field.len())
+                .any(|w| w == field)
+        );
+    }
+
+    #[test]
+    #[ignore = "requires a private D-Bus session; run with dbus-run-session"]
+    fn cache_signals_preserve_struct_arguments() {
+        futures_lite::future::block_on(async {
+            let conn = Connection::session().await.unwrap();
+            let rule = MatchRule::builder()
+                .msg_type(Type::Signal)
+                .interface("org.a11y.atspi.Cache")
+                .unwrap()
+                .path(cache_path())
+                .unwrap()
+                .build();
+            let mut signals = MessageStream::for_match_rule(rule, &conn, Some(2))
+                .await
+                .unwrap();
+            let executor = Executor::new();
+            let bus = Bus {
+                conn: conn.clone(),
+                _task: executor.spawn(std::future::pending(), "unused"),
+                socket_proxy: SocketProxy::new(&conn).await.unwrap(),
+                desktop: Arc::new(OnceLock::new()),
+            };
+            let item = CacheItem::default();
+            bus.emit_cache_signal("AddAccessible", &item).await.unwrap();
+            let added = signals.next().await.unwrap().unwrap();
+            assert_eq!(added.header().member().unwrap().as_str(), "AddAccessible");
+            assert_wire_signature(&added, "((so)(so)(so)iiassusau)");
+            let (received,): (CacheItem,) = added.body().deserialize().unwrap();
+            assert_eq!(received, item);
+
+            bus.emit_cache_signal("RemoveAccessible", &item.object)
+                .await
+                .unwrap();
+            let removed = signals.next().await.unwrap().unwrap();
+            assert_eq!(
+                removed.header().member().unwrap().as_str(),
+                "RemoveAccessible"
+            );
+            assert_wire_signature(&removed, "(so)");
+            let (received,): (ObjectRefOwned,) = removed.body().deserialize().unwrap();
+            assert_eq!(received, item.object);
+        });
+    }
+}
Lingua principale
Rust
Stelle
1.5k
Fork
122
Merge medio
1h 7m
PR unite (30g)
18

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di AccessKit/accesskit

Tutte le issue di AccessKit/accesskit

Issue simili

Altre issue su Rust

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.