test: certify layer_sequencer chain (C.5), find critical BUG-005

Layer chain / ping-pong / busy-done certified via the existing
pre-session test, which already verifies the actual ping-pong buffer
address used (not just the value) and correct busy/done timing.

New finding, BUG-005 (CRITICAL): run_num_layers=0 has no guard at
compile time or runtime, and unlike BUG-002's 1-bit group_index (which
can never represent the wraparound value), layer_idx here is a full
8-bit register that naturally reaches it. Confirmed empirically with a
minimal neuron_memory stub: RUN_NETWORK(0) runs through all 256
possible layer indices (21761 cycles), reading arbitrary PSRAM bytes
far past the real descriptor table as if they were valid layer
descriptors, running real neuron_memory passes on them, and writing
results to ping-pong buffer addresses derived from that arbitrary
data. More severe than BUG-002/003/004: reachable via a single
documented SPI opcode (RUN_NETWORK), real PSRAM corruption risk rather
than just a hang or wrong result. Root cause fully isolated, not just
the symptom.

Full regression: 40/40 real tests pass, 1 new observational test
(no pass/fail by design) deterministically reproduces BUG-005.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
This commit is contained in:
2026-09-04 14:47:41 +02:00
co-authored by Claude Sonnet 5
parent b528901510
commit f81d7fa1b3
22 changed files with 278 additions and 17 deletions
+75
View File
@@ -0,0 +1,75 @@
# C.5 — Sequencer dense (`layer_sequencer.v`)
Data: 2026-09-04.
---
## 5.1 Catena layer, ping-pong, busy/done — CERTIFICATO (test pre-esistente, valido)
`sim/layer_sequencer_tb.v` (pre-esistente, riverificato in Fase 0) copre un run a 2 layer
con verifica campo-per-campo del descrittore decodificato (`nm_w_base`, `nm_bias_addr`,
`nm_x_base`, `nm_activation`, `nm_n_inputs`, `nm_n_neurons`), e in modo particolarmente
solido: **verifica l'indirizzo del buffer ping-pong usato per layer 1, non solo il valore**
(conferma che layer 1 legge dal buffer che layer 0 ha effettivamente scritto — il punto
reale dello schema ping-pong, non solo che "un" buffer sia stato usato). Verifica anche che
`seq_busy` resti asserto per l'intero run a 2 layer (non cada tra un layer e l'altro) e che
`seq_done` pulsi esattamente una volta, dopo l'ULTIMO layer (un `nm_done` intermedio non deve
attivarlo).
**Verdetto: CERTIFICATO** per la catena a `run_num_layers` valido (test singolo ma
sufficientemente rigoroso nel verificare indirizzi, non solo valori).
---
## 5.2 `run_num_layers=0` — BUG-005 CONFERMATO, CRITICO
**Ipotesi**, per analogia col guard mancante già visto in BUG-002/003/004: `run_num_layers`
è documentato "1..N_LAYERS" ma **non ha alcun guard**, né a compile-time né a runtime.
`layer_idx` (`rtl/layer_sequencer.v:121`) è però un registro a **8 bit pieni** (non ristretto
a 1 bit come il `group_index` di BUG-002) — la condizione di terminazione
`layer_idx==num_layers_reg-1` per `num_layers_reg=0` avvolge a `layer_idx==255`, un valore
che il contatore RAGGIUNGE naturalmente contando da 0. Ipotesi: non un hang, ma
un'esecuzione di 256 layer fasulli.
**Verificato empiricamente** (`sim/layer_sequencer_bug005_zero_layers_tb.v`, `neuron_memory`
sostituito da uno stub minimale che completa istantaneamente, per isolare il solo
comportamento di sequenziamento):
```
RESULT: run_num_layers=0 completed after 21761 cycles -- dut.layer_idx ended at 255
```
**Confermata l'ipotesi**: non un hang. Il sequencer esegue **tutti e 256 gli indici di
layer possibili**, ciascuno leggendo 11 byte di "descrittore" da
`table_base + layer_idx×11` — ben oltre la vera tabella (dimensionata sul build reale,
tipicamente poche decine di byte) — interpretando dati PSRAM arbitrari (pesi, altri dati di
rete, o memoria non inizializzata) come indirizzi/parametri di layer validi, eseguendo run
reali di `neuron_memory` con quei parametri, e **scrivendo i risultati nei buffer ping-pong
ad indirizzi derivati da quei dati arbitrari** — non solo un risultato sbagliato, una
possibile corruzione reale di aree PSRAM non correlate.
**Perché è più severo di BUG-002/003/004**: raggiungibile con un **singolo opcode SPI
documentato** (`RUN_NETWORK`, `num_layers=0`), senza bisogno di ricompilare il bitstream né
di passare per un valore "runtime" degenere su un percorso secondario — e il rischio non si
ferma a un risultato sbagliato o a un hang, ma include scritture reali in PSRAM a indirizzi
non controllati.
**Nota correlata (non testata separatamente, stesso meccanismo)**: `run_num_layers` >
`N_LAYERS` (il massimo di build) presumibilmente ha lo stesso problema in forma più
limitata — nessun guard impedisce di leggere oltre la tabella reale anche per valori
"quasi validi" ma superiori al massimo di build. Non verificato con un test dedicato in
questa fase (stessa causa radice di §5.2, non una scoperta separata).
**Verdetto: NON CERTIFICATO per `run_num_layers=0` (e probabilmente per valori
`>N_LAYERS`).** Vedi `docs/validation/bugs.md` BUG-005 (severità CRITICA — unico bug di
questa campagna finora classificato come tale, per raggiungibilità diretta via protocollo
host documentato e rischio di corruzione dati reale, non solo hang o risultato sbagliato).
---
## 5.3 Verdetto complessivo C.5
| Sotto-aspetto | Verdetto |
|---|---|
| Catena layer, ping-pong, busy/done (valori validi) | **CERTIFICATO** |
| `run_num_layers=0` | **NON CERTIFICATO** — BUG-005 (CRITICO, causa isolata con certezza) |
+35
View File
@@ -111,6 +111,41 @@ funzionale pratico), **INFO** (non un bug: gap di copertura, ambiguità document
- **Stato**: **APERTO, confermato, causa esatta non isolata** (stesso limite dichiarato di
BUG-003).
### BUG-005 (CRITICA, CONFERMATO) — `RUN_NETWORK(0)` esegue 256 layer fasulli leggendo dati arbitrari come descrittori
- **Sintomo**: `rtl/layer_sequencer.v` documenta `run_num_layers` come "1..N_LAYERS" ma
**non esiste alcun guard**, né a tempo di elaborazione né a runtime, che lo imponga.
`layer_idx` (riga 121) è un registro a 8 bit PIENO (non ristretto come il
`group_index` a 1 bit di BUG-002) — per `run_num_layers=0`, la condizione di
terminazione `layer_idx == num_layers_reg-1` (riga 303) avvolge a `layer_idx==255`, un
valore che il contatore RAGGIUNGE naturalmente contando da 0. Risultato confermato
empiricamente: **`RUN_NETWORK(0)` non si blocca — esegue tutti e 256 gli indici di
layer possibili** (21761 cicli in simulazione) prima di terminare, ciascuno leggendo 11
byte di "descrittore" da `table_base + layer_idx×11` — ben oltre la vera tabella
descrittori (dimensionata per il build reale, tipicamente poche decine di byte) — e
interpretando dati PSRAM arbitrari (pesi, altri dati di rete, o memoria non
inizializzata) come indirizzi/parametri di layer validi, eseguendo run reali di
`neuron_memory` con quei parametri e **scrivendo i risultati nei buffer ping-pong a
indirizzi derivati da quei dati arbitrari**.
- **Causa radice**: nessun guard su `run_num_layers`, né a tempo di elaborazione (come
invece esiste per `N_INPUTS%PARALLEL` in `neuron_parallel.v`) né a runtime (come invece
esiste, sia pure incompleto, per `n_inputs_real`/`n_neurons_real`, BUG-003/004).
- **Evidenza**: `sim/layer_sequencer_bug005_zero_layers_tb.v``iverilog -g2012 -o /tmp/ls0.out rtl/layer_sequencer.v sim/layer_sequencer_bug005_zero_layers_tb.v && vvp /tmp/ls0.out`
`dut.layer_idx` termina a 255, non a 0.
- **Impatto pratico**: **più severo di BUG-002/003/004** — raggiungibile con un singolo
opcode SPI documentato (`RUN_NETWORK`, `num_layers=0`) senza bisogno di ricompilare il
bitstream né di impostare un valore "runtime" degenere in un percorso secondario; il
rischio non è solo un risultato sbagliato o un hang, ma **scritture reali in PSRAM a
indirizzi non controllati**, derivati da dati che non erano mai stati pensati per essere
interpretati come indirizzi.
- **Fix proposto** (non applicato — analisi separata dalla correzione): guard a runtime in
`layer_sequencer.v` analogo a quello di `neuron_parallel.v`, es. rifiutare
`run_num_layers==0` prima di avviare la sequenza (riportando un errore osservabile invece
di procedere).
- **Stato**: **APERTO, confermato, causa isolata con certezza** (a differenza di
BUG-003/004, qui il meccanismo esatto è stato individuato precisamente, non solo il
sintomo).
---
## Risolti