Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
gosentry — Toolchain Go orientata alla sicurezza, focalizzata su capacità di fuzzing all'avanguardia. | Kitploit
Strumenti/GitHubGitHub/trailofbits/gosentry
Analisi StaticaAnalisi Dinamica (Sandboxing)Analisi delle VulnerabilitàAnalisi del CodiceFuzzingDevSecOpsAnalisi di BinariApprendimento e Formazione
GitHubtrailofbits/gosentry

gosentry

Toolchain Go orientata alla sicurezza, focalizzata su capacità di fuzzing all'avanguardia.

Vedi Repository
11739 giorni faRevisionato da Kitploit

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

gosentry

integration tests

gosentry è un fork della toolchain Go incentrato sulla sicurezza, che integra numerose funzionalità per campagne di fuzzing all'avanguardia su codebase Go. Se prima usavi go test -fuzz, dovresti usare gosentry come sostituto. Include vari miglioramenti per il fuzzing e rilevatori di bug che non sono presenti nativamente nella toolchain Go. Vedi TLDR; qui sotto. Puoi anche leggere l'articolo del blog associato qui.

TLDR (funzionalità e opzioni):

  • Esegui il fuzzing direttamente su input di tipo struct (nessun parser personalizzato necessario). Aggiungi seed con f.Add(Input{N: 7, S: "hi"}) e poi f.Fuzz(func(t *testing.T, in Input) { ... }).
  • Va in panic in caso di overflow di interi e rileva problemi aritmetici
  • Fuzza con LibAFL per tecniche di fuzzing all'avanguardia come la risoluzione dei vincoli di percorso
  • Genera/muta input a partire da una grammatica per evitare mutazioni inutili. La mutazione genera un'operazione matematica valida, ad esempio X + Y - Z può diventare X / U + Z - 14 invece di X + Yè - Z
  • Panic su funzioni selezionate (come i logger di errori critici) e crash quando viene chiamata
  • Concentra il fuzzer sulle righe modificate di recente E sulla nuova copertura, per mirare principalmente ai nuovi commit
  • Rileva le data race in fase di fuzzing
  • Rileva i leak di goroutine in fase di fuzzing
  • Rileva esecuzioni bloccate con timeout in fase di fuzzing
  • Genera un report di copertura HTML dal corpus di una campagna di fuzzing con un'unica CLI

Sommario

  • Compilazione
    • Funzionalità 1: Fuzzing consapevole delle struct (fuzz di struct come input)
    • Funzionalità 2: Rilevamento di overflow di interi e problemi di troncamento
    • Funzionalità 3: Panic su funzioni selezionate
    • Funzionalità 4: Fuzzing all'avanguardia con LibAFL
    • Funzionalità 5: Fuzzing orientato al Git blame (sperimentale)
    • Funzionalità 6: Rileva race condition, leak di goroutine e blocchi in fase di fuzzing
    • Funzionalità 7: Fuzzing basato su grammatiche (Nautilus)
    • Funzionalità 8: Genera report di copertura del fuzzing dalla campagna
  • Trofei
  • Crediti

Compilazione```bash

cd src && ./make.bash # Produces ../bin/go. See GOFLAGS below.

root@kitploit:~
> [!TIP]
> Documentazione per contributori:  leggi `docs/gosentry/index.md` per una mappa del codice, il ciclo di sviluppo consigliato, i punti di ingresso della CI e gli script di benchmark.
> Questo fork utilizza la Pull GitHub App per aprire e unire automaticamente le PR da `golang/go:master` in `master`, assicurandoci di non rimanere mai indietro rispetto agli ultimi aggiornamenti della toolchain Go.

## Funzionalità 1: Fuzzing consapevole delle struct (fuzz di struct come input)

#### Panoramica

Il fuzzing nativo di Go (`go test -fuzz=...`) supporta solo un piccolo insieme di tipi scalari come parametri di fuzz (`[]byte`, `string`, numeri, ...). In gosentry, puoi anche fare fuzz di **tipi compositi** costruiti da quegli scalari: struct, array, slice e puntatori.
Questo è utile quando il tuo codice accetta naturalmente input strutturati e non vuoi costruire un encoder/decoder personalizzato solo per inizializzare e mutare il corpus.
Vedi `test/gosentry/examples/multiargs` e `test/gosentry/examples/composite` per esempi.

#### Esempio semplice```go
type Input struct {
	Data []byte
	S    string
	N    int
	OK   bool
}

func FuzzStructInput(f *testing.F) {
	// Seed the initial corpus with a Go struct (gosentry feature).
	f.Add(Input{Data: []byte("A"), S: "B", N: 7, OK: true})

	f.Fuzz(func(t *testing.T, in Input) {
		if in.OK && in.N == 1337 && in.S == "BOOMMOOB" && bytes.Equal(in.Data, []byte("A")) {
			t.Fatalf("boom")
		}
	})
}
Come funzionano i seed delle struct (f.Add) e il fuzzing delle struct (la colla creata)

Il fuzzer nativo di Go non può fare fuzzing direttamente su un valore struct (sa solo mutare una piccola lista di tipi scalari). gosentry aggiunge un piccolo strato di colla: quando il tuo fuzz target usa tipi compositi (come Input), gosentry esegue il fuzzing su un singolo []byte dietro le quinte. A ogni esecuzione, decodifica quei byte nella tua struct (campo per campo, ricorsivamente per slice/array/puntatori) e poi chiama la callback f.Fuzz con il valore decodificato. La stessa codifica viene usata per i seed, quindi f.Add(Input{...}) diventa una voce di corpus []byte codificata che il fuzzer può riutilizzare e mutare come qualsiasi altro seed.

I fuzzer (incluso LibAFL) mutano byte grezzi, quindi vogliamo un decoder che possa trasformare qualsiasi slice di byte in un valore di struct "qualsiasi" e continuare. JSON/gob rifiuterebbero la maggior parte degli input casuali (male per la copertura), e inoltre non popolano i campi non esportati, mentre il fuzzing trae spesso beneficio dalla rottura degli invarianti. Questo formato personalizzato è piccolo, veloce, deterministico e tollerante ai dati malformati.

Dietro le quinte, usa il semplice formato binario di gosentry (non gob, non JSON). Il codice si trova in :

Funzionalità 2: Rilevamento di problemi di overflow e troncamento degli interi

Panoramica

Questo lavoro trae ispirazione dal go-panikint sviluppato in precedenza. Aggiunge il rilevamento di overflow/underflow per le operazioni aritmetiche sugli interi e (opzionalmente) il rilevamento del troncamento di tipo per le conversioni tra interi. Quando viene rilevato overflow o troncamento, viene generato un panic con un messaggio di errore dettagliato, che include il tipo di operazione specifico e i tipi di interi coinvolti.

Operazioni aritmetiche: Gestisce addizione +, sottrazione -, moltiplicazione * e divisione / sia per i tipi interi con segno che senza segno. Per gli interi con segno, copre int8, int16, int32. Per gli interi senza segno, copre uint8, uint16, uint32, uint64. Il caso della divisione rileva specificamente la condizione di overflow MIN_INT / -1 per gli interi con segno. int64 e uintptr non sono controllati per le operazioni aritmetiche.

Rilevamento del troncamento di tipo: Rileva conversioni tra tipi interi potenzialmente con perdita di dati. Copre tutti i tipi interi: int8, int16, int32, int64, uint8, uint16, uint32, uint64. Esclude uintptr a causa dell'uso dipendente dalla piattaforma. Questa funzione è disabilitata per impostazione predefinita.

Il rilevamento di overflow è abilitato per impostazione predefinita. Per disabilitarlo, aggiungi GOFLAGS='-gcflags=-overflowdetect=false' prima del tuo ./make.bash. Puoi anche abilitare il controllo dei problemi di troncamento con: -gcflags=-truncationdetect=true

Come funziona

Questa funzionalità modifica la generazione SSA del compilatore in modo che le operazioni aritmetiche sugli interi e le conversioni tra interi ricevano controlli runtime aggiuntivi che chiamano il runtime per generare un panic con un messaggio di errore dettagliato quando viene rilevato un bug. I controlli vengono applicati usando un filtro basato sulla posizione nel codice sorgente, così il codice utente viene strumentato mentre i file della libreria standard e le dipendenze (cache dei moduli e vendor/) vengono saltati.

Puoi leggere il post del blog associato qui.

Soppressione dei falsi positivi

Aggiungi un marcatore sulla stessa riga dell'operazione o sulla riga immediatamente precedente per sopprimere un report specifico:

  • Overflow/underflow: overflow_false_positive
  • Troncamento: truncation_false_positive

Esempio:```go // overflow_false_positive intentionalOverflow := a + b // truncation_false_positive x := uint8(big) sum2 := a + b // overflow_false_positive x2 := uint8(big) // truncation_false_positive

root@kitploit:~
A volte questo potrebbe non funzionare, perché Go sta in-lineando la funzione. Se `// overflow_false_positive` non basta, aggiungi `//go:noinline` prima della firma della tua funzione.

## Funzionalità 3: Panico su funzioni selezionate

Quando si eseguono test di fuzzing sui target, potrebbe interessarci innescare un panico quando vengono chiamate determinate funzioni. Ad esempio, alcuni software possono emettere messaggi `log.error` invece di andare in panico, anche se tali condizioni spesso indicano stati che i ricercatori di sicurezza vorrebbero rilevare durante il fuzzing.
Tuttavia, questi errori sono solitamente gestiti internamente (ad esempio tramite meccanismi di retry o pausa, oppure stampando messaggi nei log), il che li rende in gran parte invisibili ai fuzzer. L'obiettivo di questa funzionalità è risolvere questo problema.

#### Come usarlo

Compila gosentry, poi usa il flag `--panic-on`.```bash
./bin/go test -fuzz=FuzzHarness --use-libafl --focus-on-new-code=false --catch-races=false --catch-leaks=false --panic-on="test_go_panicon.(*Logger).Warning,test_go_panicon.(*Logger).Error"

L'esempio precedente andrebbe in panic quando viene chiamata (*Logger).Warning o (*Logger).Error (elenco separato da virgole).

Come funziona la funzionalità di panic su funzioni selezionate```text ┌───────────────────────────────────────────────────────────────────────────┐ │ 1) gosentry `go test` │ │ - parses + validates `-panic-on=...` against packages being built │ │ - forwards patterns to the compiler via `-panic-on-call=...` │ └───────────────┬───────────────────────────────────────────────────────────┘ v ┌───────────────────────────────────────────────────────────────────────────┐ │ 2) `cmd/compile` │ │ - prevents inlining of matching calls so the call stays visible │ │ - SSA pass inserts a call to `runtime.panicOnCall(...)` │ └───────────────┬───────────────────────────────────────────────────────────┘ v ┌───────────────────────────────────────────────────────────────────────────┐ │ 3) `runtime.panicOnCall` │ │ - panics with: "panic-on-call: func-name" │ └───────────────────────────────────────────────────────────────────────────┘ ``` In pratica, questo fa sì che qualsiasi punto di chiamata corrispondente si comporti come un crash/panic per i fuzzer (nota: solo i punti di chiamata statici possono essere intercettati).

Funzionalità 4: fuzzing all'avanguardia con LibAFL

LibAFL offre prestazioni di gran lunga migliori rispetto al tradizionale fuzzer Go. Quando si esegue il fuzzing (go test -fuzz=...), gosentry utilizza LibAFL per impostazione predefinita (runner in golibafl/).

Nota sulla stabilità: in modalità LibAFL, gosentry forza GODEBUG=updatemaxprocs=0 (disabilita gli aggiornamenti automatici di GOMAXPROCS a runtime) per evitare un crash intermittente della CI su Linux ("sync: inconsistent mutex state"). I dettagli sono in misc/gosentry/USE_LIBAFL.md.

Quando si utilizza LibAFL (di default), è necessario scegliere esplicitamente se abilitare la pianificazione git-aware: --focus-on-new-code=true|false. Maggiori informazioni in questo file Markdown. Puoi anche passare un file di configurazione JSONC opzionale per LibAFL (incluse le opzioni di fuzzing grammaticale), vedi qui. Con "stop_all_fuzzers_on_panic": false, LibAFL salva ogni crash e riavvia il proprio client per continuare il fuzzing.```bash ./bin/go test -fuzz=FuzzHarness --focus-on-new-code=false --catch-races=false --catch-leaks=false --libafl-config=path/to/libafl.jsonc # optional --libafl-config

root@kitploit:~
Use `-fuzztime=1m` per fermare una campagna LibAFL dopo un minuto.

La generazione del report di copertura dal corpus di una campagna LibAFL è documentata nella [Funzione 8](#feature-8-generate-go-coverage-reports-from-fuzzing-campaign).

Il fuzzing basato su grammatiche (Nautilus) è documentato nella [Funzione 7](#feature-7-grammar-based-fuzzing-nautilus).

<details>
<summary><strong>Come Go + LibAFL sono collegati tra loro</strong></summary>```text
┌───────────────────────────────────────────────────────────────────────────┐
│ 1) gosentry `go test`                                                      │
│    - captures  `testing.F.Fuzz(...)` callback                             │
│    - generates  extra source file: `_libaflmain.go`                       │
└───────────────┬───────────────────────────────────────────────────────────┘
                v
┌───────────────────────────────────────────────────────────────────────────┐
│ 2) Generated bridge: `_libaflmain.go`                                     │
│    - provides libFuzzer-style C ABI entrypoints:                          │
│        LLVMFuzzerInitialize                                               │
│        LLVMFuzzerTestOneInput                                             │
│    - adapts bytes -> Go types -> calls the captured fuzz callback         │
└───────────────┬───────────────────────────────────────────────────────────┘ 
                v
┌───────────────────────────────────────────────────────────────────────────┐
│ 3) `libharness.a` (static archive on disk) contains:                      │
│      - compiled objects for all test package (+ dependencies)             │
│      - generated `_testmain.go` + `_libaflmain.go`                        │
│      - LLVMFuzzerInitialize                                               │
│      - LLVMFuzzerTestOneInput                                             │
└───────────────┬───────────────────────────────────────────────────────────┘
                v
┌───────────────────────────────────────────────────────────────────────────┐
│ 4) `golibafl/` (Rust + LibAFL)                                            │
│    env: HARNESS_LIB=/path/to/libharness.a                                 │
│    fuzz loop: mutate input -> LLVMFuzzerTestOneInput(data) -> observe     │
└───────────────────────────────────────────────────────────────────────────┘

In --use-libafl mode, gosentry compila libharness.a e il runner Rust golibafl lo esegue in-process tramite gli entrypoint di libFuzzer. Nota: HARNESS_LIB può puntare a qualsiasi nome di archivio dell'harness (ad esempio libharness_race.a usato da --catch-races).

Come funziona la strumentazione della copertura (LibAFL + target Go)

In modalità --use-libafl, gosentry compila l'harness Go con la strumentazione della copertura abilitata. Questa aggiunge piccoli contatori al codice che cambiano quando diverse parti del tuo programma vengono eseguite. Quando l'harness viene avviato all'interno di golibafl, il runtime Go espone questi contatori a LibAFL. LibAFL li legge dopo ogni input per vedere quale codice è stato eseguito e usa quella copertura per guidare le mutazioni successive.

Limitazioni

Parliamo della motivazione dietro l'uso di LibAFL. Il fuzzing con go test -fuzz è molto indietro rispetto alle tecniche di fuzzing all'avanguardia. Un buon esempio è CMPLOG/Redqueen di AFL++. Queste funzionalità consentono ai fuzzer di risolvere determinati vincoli. Supponiamo il seguente frammento di codice```go if input == "IMARANDOMSTRINGJUSTCMPLOGMEMAN" { panic("this string is illegal") }

root@kitploit:~
SOTA fuzzers come AFL++ o LibAFL troverebbero il panic istantaneamente in quel caso. Tuttavia, il fuzzer nativo di Go non lo troverebbe. Questo è un divario enorme che limita **molto** l'esplorazione della copertura.

I benchmark qui sotto mostrano questi limiti. Nota che questi benchmark possono essere **riprodotti** e migliorati tramite il [repository gosentry-bench-libafl](https://github.com/kevin-valerio/gosentry-bench-libafl/tree/main).

##### Benchmark 1:

Il grafico seguente mostra l'evoluzione del numero di righe coperte durante il fuzzing di [UUID](https://github.com/google/uuid) di Google con LibAFL rispetto al fuzzer nativo di Go.
![BENCH1](https://assets.kitploit.com/production/public/readmes/41918/7b1ea005996846c9ee4d7c0d42fee26731050d10b6b21d59c551e4b1b956923e.png "BENCH1")

##### Benchmark 2:

Il grafico seguente mostra l'evoluzione del numero di righe coperte durante il fuzzing di [go-ethereum](https://github.com/ethereum/go-ethereum) con LibAFL rispetto al fuzzer nativo di Go.
![BENCH2](https://assets.kitploit.com/production/public/readmes/41918/61aab8540ab8d339307854de6737a405056c3c8ad83a822df40f74e362b93021.png "BENCH2")



#### Esempio
Puoi testarlo su alcuni harness di fuzzing in `test/gosentry/examples/`.```bash
cd test/gosentry/examples/reverse
../../../../bin/go test -fuzz=FuzzReverse --focus-on-new-code=false --catch-races=false --catch-leaks=false

Ferma la campagna di fuzzing con Ctrl+C.

Directory di output di LibAFL (identità della campagna / riutilizzo del corpus)

gosentry memorizza lo stato della campagna di LibAFL (corpus, crash, ecc.) sotto la root della cache di fuzzing di Go (circa $(go env GOCACHE)/fuzz), in una directory deterministica derivata dallo stesso pacchetto + stesso target di fuzzing (e dalla stessa root del progetto).

Questo significa che fermando (Ctrl+C) e riavviando la stessa campagna di fuzzing, per impostazione predefinita si continuerà dal precedente corpus queue/ di LibAFL.

Il percorso viene stampato al termine dell'esecuzione:```text libafl output dir: /full/path/to/.../fuzz//libafl//

root@kitploit:~
Note:
- `<harness>` è il nome del target di fuzzing quando `-fuzz` è un semplice identificatore come `FuzzXxx` (o `^FuzzXxx$`), altrimenti è `pattern-<hash>`.
- La generazione della coverage (`--generate-coverage`) usa la stessa regola per trovare il corpus `queue/` giusto, quindi deve essere eseguita dallo stesso package con lo stesso `-fuzz=...`.

## Funzionalità 5: fuzzing orientato a Git blame (sperimentale)

#### Panoramica

Il fuzzing guidato dalla coverage è ottimo per esplorare nuovi percorsi, ma tratta tutto il codice coperto come ugualmente interessante. Quando si esegue il fuzzing su codebase di grandi dimensioni, potrebbe essere utile orientare il fuzzer verso il codice modificato di recente, dove è più probabile che vengano introdotte regressioni e bug. In modalità LibAFL, gosentry può usare `git blame` per preferire input che eseguono righe modificate di recente (mantenendo comunque la guida della coverage come segnale primario).

Questo lavoro si basa su un precedente lavoro di [LibAFL-git-aware](https://github.com/kevin-valerio/LibAFL-git-aware). Tutti i dettagli tecnici approfonditi sono documentati lì.

#### Come usarlo

Abilita la schedulazione git-aware con `--focus-on-new-code=true`:```bash
./bin/go test -fuzz=FuzzHarness --use-libafl --focus-on-new-code=true --catch-races=false --catch-leaks=false

Questa modalità richiede git (per eseguire git blame) e go tool addr2line per mappare i contatori di copertura alla sorgente file:line.

Come funziona il fuzzing basato su git blame```text ┌───────────────────────────────────────────────────────────────────────────┐ │ 1) gosentry `go test -fuzz` │ │ - builds `libharness.a` (contains `go.o` + `.go.fuzzcntrs`) │ │ - runs `golibafl` with `GOLIBAFL_FOCUS_ON_NEW_CODE=1` │ └───────────────┬───────────────────────────────────────────────────────────┘ v ┌───────────────────────────────────────────────────────────────────────────┐ │ 2) `golibafl` generates a cached "git recency map" │ │ - maps coverage counters -> (file:line) via `go tool addr2line` │ │ - runs `git blame` to get a timestamp per line │ │ - stores timestamps in `git_recency_map.bin` │ └───────────────┬───────────────────────────────────────────────────────────┘ v ┌───────────────────────────────────────────────────────────────────────────┐ │ 3) LibAFL scheduler uses the recency map │ │ - coverage decides what enters the corpus │ │ - among the corpus, prioritize inputs that hit newer lines │ └───────────────────────────────────────────────────────────────────────────┘ ```
Come gosentry costruisce git_recency_map.bin

.go.fuzzcntrs è la sezione del linker che contiene i contatori di copertura a 8 bit in stile libFuzzer di Go (abilitati con -gcflags=all=-d=libfuzzer); ogni byte indica "quante volte è stato raggiunto questo punto instrumentato". Quando --focus-on-new-code=true, golibafl genera git_recency_map.bin:

  1. Estrarre go.o da libharness.a.
  2. Leggere la dimensione della sezione .go.fuzzcntrs per ottenere il numero di contatori N.
  3. Analizzare le rilocazioni di .text che referenziano i simboli di .go.fuzzcntrs per recuperare l'indirizzo di ciascun indice di contatore.
Benchmark 1 (go-ethereum / geth): baseline vs git-aware

Eseguito con misc/gosentry/bench_focus_on_new_code_geth.sh --trials 5 --warmup 600 --timeout 200.```text gitaware_5: crash (7122ms) baseline results: trial 1: crash (107747ms) trial 2: crash (146415ms) trial 3: crash (37902ms) trial 4: crash (154034ms) trial 5: timeout (200000ms) baseline crashes: 4/5 (timeouts=1, errors=0) baseline median (capped to timeout): 146.415s

git-aware results: trial 1: timeout (200000ms) trial 2: crash (87432ms) trial 3: crash (61733ms) trial 4: crash (157540ms) trial 5: crash (7122ms) git-aware crashes: 4/5 (timeouts=1, errors=0) git-aware median (capped to timeout): 87.432s

root@kitploit:~
</details>

## Funzionalità 6: Rilevare race condition, goroutine leak e blocchi (timeout) durante il fuzzing

##### Rilevamento di blocchi confermati (timeout di LibAFL)

Quando si esegue il fuzzing con LibAFL, un'esecuzione dell'harness può andare in **timeout** (ad esempio a causa di un deadlock / goroutine bloccate in attesa, o di un percorso estremamente lento).

Per ridurre i falsi positivi, gosentry tratta un timeout come un candidato blocco e lo conferma riproducendo l'input andato in timeout alcune volte con un timeout più ampio. In caso di blocco confermato, gosentry scrive l'input in `<libafl output dir>/hangs/` e interrompe la campagna di fuzzing (lo tratta come un bug/crash).

Prima di uscire, `golibafl` tenta di minimizzare l'input che causa il crash/blocco (best-effort; i blocchi sono limitati a ~60s in totale).

Nota: la conferma dei blocchi viene eseguita anche durante l'importazione/generazione iniziale del corpus, quindi i target che vanno in timeout su ogni input possono comunque essere rilevati in modo deterministico.

Questa funzionalità è configurata tramite `--libafl-config`:
- `catch_hangs` (predefinito: `true`)
- `hang_timeout_ms` (predefinito: `10000`)
- `hang_confirm_runs` (predefinito: `3`)

##### Rilevamento di data race (`--catch-races`)

gosentry può eseguire un ciclo di replay `-race` separato che osserva la directory `queue/` di LibAFL e riproduce i seed appena scoperti con `GORACE=halt_on_error=1`.

Il ciclo di replay compila un archivio separato dell'harness `-race` solo per il replay (senza strumentazione di copertura per il fuzzing).

Quando viene rilevata una data race durante il replay, gosentry stampa il report completo del race detector prima del riepilogo `catch-races:` e del comando di riproduzione.

Nota: il race detector di Go rileva solo data race **all'interno di una singola esecuzione dell'harness** (race tra goroutine nello stesso processo che accedono alla stessa memoria senza una corretta sincronizzazione). `--catch-races` non rileverà le race se il seed non innesca la concorrenza problematica e non rileva race tra processi diversi.

<details>
<summary><strong>Come funziona la modalità data race</strong></summary>

Questa modalità avvia un piccolo monitor all'interno di `go test` (stesso processo padre) e resta attivo per l'intera campagna di fuzzing.

- Quando: prima che venga avviato il processo principale di fuzzing LibAFL, gosentry compila l'harness di replay + il runner.
- Monitoraggio: prima che il fuzzing inizi, gosentry acquisisce un'istantanea del contenuto iniziale di `<libafl output dir>/queue/` in un set `seen`. Una goroutine interroga quindi `<libafl output dir>/queue/` ogni ~1s e riproduce solo i seed appena creati (ignora i file nascosti e i `*.metadata`).```text
Legend: output/... = <libafl output dir>/...

┌───────────────────────────────────────────────────────────────────────────┐
│ 1) Main LibAFL fuzzing run                                                 │
│    - `golibafl` writes new seeds to `output/queue/`                        │
└───────────────┬───────────────────────────────────────────────────────────┘
                v
┌───────────────────────────────────────────────────────────────────────────┐
│ 2) `--catch-races` sidecar setup                                           │
│    - builds replay harness: `libharness_race.a` (`go test -race ...`)      │
│    - builds replay runner: `golibafl-race` (linked against race harness)   │
└───────────────┬───────────────────────────────────────────────────────────┘
                v
┌───────────────────────────────────────────────────────────────────────────┐
│ 3) Replay loop                                                             │
│    - polls `output/queue/` for new seeds                                   │
│    - runs: `GORACE=halt_on_error=1 golibafl-race run --input <seed>`       │
│      (2 workers × 3 repeats per seed)                                      │
└───────────────┬───────────────────────────────────────────────────────────┘
                v
┌───────────────────────────────────────────────────────────────────────────┐
│ 4) On "DATA RACE"                                                          │
│    - prints the race detector report                                       │
│    - copies seed to `output/races/`                                        │
│    - stops the fuzz campaign (treat as bug/crash)                          │
└───────────────────────────────────────────────────────────────────────────┘
Rilevare le perdite di goroutine (--catch-leaks)

gosentry può anche eseguire un ciclo di replay di goleak che osserva la directory queue/ di LibAFL e ripete i seed appena scoperti con go.uber.org/goleak abilitato.

Quando viene rilevata una perdita di goroutine, gosentry stampa il percorso esatto del seed e lo copia in <libafl output dir>/leaks/.

Nota: goleak serve per le perdite di goroutine, non per le perdite di memoria.

Come funziona la modalità di rilevamento delle perdite di goroutine

Questa modalità avvia anche un piccolo monitor all'interno di go test (stesso processo padre) e rimane attivo per l'intera campagna di fuzzing.

  • Monitoraggio: una goroutine interroga <libafl output dir>/queue/ ogni ~1s e ripete ogni nuovo seed con GOSENTRY_LIBAFL_CATCH_LEAKS=1 (abilita go.uber.org/goleak dopo ogni esecuzione).```text Legend: output/... = /...

┌───────────────────────────────────────────────────────────────────────────┐ │ 1) Main LibAFL fuzzing run │ │ - golibafl writes new seeds to output/queue/ │ └───────────────┬───────────────────────────────────────────────────────────┘ v ┌───────────────────────────────────────────────────────────────────────────┐ │ 2) --catch-leaks sidecar setup │ │ - builds replay runner: golibafl-leak (linked against the harness) │ └───────────────┬───────────────────────────────────────────────────────────┘ v ┌───────────────────────────────────────────────────────────────────────────┐ │ 3) Replay loop │ │ - polls output/queue/ for new seeds │ │ - runs: │ │ (enables checks after each execution) │ └───────────────┬───────────────────────────────────────────────────────────┘ v ┌───────────────────────────────────────────────────────────────────────────┐ │ 4) On "catch-leaks: detected goroutine leak" │ │ - copies seed to │ │ - stops the fuzz campaign (treat as bug/crash) │ └───────────────────────────────────────────────────────────────────────────┘

Come funziona il fuzzing grammaticale in gosentry```text Legend: output/... = /...

┌───────────────────────────────────────────────────────────────────────────┐ │ 0) gosentry go test -fuzz=FuzzXxx (LibAFL + --use-grammar) │ │ - captures your testing.F.Fuzz callback + its parameter types │ │ - builds libharness.a (libFuzzer-style entrypoints for LibAFL) │ │ - runs golibafl fuzz ... --use-grammar --grammar ... │ └───────────────┬───────────────────────────────────────────────────────────┘ v ┌───────────────────────────────────────────────────────────────────────────┐ │ 1) golibafl (Rust + LibAFL) fuzzes the Go harness in-process │ │ - loads libharness.a via HARNESS_LIB=... │ │ - observers: edges + time (+ cmplog for comparisons) │ │ - feedback/objective: coverage/time/crash (and optional hang handling) │ │ - scheduler selects a corpus seed (coverage-guided) │ └───────────────┬───────────────────────────────────────────────────────────┘ v ┌───────────────────────────────────────────────────────────────────────────┐ │ 2) Nautilus (in-process, per client) │ │ - loads the JSON grammar into a Nautilus context │ │ - fuzz loop stage: parse seed -> mutate tree -> unparse to bytes │ │ - if the seed is not parseable: fall back to generation-from-scratch │ └───────────────┬───────────────────────────────────────────────────────────┘ v ┌───────────────────────────────────────────────────────────────────────────┐ │ 3) Grammar mode stages │ │ - initial corpus: if input dir empty, call N times │ │ - fuzz loop: corpus seed -> grammar mutate -> exec harness │ │ - new coverage inputs are added to the on-disk corpus () │ └───────────────────────────────────────────────────────────────────────────┘

Scarica lo strumento
src/testing/libafl.go
  • Encode: libaflMarshalInputs / libaflAppendValue
  • Decode: libaflUnmarshalArgs / libaflDecodeValue

Regole di codifica (a livello generale):

  • bool: 1 byte (0 o 1)
  • Interi: byte little-endian (int/uint sono 8 byte)
  • Float: bit IEEE-754 in little-endian (float32 = 4 byte, float64 = 8 byte)
  • string: uvarint(len) poi byte grezzi della stringa
  • []byte: uvarint(len) poi byte grezzi
  • Altre slice: uvarint(len) poi ogni elemento codificato
  • Struct: campi codificati nell'ordine di dichiarazione
  • Puntatori: 1 byte (0 = nil, 1 = presente) poi il valore puntato
  • Risolvere ogni indirizzo in file:line usando go tool addr2line.
  • Eseguire git blame --line-porcelain per ottenere il committer-time per ogni riga.
  • Scrivere git_recency_map.bin come u64 head_time + u64 N + N * u64 timestamps (little-endian). Le voci non mappate usano timestamp 0.
  • GOSENTRY_LIBAFL_CATCH_LEAKS=1 golibafl-leak run --input <seed>
    go.uber.org/goleak
    output/leaks/
    root@kitploit:~
    </details>
    
    
    #### Come usare
    
    Abilita il rilevamento delle perdite di goroutine con `--catch-leaks=true` o il rilevamento delle race con `--catch-races=true````bash
    ./bin/go test -fuzz=FuzzHarness --use-libafl --focus-on-new-code=false --catch-races=true --catch-leaks=true
    

    Funzionalità 7: Fuzzing basato su grammatica (Nautilus)

    Panoramica

    Il fuzzing a livello di byte è ottimo, ma parser e formati di file spesso richiedono input strutturati. Con --use-grammar, gosentry usa il mutatore di grammatica Nautilus di LibAFL per generare e mutare input conformi a una grammatica fornita dall'utente (formato JSON) e li passa al tuo normale harness di fuzz Go (testing.F.Fuzz).

    In modalità grammatica, LibAFL esegue comunque il normale loop guidato dalla copertura (seleziona un seed dal corpus → muta → esegui → mantieni gli input che aumentano la copertura). Il runner aggiunge la mutazione Nautilus (seed → albero di grammatica → muta → de-parsing) più (per impostazione predefinita) una fase guidata da CMPLOG, simile a I2S, che riscrive i terminali foglia di Nautilus in base ai confronti a runtime. Questo mantiene gli input validi per la grammatica (non esegue le fasi raw havoc/token a livello di byte in modalità grammatica). Puoi disabilitare la fase CMPLOG/I2S in --libafl-config tramite nautilus_cmplog_i2s=false (il fuzzing a livello di byte mantiene comunque CMPLOG/I2S sempre attivo).

    [!NOTE] La modalità grammatica è solitamente più lenta del fuzzing a livello di byte. È un compromesso: più struttura vs meno esecuzioni al secondo.

    Per ottenere i migliori risultati, usa un callback di fuzz con un solo argomento che accetti uno slice di byte ([]byte) o una string:```go f.Fuzz(func(t testing.T, data []byte) { / parse data */ }) // or: f.Fuzz(func(t testing.T, s string) { / parse s */ })

    root@kitploit:~
    La modalità Grammar funziona meglio con un singolo argomento di input (`[]byte` o `string`). I callback di fuzz multi-argomento portano gosentry a decodificare il buffer di byte sottostante in valori separati, quindi il testo originale generato dalla grammatica non rimarrà intatto.
    
    > [!NOTE]
    > La modalità Grammar genera comunque **byte/stringhe**. Se hai bisogno di input strutturati (o stai facendo differential fuzzing), è nell'harness che converti `data` in valori di dominio (parse/unmarshal). (Fuori dalla modalità Grammar, gosentry può anche fare fuzzing su tipi Go > composti decodificandoli dai byte; vedi [Feature 1](#feature-1-struct-aware-fuzzing-fuzz-structs-as-inputs).)
    
    Puoi regolare Nautilus tramite `--libafl-config` (usato solo con `--use-grammar`): `nautilus_max_len` e `nautilus_cmplog_i2s` (vedi `misc/gosentry/libafl.config.jsonc`).
    
    <details>
    <summary><strong>Benchmark: fase CMPLOG/I2S della grammatica Nautilus (attivo vs disattivo)</strong></summary>
    
    Eseguito il 17 febbraio 2026 utilizzando l'esempio di grammatica JSON del repository (`test/gosentry/examples/grammar_json`, `FuzzGrammarJSON`, grammar `testdata/JSON.json`).
    
    Risultati (LibAFL `UserStats`):
    
    | modalità | `nautilus_cmplog_i2s` | tempo di esecuzione | esecuzioni | esec/sec | edges |
    |---|---:|---:|---:|---:|---:|
    | attivo | `true` | 1m-5s | 103818 | 1.586k | 388/8008 (4%) |
    | disattivo | `false` | 1m-0s | 256659 | 4.251k | 388/8008 (4%) |
    
    Nota: `edges` sono gli archi della coverage map di LibAFL, non le righe del sorgente Go.
    
    </details>
    
    Imposta `GOSENTRY_VERBOSE_AFL=1` per stampare alcuni input generati. Imposta `GOSENTRY_VERBOSE_AFL_ALL_INPUTS=1` per stampare **ogni** esecuzione in modalità Grammar come `GOLIBAFL_MUTATED_INPUT "..."` (molto rumoroso).
    
    #### Helper per la creazione di grammatiche
    
    Se devi creare una nuova grammatica JSON Nautilus per il tuo formato/protocollo di destinazione, gosentry include:
    
    - Un prompt pronto per LLM: [misc/gosentry/nautilus/prompt.md](https://github.com/trailofbits/gosentry/blob/HEAD/misc/gosentry/nautilus/prompt.md)
    - Un piccolo set di grammatiche di esempio: [misc/gosentry/nautilus/examples/](https://github.com/trailofbits/gosentry/blob/HEAD/misc/gosentry/nautilus/examples/)
    
    <details>
    <summary><strong>Esempio di harness Go per fuzzing (JSON)</strong></summary>```go
    func FuzzGrammarJSON(f *testing.F) {
    	f.Fuzz(func(t *testing.T, data []byte) {
    		dec := json.NewDecoder(bytes.NewReader(data))
    		dec.UseNumber()
    
    		var v any
    		if err := dec.Decode(&v); err != nil {
    			t.Fatalf("invalid JSON: %v", err)
    		}
    		if err := dec.Decode(&struct{}{}); err != io.EOF {
    			t.Fatalf("invalid JSON: trailing data")
    		}
    	})
    }
    

    Bozza di harness di fuzzing differenziale (due parser):```go f.Fuzz(func(t *testing.T, data []byte) { gotA, errA := ParseA(data) gotB, errB := ParseB(data) if (errA == nil) != (errB == nil) { t.Fatalf("parser disagreement: A=%v B=%v", errA, errB) } _ = gotA _ = gotB })

    root@kitploit:~
    </details>
    
    <details>
    <summary><strong>Esempio: fuzzing grammaticale di un "linguaggio di input reale" (senza encoder personalizzato)</strong></summary>
    
    Questo esempio sottopone a fuzzing un piccolo valutatore di espressioni aritmetiche generando **espressioni valide** a partire da una grammatica. Non c'è alcuna codifica ad hoc del tipo “struct to bytes”: il fuzzer produce lo stesso tipo di input che il tuo codice normalmente analizzerebbe.
    
    Harness (l'input `string` a 1 argomento funziona meglio in modalità grammatica):```go
    func FuzzExprEval(f *testing.F) {
    	f.Add("1+2")
    	f.Add("(3*4)-5")
    
    	f.Fuzz(func(t *testing.T, expr string) {
    		// Parse+eval your language/protocol.
        // You can be **sure** that `expr` will always be a valid math operation. Just decode/parse/unmarshall it afterwards. 
    		_, _ = Eval(expr)
    	})
    }
    

    Schema grammaticale (formato JSON Nautilus):```json [ ["Expr", "{Term}"], ["Expr", "{Term}+{Expr}"], ["Expr", "{Term}-{Expr}"], ["Term", "{Factor}"], ["Term", "{Factor}*{Term}"], ["Factor", "{Num}"], ["Factor", "({Expr})"], ["Num", "0"], ["Num", "1"], ["Num", "2"], ["Num", "3"] ]

    root@kitploit:~
    </details>
    
    <details>
    <summary><strong>Esempio di grammatica JSON di Nautilus (piccolo sottoinsieme JSON)</strong></summary>
    
    Questo è il formato di file previsto da `--grammar=...`:
    - La grammatica è un array JSON di regole: `["NonTerm", "RHS"]`.
    - I nomi dei non terminali devono iniziare con una lettera maiuscola (`Value`, `Object`, ...).
    - Usa `{NonTerm}` nella RHS per fare riferimento a un'altra regola.
    - Le parentesi graffe `{` e `}` sono riservate ai riferimenti non terminali; per emettere parentesi letterali, usa `\\{` e `\\}` nella stringa RHS.```json
    [
      ["Json", "{Value}"],
      ["Value", "null"],
      ["Value", "{String}"],
      ["String", "\"{Chars}\""],
      ["Chars", ""],
      ["Chars", "{Char}{Chars}"],
      ["Char", "a"],
      ["Char", "b"]
    ]
    
    generate
    output/queue/
    root@kitploit:~
    </details>
    
    Limitazioni (attuale collante):
    - La modalità grammatica funziona meglio con un singolo argomento di input; i target di fuzzing multi-argomento decodificheranno il buffer di byte sottostante in valori separati.
    - Nessuna ricombinazione/crossover della grammatica tra due seed del corpus per ora (la mutazione è a seed singolo).
    
    ## Funzionalità 8: Generare report di copertura Go dalle campagne di fuzzing
    
    Dopo (o durante) l'esecuzione di una campagna di fuzzing LibAFL, gosentry può generare un report di copertura Go riproducendo l'attuale **queue corpus** di LibAFL (senza fuzzing).```bash
    # Same package + same fuzz target as your fuzz campaign:
    ./bin/go test -fuzz=FuzzHarness --generate-coverage .
    

    This replays inputs from <libafl output dir>/queue/ and writes cover.out and cover.html.

    Trofei

    Questi bug sono stati trovati eseguendo una campagna di differential-fuzzing utilizzando la funzione di grammar fuzzing di gosentry.

    Optimism

    • Kona e op-node possono non concordare sui canali brotli
    • Tipo di batch sconosciuto causa un panic e denial of service in kona-protocol
    • Il parsing dei frame di Kona non corrisponde a op-node e OP Stack Specs

    REVM

    • Un deposito fallito in op-revm che si ferma con OutOfFunds non incrementa il nonce, portando a una mancata corrispondenza della state root rispetto ad altri client

    Crediti

    • golibafl
    • Nautilus
    • LibAFL
    • goleak
    • go
    • go-panikint