redox-wayland-compositor/docs/phase13-1-c-ion-bug-b2-upstream.md
Votre Nom 095d7e5550 Phase 13.1.c — rapport upstream bug B.2 ion + warn image étroite (B.4)
B.2 (ion page fault) : pas fixable dans ce repo. Source d'ion clonée
depuis upstream et inspectée : pas de commit récent visant ce pattern
de crash, binaire stripped sur l'image (impossible de mapper RIP=0x2335ae
à une fonction sans non-stripped build). Rapport upstream-ready rédigé
dans docs/phase13-1-c-ion-bug-b2-upstream.md, à coller sur
gitlab.redox-os.org/redox-os/ion quand on aura un compte.

Bonus diagnostic noté : warning relibc « Cancellation for unknown id »
apparaît juste avant le crash ion, peut être lié à un side-effect dans
procmgr (à investiguer côté relibc aussi).

Workaround documenté pour utilisateurs : quitter les clients background
AVANT le serveur Wayland (ESC sur le client, puis Ctrl+Q sur le compo).

B.4 (image étroite) : run-qemu.sh warn maintenant si harddrive.img <
4 GiB, avec les commandes de resize prêtes à copier-coller. Continue
sans bloquer (image actuelle de l'utilisateur fait 10 GiB, no-op pour
notre setup, mais utile pour quelqu'un qui clone le repo et utilise
l'image desktop par défaut de Redox.)

Leyoda 2026 – GPLv3
2026-05-16 11:48:14 +02:00

6.2 KiB

Bug B.2 — Rapport pour upstream Redox ion

Document produit le 2026-05-16 dans le cadre de phase13-1-c-cursor.md. À copier-coller (en traduisant en anglais) dans une issue sur gitlab.redox-os.org/redox-os/ion/-/issues quand on aura un compte.


Title

ion: page fault 0x70 when a background job dies on broken pipe (Wayland client + compositor scenario)

Environment

  • Redox image : harddrive.img du 2026-05-08 (build x86_64 desktop, rebuild depuis redox-src la veille)
  • ion : version embarquée dans cette image (binaire stripped, source upstream synced via cookbook recipe core/ion)
  • QEMU : qemu-system-x86_64 avec -enable-kvm -cpu host -k fr sur Linux host (CachyOS 7.0.8)
  • Repro : 100 % reproductible sur 3 sessions distinctes avec reboot frais entre chaque

Symptôme

Quand un job background termine sur Broken pipe (ex: client Wayland dont le compositor vient de fermer son socket), ion crashe avec un page fault à l'adresse 0x70. Le kernel logue UNHANDLED EXCEPTION pour le process ion. Le shell devient indisponible jusqu'au prochain login.

ion: ([Page fault: 0000000000000070 US
RFLAG: 0000000000010212
CS:    000000000000002b
RIP:   00000000002335ae
RSP:   0000000000bb0e60
SS:    0000000000000023
FSBASE  000000000092b000
GSBASE  0000000000000000
KGSBASE ffff80007fed8000
RAX:   0000000000000070       <-- valeur fautive = offset du field
RCX:   0000000000000000
RDX:   0000000000233550
RDI:   0000000000000001
RSI:   00000000006c9970
R8:    0000000000000000
R9:    0000000000000000
R10:   0000000000000000
R11:   0000000000000008
RBX:   000000000092b000
RBP:   0000000000bb0e70
R12:   000000000092acc8
R13:   00000000006afe20
R14:   0000000000000001
R15:   00000000006afe20
  FP 0000000000bb0e70: PC 0000000000f27745
  FP 0000000000bb0f40: PC 0000000000f0a167
  FP 0000000000bb0f70: PC 0000000000f0a02d
  FP 0000000000bb0ff0: PC 0000000000eabd43
  <Invalid next frame pointer 0x0000000000000000; stack walk ended>
  FP ffff80001798fe80: PC ffffffff8008a42d
    kernel::arch::x86_shared::interrupt::exception::page::inner
  FP ffff80001798ff50: PC ffffffff80087ee7
    kernel::arch::x86_shared::interrupt::exception::page
  0000000000bb0e70: GUARD PAGE
kernel::context::signal:INFO -- UNHANDLED EXCEPTION, CPU #3, PID 45, NAME /usr/bin/ion

Pattern : déréférence d'un pointer null avec accès au champ à l'offset 0x70 (RAX=0x70, addr=0x70).

Repro minimal

# Dans une session ion fraîche, avec le compositor Wayland
# (https://gitlab.com/leyoda/redox-wayland-compositor) :
redox-wl-compositor &              # backround job [0]
sleep 1
redox-wl-real-client-simple-window &   # background job [1]
sleep 2
# Maintenant, tuer le compositor SANS quitter le client proprement.
# Le compositor ferme son socket, le client reçoit Broken pipe et exit
# avec status non-zéro. C'est en traitant cette terminaison que ion crashe.
pkill redox-wl-compositor

Reproductible aussi avec n'importe quelle paire client-serveur Unix :

  • serveur en bg qui ferme son socket
  • client en bg qui écrit dessus et reçoit EPIPE

Hypothèse de localisation (best-effort sans symboles)

Le crash semble survenir dans BackgroundEventCallback. La closure dans src/main.rs:224-234 :

shell.set_background_event(Some(Arc::new(|njob, pid, kind| match kind {
    BackgroundEvent::Added => eprintln!("ion: bg [{}] {}", njob, pid),
    BackgroundEvent::Stopped => eprintln!("ion: ([{}] {}) Stopped", njob, pid),
    BackgroundEvent::Resumed => eprintln!("ion: ([{}] {}) Running", njob, pid),
    BackgroundEvent::Exited(status) => {
        eprintln!("ion: ([{}] {}) exited with {}", njob, pid, status)
    }
    BackgroundEvent::Errored(error) => {
        eprintln!("ion: ([{}] {}) errored: {}", njob, pid, error)
    }
})));

Appelée depuis src/lib/shell/pipe_exec/job_control.rs:148-228 (watch_background), elle-même runs depuis un thread dédié au polling de waitpid.

Note : sur la sortie observée, le message "exited with" est imprimé AVANT le page fault :

[real-client] FAIL: Backend error: Io error: Broken pipe (os error 32)
root:~# [src/procmgr.rs:299 WARN] Cancellation for unknown id Id(26)
root:~#
ion: ([Page fault: 0000000000000070 ...

Donc le callback aurait fini son eprintln!, le crash arrive juste après — possiblement dans le code qui suit, peut-être le get_process!(|process| { process.forget(); ... }) ou dans le drop du job, ou dans le retour de thread vers le main thread.

Disassembly stripped autour de RIP=0x2335ae

2335a2: 48 8b 85 60 fe ff ff   mov    -0x1a0(%rbp), %rax
2335a9: 48 d1 e0               shl    $1, %rax
2335ac: 48 85 c0               test   %rax, %rax
2335af: 74 0d                  je     0x2335be

Pattern Vec/Box drop : "test capacity*2, jump-if-zero, else free pointer". Le 0x2335ae tombe au milieu de l'instruction test — possible que le strip + offset stable ne pointe pas exactement à la même instruction sur l'image installée vs le rebuild local. RAX=0x70 au crash suggère un load mov [rdi+0x70], rax (ou similaire) où rdi est null. Le champ à l'offset 0x70 d'une struct Rust commune (BackgroundProcess ? File ? Mutex<Vec<...>> ?) — à confirmer avec un build debug.

Side-bug observé en parallèle

Sur la même session, juste avant le crash :

[src/procmgr.rs:299 WARN] Cancellation for unknown id Id(26)

Ce warning vient de relibc (procmgr), pas d'ion. Peut être lié — une cancellation de syscall qui laisse un state inconsistent, qu'ion manipule ensuite. À investiguer côté relibc aussi.

Demande

  1. Identifier la fonction à 0x2335ae (rebuild non-stripped + addr2line).
  2. Confirmer si c'est un null deref dans process.forget(), drop de BackgroundProcess, ou la déstructure du Vec<BackgroundProcess>.
  3. Patch.

Workaround utilisateur en attendant

Toujours quitter les clients background avant le serveur :

# Bon ordre :
redox-wl-real-client-simple-window &
# (interagir)
fg %1   # ramener au foreground
# ESC ou autre pour quitter proprement
pkill redox-wl-compositor   # seulement après que tous les clients sont sortis

Référence

Bug détecté pendant le port d'un compositor Wayland minimal sur Redox : https://gitlab.com/leyoda/redox-wayland-compositor (phase 13.1.b / 13.1.c).