Marvin Pascale

[B.Log]

26 Agosto 2026

Linux Kernel Panic

Linux Kernel Panic

Il server si blocca, la console si riempie di indirizzi esadecimali e l’unica frase comprensibile sembra essere:

Kernel panic - not syncing

A prima vista non è proprio il messaggio che vorremmo leggere su un sistema di produzione.

La reazione più naturale è riavviare la macchina, verificare che i servizi siano tornati disponibili e sperare che il problema non si ripresenti. Purtroppo, così facendo rischiamo di cancellare una parte importante delle prove.

Un kernel panic non è soltanto la fine improvvisa del sistema. È anche l’ultimo tentativo del kernel di raccontarci cosa stava succedendo.

Oggi mettiamo le mani in pasta e vediamo come preparare un sistema Linux alla raccolta dei crash dump, come leggere un vmcore e, soprattutto, come distinguere il punto in cui il kernel si è fermato dalla vera causa del problema.

Cos’è realmente un kernel panic?

Il kernel gestisce memoria, processi, filesystem, dispositivi, CPU, rete e praticamente tutto ciò che consente al sistema operativo di funzionare.

Quando incontra una condizione dalla quale non può recuperare in sicurezza, può decidere di fermare il sistema. Continuare l’esecuzione potrebbe peggiorare la situazione, corrompere dati o produrre comportamenti imprevedibili.

Tra le cause più comuni troviamo:

  • errori nei driver;
  • moduli kernel incompatibili;
  • dereferenziazioni di puntatori NULL;
  • accessi a memoria già liberata;
  • corruzione della memoria;
  • race condition;
  • deadlock e lockup;
  • problemi hardware;
  • errori sul filesystem o sul dispositivo di root;
  • configurazioni che richiedono esplicitamente un panic;
  • bug del kernel.

Non tutti gli errori del kernel provocano necessariamente un panic.

Un warning, un oops e un panic rappresentano situazioni differenti.

Un warning segnala una condizione anomala, ma normalmente il sistema continua a funzionare.

Un oops indica un errore serio avvenuto nel kernel. Il processo o il contesto coinvolto può essere terminato, mentre il resto del sistema tenta di proseguire.

Un panic, invece, blocca l’intero sistema perché il kernel considera impossibile continuare in modo affidabile.

La distinzione è importante perché un sistema che sopravvive a un oops potrebbe essere già compromesso. Continuare a utilizzarlo come se nulla fosse non è sempre una buona idea.

Prima del problema: prepariamoci al crash

La parte più importante dell’analisi avviene prima che il problema si presenti.

Senza una console remota, un sistema di raccolta dei log o un crash dump, potremmo ritrovarci con pochissime informazioni. In alcuni casi avremo soltanto una fotografia dello schermo, magari sfocata e incompleta.

Per un server importante dovremmo almeno predisporre:

  • una console seriale o una console remota IPMI, iDRAC, iLO o equivalente;
  • la persistenza del journal;
  • pstore, quando supportato;
  • kdump;
  • i simboli di debug dei kernel installati;
  • un sistema di monitoraggio hardware;
  • una procedura per copiare e conservare le prove.

Kdump utilizza kexec per avviare un kernel secondario, chiamato dump-capture kernel, quando il kernel principale va in panic. Una porzione di memoria viene riservata in anticipo, in modo che il secondo kernel possa avviarsi e salvare il contenuto della memoria del sistema in crash.

Il file ottenuto viene normalmente chiamato vmcore.

Il ruolo di crashkernel

Per permettere al kernel di raccolta di avviarsi, dobbiamo riservargli una quantità di memoria tramite il parametro crashkernel.

Possiamo verificare la riga di comando utilizzata dal kernel corrente con:

cat /proc/cmdline

Cerchiamo un parametro simile a uno dei seguenti:

crashkernel=512M

oppure:

crashkernel=auto

La sintassi supportata e la quantità di memoria necessaria dipendono dall’architettura, dalla distribuzione, dalla versione del kernel e dalla complessità del sistema.

Su macchine con molti dispositivi, grandi quantità di RAM, storage complesso o driver particolari, una riserva troppo piccola può impedire al kernel di raccolta di completare il salvataggio.

Dopo aver modificato la configurazione del bootloader è normalmente necessario rigenerarla e riavviare il sistema.

Su Debian e Ubuntu il parametro può essere aggiunto alle opzioni di GRUB, per esempio in:

/etc/default/grub

Successivamente:

sudo update-grub
sudo reboot

Su sistemi appartenenti alla famiglia Red Hat è possibile utilizzare grubby:

sudo grubby --update-kernel=ALL --args="crashkernel=512M"
sudo reboot

Questi sono esempi generali. Prima di applicarli in produzione bisogna verificare la procedura prevista dalla distribuzione e dal tipo di boot utilizzato.

Dopo il riavvio controlliamo che la memoria sia stata realmente riservata:

dmesg | grep -i crash

Possiamo anche verificare la presenza della riserva attraverso:

grep -i crash /proc/iomem

Installare e abilitare kdump

Su Debian e Ubuntu possiamo installare i componenti principali con:

sudo apt update
sudo apt install kdump-tools crash

Il file di configurazione più importante è normalmente:

/etc/default/kdump-tools

Verifichiamo che kdump sia abilitato:

USE_KDUMP=1

Poi controlliamo il servizio:

systemctl status kdump-tools

Su Red Hat Enterprise Linux, Rocky Linux, AlmaLinux e distribuzioni simili:

sudo dnf install kexec-tools crash

Il servizio può essere abilitato con:

sudo systemctl enable --now kdump

Controlliamo quindi lo stato:

systemctl status kdump

A seconda della distribuzione, la configurazione può trovarsi in file differenti, tra cui:

/etc/kdump.conf

Il dump può essere scritto sul disco locale, su un filesystem dedicato oppure trasferito attraverso la rete.

Salvare il vmcore sulla stessa unità che potrebbe aver causato il problema non è sempre la scelta migliore. Per sistemi critici può avere senso utilizzare uno storage separato o un server remoto.

Attenzione allo spazio disponibile

Un vmcore può essere molto grande.

La dimensione non coincide necessariamente con tutta la RAM installata, perché strumenti come makedumpfile possono escludere pagine non utili e comprimere il risultato. Tuttavia, su un server con centinaia di gigabyte di memoria, il dump può comunque occupare parecchio spazio.

Controlliamo la destinazione:

df -h /var/crash

Verifichiamo anche gli inode:

df -i /var/crash

Una configurazione perfetta diventa inutile se il filesystem destinato ai dump è pieno.

È inoltre opportuno definire:

  • una politica di conservazione;
  • un limite al numero di dump;
  • un sistema di copia verso uno storage esterno;
  • permessi adeguati;
  • eventuale cifratura.

Il vmcore può contenere informazioni sensibili presenti nella memoria del kernel e dei processi. Deve quindi essere trattato come un dato riservato.

Testare kdump

Configurare kdump senza provarlo significa scoprire se funziona soltanto durante un vero incidente.

Linux permette di provocare intenzionalmente un crash tramite Magic SysRq.

L’operazione deve essere eseguita esclusivamente su una macchina di test o durante una finestra di manutenzione autorizzata. Il sistema andrà immediatamente in crash.

Verifichiamo il valore corrente:

sysctl kernel.sysrq

Per abilitarlo temporaneamente:

sudo sysctl -w kernel.sysrq=1

Il panic di test può essere provocato con:

echo c | sudo tee /proc/sysrq-trigger

Dopo il riavvio dovremmo trovare un nuovo dump nella directory configurata, spesso:

/var/crash

Se il test fallisce, controlliamo:

journalctl -u kdump
journalctl -u kdump-tools
dmesg | grep -i kdump
dmesg | grep -i crash

Il nome del servizio dipende dalla distribuzione.

Dove cercare le prove

Dopo un crash dobbiamo raccogliere tutto ciò che può aiutarci a ricostruire la sequenza degli eventi.

Le fonti principali sono:

Console

La console mostra spesso:

  • tipo di eccezione;
  • CPU coinvolta;
  • processo corrente;
  • instruction pointer;
  • registri;
  • call trace;
  • moduli caricati;
  • flag di taint;
  • messaggio finale di panic.

Una console remota con registrazione automatica vale molto più di una fotografia scattata dopo il problema.

Journal del boot precedente

Dopo il riavvio possiamo esaminare i messaggi kernel del boot precedente:

journalctl -k -b -1

Per visualizzare la parte finale:

journalctl -k -b -1 -e

Se il journal non è persistente, i log del boot precedente potrebbero non essere disponibili.

Su sistemi con systemd-journald possiamo verificare o configurare la persistenza attraverso:

/etc/systemd/journald.conf

La direttiva interessata è:

Storage=persistent

Dopo la modifica:

sudo systemctl restart systemd-journald

Pstore

Pstore consente al kernel di conservare alcuni messaggi attraverso un reboot, utilizzando meccanismi persistenti messi a disposizione dalla piattaforma.

Controlliamo:

ls -lah /sys/fs/pstore

Potremmo trovare file contenenti parti della console o del dmesg precedente.

Pstore non sostituisce un vmcore, ma può salvarci quando kdump non era attivo o non è riuscito a completare il dump.

Directory dei crash

La destinazione più comune è:

/var/crash

All’interno possiamo trovare:

vmcore
vmcore-dmesg.txt
kexec-dmesg.log

Il contenuto esatto cambia tra le distribuzioni.

Prima di iniziare l’analisi è consigliabile copiare l’intera directory e calcolare un hash dei file:

sha256sum vmcore vmcore-dmesg.txt > SHA256SUMS

In questo modo possiamo lavorare su una copia, conservando l’originale.

Il dettaglio che non possiamo sbagliare: i simboli

Un vmcore contiene indirizzi e strutture presenti nella memoria del kernel al momento del crash.

Per trasformare questi indirizzi in nomi di funzioni, variabili, tipi e righe di codice servono i simboli di debug del kernel esatto che ha generato il dump.

Non basta usare un kernel con la stessa versione principale.

Devono corrispondere:

  • versione;
  • release;
  • architettura;
  • configurazione;
  • build;
  • simboli.

Il comando seguente mostra la versione del kernel in esecuzione:

uname -r

Nel caso di un dump proveniente da un’altra macchina, la versione può essere ricavata dai log o dagli strumenti di analisi.

Su Debian e Ubuntu i pacchetti dei simboli possono avere nomi come:

linux-image-VERSION-dbg
linux-image-unsigned-VERSION-dbgsym

Su Red Hat e derivate vengono normalmente utilizzati pacchetti debuginfo.

La posizione del file vmlinux varia. Alcuni percorsi frequenti sono:

/usr/lib/debug/boot/vmlinux-VERSION
/usr/lib/debug/lib/modules/VERSION/vmlinux

Attenzione a non confondere vmlinux con vmlinuz.

Vmlinuz è normalmente l’immagine compressa utilizzata per il boot.

Vmlinux è l’immagine ELF non compressa che può contenere i simboli necessari all’analisi.

Apriamo il vmcore con crash

La sintassi generale è:

crash /percorso/al/vmlinux /percorso/al/vmcore

Per esempio:

sudo crash \
  /usr/lib/debug/lib/modules/6.8.0-XX-generic/vmlinux \
  /var/crash/2026XXXX/vmcore

Se tutto corrisponde, crash mostra informazioni simili a queste:

KERNEL: /usr/lib/debug/lib/modules/6.8.0-XX-generic/vmlinux
DUMPFILE: /var/crash/2026XXXX/vmcore
CPUS: 8
DATE: ...
UPTIME: ...
LOAD AVERAGE: ...
TASKS: ...
PANIC: ...

La sessione interattiva utilizza il prompt:

crash>

Crash mette a disposizione comandi per analizzare log, processi, memoria, CPU, simboli, moduli, dispositivi, stack e strutture dati del kernel.

I primi comandi da eseguire

Iniziamo con una fotografia generale:

crash> sys

Questo comando ci aiuta a identificare:

  • kernel;
  • data del crash;
  • uptime;
  • panic string;
  • architettura;
  • CPU;
  • stato del dump;
  • eventuale taint.

Poi leggiamo il log:

crash> log

Per vedere i moduli caricati:

crash> mod

Per elencare i processi:

crash> ps

Per visualizzare i task per CPU:

crash> ps -S

Per controllare le run queue:

crash> runq

Per esaminare la memoria:

crash> kmem -i

Per mostrare il backtrace del task corrente:

crash> bt

Per includere i registri, quando supportato:

crash> bt -r

Un buon punto di partenza consiste nel salvare l’output dei comandi principali in un file di lavoro, in modo da poterlo confrontare e annotare.

Anatomia di un panic

Prendiamo un esempio semplificato:

BUG: kernel NULL pointer dereference, address: 0000000000000018
CPU: 3 PID: 2451 Comm: kworker/3:2
RIP: 0010:funzione_driver+0x142/0x4f0
CR2: 0000000000000018
Call Trace:
  funzione_driver+0x142/0x4f0
  funzione_precedente+0x50/0x120
  altra_funzione+0x90/0x180
Kernel panic - not syncing: Fatal exception

Ogni riga ci racconta qualcosa.

Tipo di errore

BUG: kernel NULL pointer dereference

Il kernel ha tentato di utilizzare un puntatore NULL o molto vicino a zero.

L’indirizzo 0x18 suggerisce spesso l’accesso a un campo situato a offset 0x18 all’interno di una struttura il cui puntatore di base valeva zero.

In termini semplificati, qualcosa di concettualmente simile a:

oggetto->campo

dove oggetto era NULL.

CPU, PID e Comm

CPU: 3 PID: 2451 Comm: kworker/3:2

L’errore è stato rilevato sulla CPU 3 mentre era in esecuzione il task indicato.

Questo non significa necessariamente che quel processo abbia causato il problema.

Se compare nginx, PostgreSQL o un altro servizio, non dobbiamo concludere che l’applicazione sia difettosa. Potrebbe aver semplicemente eseguito una system call o generato traffico che ha attraversato un percorso del kernel contenente un bug.

Nel caso di un kworker, il crash è avvenuto durante l’esecuzione di un worker del kernel.

RIP

RIP: 0010:funzione_driver+0x142/0x4f0

Su x86_64, RIP identifica l’istruzione eseguita al momento dell’eccezione.

In questo caso:

  • funzione_driver è la funzione;
  • 0x142 è l’offset all’interno della funzione;
  • 0x4f0 è la dimensione complessiva indicata per quel simbolo.

Questo è il punto in cui l’errore si è manifestato, ma non necessariamente quello in cui è nato.

Una corruzione della memoria può essere avvenuta secondi o minuti prima.

CR2

Su x86, il registro CR2 contiene l’indirizzo lineare che ha causato un page fault.

CR2: 0000000000000018

Se CR2 vale 0x18 e l’istruzione stava accedendo a un campo a offset 0x18, l’ipotesi del puntatore NULL diventa molto forte.

Call Trace

La call trace ricostruisce il percorso delle chiamate che ha portato al crash.

Dobbiamo leggerla distinguendo almeno tre zone:

  • il punto in cui il problema è stato rilevato;
  • il percorso funzionale che ha portato a quel punto;
  • le funzioni di gestione dell’eccezione e del panic.

Funzioni come panic, oops_end, exc_page_fault, die o crash_kexec descrivono spesso la gestione finale dell’errore. Non sono normalmente la causa iniziale.

La parte interessante è il primo frame appartenente al sottosistema o al modulo coinvolto.

Dal backtrace all’istruzione

Supponiamo che il crash sia avvenuto qui:

funzione_driver+0x142

Possiamo disassemblare la funzione:

crash> dis funzione_driver

Oppure chiedere informazioni intorno all’indirizzo:

crash> dis -l funzione_driver+0x142

Con simboli e informazioni di debug adeguate possiamo ottenere anche il file sorgente e la riga corrispondente.

Immaginiamo di trovare:

mov 0x18(%rax), %rdx

L’istruzione legge dalla memoria all’indirizzo contenuto in RAX più 0x18 e salva il risultato in RDX.

Controlliamo quindi i registri:

crash> bt -r

Se troviamo:

RAX: 0000000000000000

il processore ha tentato di leggere dall’indirizzo:

0x0 + 0x18 = 0x18

Abbiamo quindi spiegato il meccanismo immediato del crash.

Ma non abbiamo ancora trovato la root cause.

Il punto del crash non è sempre la causa

Questa è probabilmente la regola più importante di tutta l’analisi.

Una dereferenziazione NULL può dipendere da:

  • controllo mancante;
  • valore di ritorno non verificato;
  • errore di inizializzazione;
  • race condition;
  • oggetto rimosso da un’altra CPU;
  • percorso di errore incompleto;
  • struttura corrotta;
  • problema avvenuto molto prima.

Allo stesso modo, un puntatore apparentemente valido non garantisce che l’oggetto lo sia ancora.

Nel caso di un use-after-free, l’indirizzo può sembrare perfettamente plausibile. La memoria potrebbe contenere ancora parte dei vecchi dati oppure essere già stata riutilizzata per un altro oggetto.

Per questo motivo dobbiamo correlare:

  • istruzione;
  • registri;
  • sorgente;
  • strutture dati;
  • stato dei task;
  • moduli;
  • log precedenti;
  • modifiche recenti;
  • comportamento hardware.

Esaminare task e stack

Per visualizzare il contesto del task che ha generato il panic:

crash> bt

Per analizzare un PID specifico:

crash> bt 2451

Per cambiare contesto:

crash> set 2451

Poi possiamo eseguire nuovamente:

crash> bt

Per un backtrace più dettagliato:

crash> bt -f

Su alcune architetture e versioni, opzioni aggiuntive permettono di mostrare stack, registri e contenuti dei frame.

Dobbiamo prestare attenzione a:

  • funzioni ripetute;
  • ricorsione anomala;
  • stack molto profondo;
  • frame corrotti;
  • indirizzi non risolti;
  • moduli esterni;
  • passaggi tra contesto processo, interrupt e softirq.

Marcatori come IRQ, SOFTIRQ o NMI indicano che il flusso di esecuzione ha attraversato contesti particolari.

Un bug che avviene in interrupt context ha vincoli diversi da uno che avviene nel contesto normale di un processo. Per esempio, alcune operazioni che possono dormire non sono consentite in determinati contesti atomici.

Kernel tainted: quei caratteri contano

Nel log possiamo trovare una riga simile a:

Tainted: G OE

Il kernel utilizza i flag di taint per indicare condizioni che possono influenzare l’analisi.

Tra gli esempi troviamo:

  • moduli proprietari;
  • moduli esterni all’albero ufficiale;
  • moduli caricati forzatamente;
  • warning precedenti;
  • errori hardware;
  • kernel modificato;
  • staging driver.

La presenza di un taint non dimostra automaticamente che il modulo esterno abbia causato il crash.

È però un’informazione importante.

Se il backtrace attraversa proprio un modulo proprietario o out-of-tree, quel componente diventa uno dei primi elementi da verificare.

Per decodificare lo stato possiamo controllare:

cat /proc/sys/kernel/tainted

Il valore numerico è una maschera di bit. La documentazione del kernel descrive il significato dei diversi flag.

Esaminare i moduli

All’interno di crash:

crash> mod

Cerchiamo:

  • moduli non standard;
  • driver appena aggiornati;
  • moduli proprietari;
  • indirizzi vicini al RIP;
  • moduli presenti nella call trace;
  • versioni non allineate con il kernel.

Sul sistema o nella documentazione raccolta possiamo aggiungere:

modinfo nome_modulo

Verifichiamo versione, autore, firmware richiesto, alias e parametri.

Quando il problema compare dopo un aggiornamento del kernel, un modulo DKMS non ricostruito correttamente è un candidato ragionevole.

Soft lockup, hard lockup e hung task

Non tutti i crash nascono da un accesso invalido alla memoria.

Un soft lockup indica generalmente che una CPU è rimasta troppo a lungo nel kernel senza consentire al watchdog di essere eseguito.

Un hard lockup indica che una CPU non ha gestito nemmeno gli interrupt del watchdog per un periodo considerato eccessivo.

Un hung task segnala invece un task rimasto bloccato, spesso in stato non interrompibile, oltre la soglia prevista.

I messaggi possono contenere espressioni simili a:

watchdog: BUG: soft lockup
watchdog: Watchdog detected hard LOCKUP
INFO: task nome:PID blocked for more than N seconds

Queste condizioni non provocano sempre automaticamente un panic. Il comportamento dipende anche da parametri come:

kernel.softlockup_panic
kernel.hardlockup_panic
kernel.hung_task_panic

Se configurati, trasformano l’evento in un panic e permettono quindi a kdump di acquisire il vmcore.

In questi casi dobbiamo analizzare:

  • backtrace di tutte le CPU;
  • lock detenuti;
  • run queue;
  • interrupt;
  • task in stato D;
  • saturazione I/O;
  • driver coinvolti;
  • firmware;
  • eventuali problemi hardware.

Out Of Memory: non sempre è un kernel panic

Quando la memoria termina, Linux prova normalmente a recuperare la situazione attraverso l’OOM killer, terminando uno o più processi.

Nei log possiamo trovare:

Out of memory: Killed process ...

Questo, da solo, non è un kernel panic.

Il sistema può però essere configurato per andare in panic in caso di OOM:

sysctl vm.panic_on_oom

Se il valore è diverso da zero, il comportamento può cambiare in base alla configurazione.

Durante l’analisi controlliamo:

crash> kmem -i

e valutiamo:

  • memoria libera;
  • swap;
  • slab;
  • pagine riservate;
  • huge page;
  • zone di memoria;
  • frammentazione;
  • leak nel kernel;
  • consumo anomalo di un sottosistema.

Quando sospettare l’hardware

Non tutto ciò che produce una call trace è un bug software.

RAM difettosa, alimentazione instabile, CPU, controller storage, schede PCIe e firmware possono generare errori che si manifestano come corruzioni casuali.

Segnali sospetti:

  • crash in funzioni sempre diverse;
  • indirizzi incoerenti;
  • errori ECC;
  • Machine Check Exception;
  • problemi PCIe AER;
  • I/O error;
  • reset del controller;
  • filesystem che segnala corruzione;
  • errori contemporanei su più sottosistemi;
  • crash che aumentano sotto carico o con temperature elevate.

Controlliamo, quando disponibili:

journalctl -k | grep -Ei "mce|edac|ecc|aer|hardware error|i/o error"

Strumenti utili possono includere:

rasdaemon
mcelog
smartctl
nvme
ipmitool
edac-util

L’utilità effettiva dipende dall’hardware e dalla piattaforma.

Per i dischi possiamo esaminare i dati SMART:

sudo smartctl -a /dev/sdX

Per dispositivi NVMe:

sudo nvme smart-log /dev/nvme0

Su server dotati di BMC è opportuno controllare anche il System Event Log.

Correlare il crash con i cambiamenti

Prima di perderci nell’assembly, costruiamo una timeline.

Chiediamoci cosa è cambiato nelle ore o nei giorni precedenti:

  • aggiornamento del kernel;
  • nuovo driver;
  • aggiornamento firmware;
  • nuovo modulo DKMS;
  • sostituzione hardware;
  • modifica al bootloader;
  • nuovo carico applicativo;
  • variazione del traffico;
  • modifica storage o rete;
  • attivazione di eBPF;
  • nuovo antivirus o agente di sicurezza;
  • modifica dei parametri sysctl;
  • live patch;
  • aggiornamento dell’hypervisor.

Controlliamo la cronologia dei pacchetti.

Su Debian e Ubuntu:

less /var/log/apt/history.log
less /var/log/dpkg.log

Su sistemi RPM:

dnf history
rpm -qa --last

Se il problema è iniziato immediatamente dopo una modifica, non abbiamo ancora una prova, ma abbiamo una direzione investigativa molto più concreta.

Crash dentro una macchina virtuale

In una VM dobbiamo separare il punto di vista guest da quello dell’hypervisor.

Un kernel panic nel guest può dipendere da:

  • bug del kernel guest;
  • driver paravirtualizzato;
  • configurazione della VM;
  • hotplug;
  • ballooning;
  • storage virtuale;
  • problemi dell’host;
  • migrazione;
  • errore hardware sottostante.

Raccogliamo quindi sia il vmcore del guest sia:

  • log dell’hypervisor;
  • eventi della VM;
  • modifiche alla configurazione;
  • stato dello storage;
  • eventuali errori dell’host;
  • orario preciso del crash.

L’orologio del guest e quello dell’host devono essere correlabili. Senza una timeline comune diventa molto più difficile unire gli eventi.

Container e kernel panic

Un container condivide il kernel dell’host.

Se un workload containerizzato provoca un kernel panic, il problema riguarda il kernel dell’host, un suo modulo o un percorso attivato dal workload.

Il nome di un processo del container può comparire nel panic, ma non dobbiamo fermarci a quella informazione.

Raccogliamo:

  • runtime utilizzato;
  • versione;
  • cgroup;
  • namespace;
  • immagine;
  • capability;
  • dispositivi esposti;
  • moduli coinvolti;
  • networking;
  • storage driver;
  • eventi eBPF;
  • carico generato.

Un’applicazione in userspace non dovrebbe poter mandare in crash un kernel sano. Se un input non privilegiato causa un panic riproducibile, potremmo trovarci davanti a un vero bug del kernel e, in alcuni casi, a un problema di sicurezza.

Riprodurre senza distruggere la produzione

La riproducibilità trasforma un incidente isolato in un problema analizzabile.

Non ripetiamo però il test direttamente sul server di produzione.

Creiamo un ambiente che riproduca il più possibile:

  • versione esatta del kernel;
  • configurazione;
  • moduli;
  • firmware;
  • architettura;
  • numero di CPU;
  • memoria;
  • workload;
  • traffico;
  • filesystem;
  • storage;
  • parametri sysctl.

In laboratorio possiamo abilitare strumenti di debug più invasivi, tra cui:

  • KASAN;
  • KCSAN;
  • lockdep;
  • kmemleak;
  • slab debugging;
  • page poisoning;
  • fault injection;
  • ftrace;
  • perf;
  • bpftrace;
  • dynamic debug.

Questi strumenti possono avere un impatto significativo sulle prestazioni e non vanno attivati alla cieca in produzione.

Un metodo pratico di analisi

Quando mi trovo davanti a un kernel panic, cerco di seguire sempre lo stesso ordine.

1. Conservare le prove

Copio vmcore, log, pstore, configurazioni e informazioni hardware.

2. Stabilire la timeline

Individuo l’orario del crash e gli eventi precedenti.

3. Identificare il kernel esatto

Recupero versione, build e simboli corrispondenti.

4. Leggere il messaggio di panic

Classifico il problema: NULL pointer, page fault, lockup, MCE, BUG, OOM o altro.

5. Identificare il contesto

Controllo CPU, PID, Comm, interrupt context, taint e moduli.

6. Analizzare la call trace

Separo il percorso funzionale dalla gestione finale dell’eccezione.

7. Studiare l’istruzione

Correlando RIP, disassembly e registri capisco l’operazione che ha generato il fault.

8. Verificare le strutture dati

Controllo puntatori, oggetti, task, memoria, code e lock coinvolti.

9. Correlare software e hardware

Confronto il crash con modifiche, errori hardware e incidenti simili.

10. Formulare e verificare un’ipotesi

Una buona ipotesi deve spiegare tutte le prove, non soltanto una riga del backtrace.

Checklist per l’incidente

Durante la raccolta annotiamo almeno:

Data e ora:
Host:
Ambiente:
Versione kernel:
Architettura:
Uptime:
Tipo di panic:
CPU coinvolta:
PID e Comm:
RIP o PC:
Moduli nella call trace:
Kernel tainted:
Vmcore disponibile:
Simboli disponibili:
Pstore disponibile:
Modifiche recenti:
Errori hardware:
Riproducibile:
Workaround:
Root cause:
Correzione definitiva:

Questo piccolo schema evita di dimenticare informazioni importanti e rende molto più semplice confrontare incidenti successivi.

Errori da evitare

Riavviare senza raccogliere nulla

Può essere necessario per ripristinare il servizio, ma prima salviamo almeno console, pstore e log disponibili.

Usare simboli di un altro kernel

L’output può risultare errato o completamente fuorviante.

Accusare il processo indicato da Comm

È il task in esecuzione, non necessariamente il responsabile.

Fermarsi alla prima funzione della trace

Il punto di rilevamento non coincide sempre con l’origine della corruzione.

Ignorare i moduli esterni

Un driver proprietario o DKMS deve essere incluso nell’analisi.

Ignorare l’hardware

Un crash apparentemente software può essere provocato da RAM, PCIe, storage o alimentazione.

Applicare soltanto un workaround

Disabilitare una funzione o tornare al kernel precedente può ripristinare il servizio, ma la causa deve comunque essere compresa e documentata.

Conclusioni

Un kernel panic fa paura soprattutto quando non siamo preparati.

Con kdump configurato, simboli corretti e un metodo di analisi ordinato, quella schermata piena di indirizzi smette di essere rumore e diventa una sequenza di indizi.

Il messaggio di panic ci dice cosa è stato rilevato.

La call trace ci mostra il percorso seguito.

L’istruzione e i registri ci spiegano come è avvenuto il fault.

Le strutture dati, i log, l’hardware e la timeline ci aiutano a capire perché è avvenuto.

La differenza tra un semplice riavvio e una vera analisi sta tutta qui: non limitarsi a far ripartire il sistema, ma raccogliere abbastanza informazioni da evitare che il problema torni a trovarci nel momento peggiore.

Le opinioni in quanto tali sono opinabili e nulla ci vieta di approfondire l’argomento.


Le opinioni in quanto tali sono opinabili e nulla ti vieta di approfondire l’argomento.

Risorse: