# Bug B.2 — Rapport pour upstream Redox ion > Document produit le 2026-05-16 dans le cadre de > [`phase13-1-c-cursor.md`](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 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 ```sh # 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` : ```rust 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> ?) — à 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`. 3. Patch. ## Workaround utilisateur en attendant Toujours quitter les clients background **avant** le serveur : ```sh # 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).