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
This commit is contained in:
Votre Nom 2026-05-16 11:48:14 +02:00
parent 58ef6a85c2
commit 095d7e5550
2 changed files with 185 additions and 0 deletions

View file

@ -0,0 +1,167 @@
# 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
<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
```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<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 :
```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).

View file

@ -84,6 +84,24 @@ if [[ ! -e /dev/fuse ]]; then
exit 3
fi
# Phase 13.1.c B.4 : warn si l'image est trop petite. L'image desktop par
# défaut fait ~680 Mo et sature dès qu'on tente `pkg update` (cf. session
# de la phase 13.1.b). Pour confort, recommandation : 4 Go minimum, idéal
# 10 Go. La grossir avec :
# qemu-img resize -f raw <image> 10G
# <redox-src>/build/fstools/bin/redoxfs-resize <image>
IMAGE_SIZE_BYTES=$(stat -c %s "$IMAGE" 2>/dev/null || echo 0)
MIN_IMAGE_SIZE=$((4 * 1024 * 1024 * 1024)) # 4 GiB
if [[ "$IMAGE_SIZE_BYTES" -gt 0 && "$IMAGE_SIZE_BYTES" -lt "$MIN_IMAGE_SIZE" ]]; then
image_size_human=$(numfmt --to=iec --suffix=B "$IMAGE_SIZE_BYTES" 2>/dev/null || echo "${IMAGE_SIZE_BYTES} octets")
echo "WARN : image $IMAGE de taille ${image_size_human} (< 4 GiB recommandés)" >&2
echo " Tu risques 'No space left on device' au moindre pkg update." >&2
echo " Pour la grossir :" >&2
echo " qemu-img resize -f raw $IMAGE 10G" >&2
echo " $REDOX_SRC/build/fstools/bin/redoxfs-resize $IMAGE" >&2
echo " (warning seulement, on continue)" >&2
fi
# --- 2. Mount ---
mkdir -p "$MOUNT"
echo "==> monter $IMAGE -> $MOUNT"