
Toolchain de Go orientado a la seguridad, centrado en capacidades de fuzzing de última generación.
# gosentry
[](https://github.com/trailofbits/gosentry/actions/workflows/go.yml)
gosentry es un fork centrado en la seguridad de la cadena de herramientas de Go, que integra numerosas funciones para campañas de fuzzing de vanguardia en bases de código Go. Si antes usabas `go test -fuzz`, deberías usar gosentry como reemplazo.
Incluye varias mejoras de fuzzing y detectores de errores que no están presentes de forma nativa en la cadena de herramientas de Go. Consulta el TLDR; a continuación. También puedes leer el artículo de blog asociado [aquí](https://blog.trailofbits.com/2026/05/12/go-fuzzing-was-missing-half-the-toolkit.-we-forked-the-toolchain-to-fix-it./).
**TLDR (funcionalidades y opciones)**:
- Fuzzear entradas de tipo `struct` directamente (sin necesidad de un parser personalizado). Añadir semillas con `f.Add(Input{N: 7, S: "hi"})` y luego `f.Fuzz(func(t *testing.T, in Input) { ... })`.
- Entrar en pánico ante el desbordamiento de enteros y detectar problemas aritméticos
- Fuzzear con LibAFL para técnicas de fuzzing de vanguardia como la resolución de restricciones de ruta
- Generar/mutar entradas a partir de una gramática para evitar mutaciones inútiles. La mutación genera operaciones matemáticas válidas, por ejemplo `X + Y - Z` puede convertirse en `X / U + Z - 14` en lugar de `X + Yè - Z`
- Entrar en pánico en funciones seleccionadas (como registradores de errores críticos) y abortar cuando se llama
- Enfocar el fuzzer en líneas modificadas recientemente Y en nueva cobertura para apuntar principalmente a commits nuevos
- Detectar condiciones de carrera en tiempo de fuzzing
- Detectar fugas de Go en tiempo de fuzzing
- Detectar ejecuciones bloqueadas con timeouts en tiempo de fuzzing
- Generar un informe de cobertura HTML a partir del corpus de una campaña de fuzzing con una única CLI
## Tabla de contenidos
- [Compilación](#build)
- [Funcionalidad 1: Fuzzing orientado a structs (fuzz de structs como entradas)](#feature-1-struct-aware-fuzzing-fuzz-structs-as-inputs)
- [Funcionalidad 2: Detección de problemas de desbordamiento y truncamiento de enteros](#feature-2-integer-overflow-and-truncation-issues-detection)
- [Funcionalidad 3: Pánico en funciones seleccionadas](#feature-3-panic-on-selected-functions)
- [Funcionalidad 4: Fuzzing de vanguardia con LibAFL](#feature-4-libafl-state-of-the-art-fuzzing)
- [Funcionalidad 5: Fuzzing orientado a git blame (experimental)](#feature-5-git-blame-oriented-fuzzing-experimental)
- [Funcionalidad 6: Detectar condiciones de carrera, fugas de goroutines y bloqueos en tiempo de fuzzing](#feature-6-detect-race-conditions-goroutine-leaks-and-hangs-at-fuzz-time)
- [Funcionalidad 7: Fuzzing basado en gramáticas (Nautilus)](#feature-7-grammar-based-fuzzing-nautilus)
- [Funcionalidad 8: Generar informes de cobertura de fuzzing a partir de la campaña](#feature-8-generate-go-coverage-reports-from-fuzzing-campaign)
- [Trofeos](#trophies)
- [Créditos](#credits)
## Compilación```bash
cd src && ./make.bash # Produces `../bin/go`. See `GOFLAGS` below.
```
> [!TIP]
> Documentación para contribuidores: lee `docs/gosentry/index.md` para ver un mapa del código, el bucle de desarrollo recomendado, los puntos de entrada de CI y los scripts de benchmark.
> Este fork utiliza la Pull GitHub App para abrir y fusionar automáticamente los PR desde `golang/go:master` hacia `master`, asegurando que nunca nos quedemos atrás de las últimas actualizaciones de la cadena de herramientas de Go.
## Característica 1: Fuzzing consciente de structs (fuzzing de structs como entradas)
#### Resumen
El fuzzing nativo de Go (`go test -fuzz=...`) solo admite un pequeño conjunto de tipos escalares como parámetros de fuzzing (`[]byte`, `string`, números, ...). En gosentry, también puedes someter a fuzzing **tipos compuestos** construidos a partir de esos escalares: structs, arrays, slices y punteros.
Esto es útil cuando tu código acepta de forma natural entradas estructuradas y no quieres construir un codificador/decodificador personalizado solo para sembrar y mutar el corpus.
Consulta `test/gosentry/examples/multiargs` y `test/gosentry/examples/composite` para ver ejemplos.
#### Ejemplo simple```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")
}
})
}
```
<details>
<summary><strong>Cómo funcionan las semillas de structs (<code>f.Add</code>) y el fuzzing de structs (el pegamento creado)</strong></summary>
El fuzzer nativo de Go no puede fuzzear un valor `struct` directamente (solo sabe mutar una pequeña lista de tipos escalares). gosentry añade una pequeña capa de pegamento: cuando tu objetivo de fuzzing usa tipos compuestos (como `Input`), gosentry fuzzea un único `[]byte` entre bastidores. En cada ejecución, **decodifica** esos bytes en tu struct (campo por campo, recursivamente para slices/arrays/punteros) y luego llama a tu callback `f.Fuzz` con el valor decodificado. La misma **codificación** se usa para las semillas, por lo que `f.Add(Input{...})` se convierte en una entrada de corpus `[]byte` codificada que el fuzzer puede reutilizar y mutar como cualquier otra semilla.
Los fuzzers (incluido LibAFL) mutan bytes crudos, por lo que queremos un decodificador que pueda convertir **cualquier** slice de bytes en "algún" valor de struct y seguir adelante. JSON/`gob` rechazarían la mayoría de las entradas aleatorias (malo para la cobertura), y además no pueblan campos no exportados, mientras que el fuzzing a menudo se beneficia de romper invariantes. Este formato personalizado es pequeño, rápido, determinista y tolerante a datos malformados.
Bajo el capó, esto usa el propio formato binario simple de gosentry (ni `gob`, ni JSON). El código vive en `src/testing/libafl.go`:
- Codificar: `libaflMarshalInputs` / `libaflAppendValue`
- Decodificar: `libaflUnmarshalArgs` / `libaflDecodeValue`
Reglas de codificación (alto nivel):
- `bool`: 1 byte (`0` o `1`)
- Enteros: bytes little-endian (`int`/`uint` son 8 bytes)
- Flotantes: bits IEEE-754 en little-endian (`float32` = 4 bytes, `float64` = 8 bytes)
- `string`: `uvarint(len)` y luego bytes crudos de la cadena
- `[]byte`: `uvarint(len)` y luego bytes crudos
- Otros slices: `uvarint(len)` y luego cada elemento codificado
- Structs: campos codificados en orden de declaración
- Punteros: 1 byte (`0` = nil, `1` = presente) y luego el valor apuntado
</details>
## Feature 2: Detección de problemas de desbordamiento de enteros y truncamiento
#### Descripción general
Este trabajo está inspirado en el [go-panikint](https://github.com/trailofbits/go-panikint) desarrollado anteriormente. Añade detección de desbordamiento/subdesbordamiento para operaciones aritméticas con enteros y (opcionalmente) detección de truncamiento de tipos para conversiones de enteros. Cuando se detecta un desbordamiento o truncamiento, se lanza un panic con un mensaje de error detallado, incluyendo el tipo de operación específico y los tipos de enteros implicados.
_Operaciones aritméticas_: Gestiona la suma `+`, la resta `-`, la multiplicación `*` y la división `/` para tipos de enteros tanto con signo como sin signo. Para enteros con signo, cubre `int8`, `int16`, `int32`. Para enteros sin signo, cubre `uint8`, `uint16`, `uint32`, `uint64`. El caso de la división detecta específicamente la condición de desbordamiento `MIN_INT / -1` para enteros con signo. `int64` y `uintptr` no se comprueban para operaciones aritméticas.
_Detección de truncamiento de tipos_: Detecta conversiones de tipos enteros potencialmente con pérdida de datos. Cubre todos los tipos enteros: `int8`, `int16`, `int32`, `int64`, `uint8`, `uint16`, `uint32`, `uint64`. Excluye `uintptr` debido a su uso dependiente de la plataforma. Esta opción está deshabilitada por defecto.
La detección de desbordamiento está habilitada por defecto. Para deshabilitarla, añade `GOFLAGS='-gcflags=-overflowdetect=false'` antes de tu `./make.bash`. También puedes habilitar el comprobador de problemas de truncamiento con: `-gcflags=-truncationdetect=true`
#### Cómo funciona
Esta característica parchea la generación de SSA del compilador para que las operaciones aritméticas con enteros y las conversiones de enteros reciban comprobaciones adicionales en tiempo de ejecución que llaman al runtime para lanzar un panic con un mensaje de error detallado cuando se detecta un error. Las comprobaciones se aplican mediante un filtrado basado en la ubicación del código fuente, de modo que el código del usuario se instrumenta mientras que los archivos de la biblioteca estándar y las dependencias (caché de módulos y `vendor/`) se omiten.
Puedes leer la entrada de blog asociada [**aquí**](https://blog.trailofbits.com/2025/12/31/detect-gos-silent-arithmetic-bugs-with-go-panikint/).
#### Supresión de falsos positivos
Añade un marcador en la misma línea de la operación o en la línea inmediatamente superior para suprimir un informe específico:
- Desbordamiento/subdesbordamiento: `overflow_false_positive`
- Truncamiento: `truncation_false_positive`
Ejemplo:```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
```
A veces esto puede no funcionar, porque Go está integrando la función en línea. Si `// overflow_false_positive` no es suficiente, añade `//go:noinline` antes de la firma de tu función.
## Característica 3: Pánico en funciones seleccionadas
Al hacer fuzzing de objetivos, puede interesarnos provocar un pánico cuando se llama a ciertas funciones. Por ejemplo, algún software puede emitir mensajes `log.error` en lugar de entrar en pánico, aunque tales condiciones suelen indicar estados que los investigadores de seguridad querrían detectar durante el fuzzing.
Sin embargo, estos errores normalmente se manejan internamente (por ejemplo, mediante mecanismos de reintento o pausa, o imprimiendo mensajes en los registros), lo que los hace en gran medida invisibles para los fuzzers. El objetivo de esta característica es abordar este problema.
#### Cómo usarlo
Compila gosentry y luego usa el indicador `--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"
```
El ejemplo anterior provocaría un panic cuando se llame a `(*Logger).Warning` o `(*Logger).Error` (lista separada por comas).
<details>
<summary><strong>Cómo funciona la característica de panic en funciones seleccionadas</strong></summary>```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" │
└───────────────────────────────────────────────────────────────────────────┘
```
En la práctica, esto hace que cualquier punto de llamada coincidente se comporte como un crash/panic para los fuzzers (nota: solo los puntos de llamada estáticos pueden ser atrapados).
</details>
## Característica 4: Fuzzing de vanguardia con LibAFL
LibAFL rinde *mucho* mejor que el fuzzer tradicional de Go. Al realizar fuzzing (`go test -fuzz=...`), gosentry usa [LibAFL](https://github.com/AFLplusplus/LibAFL) **por defecto** (runner en `golibafl/`).
Nota de estabilidad: en modo LibAFL, gosentry fuerza `GODEBUG=updatemaxprocs=0` (desactiva las actualizaciones automáticas de GOMAXPROCS en tiempo de ejecución) para evitar un fallo intermitente del CI de Linux ("sync: inconsistent mutex state"). Los detalles están en `misc/gosentry/USE_LIBAFL.md`.
Al usar LibAFL (por defecto), debes elegir explícitamente si habilitar la planificación consciente de git: `--focus-on-new-code=true|false`. Más documentación en [este archivo Markdown.](https://github.com/trailofbits/gosentry/blob/master/misc/gosentry/USE_LIBAFL.md)
También puedes pasar un archivo de configuración JSONC opcional para LibAFL (incluidas opciones de fuzzing de gramática), consúltalo [aquí.](https://github.com/trailofbits/gosentry/blob/master/misc/gosentry/libafl.config.jsonc)
Con `"stop_all_fuzzers_on_panic": false`, LibAFL guarda cada crash y reinicia su cliente para seguir fuzzeando.```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
```
Usa `-fuzztime=1m` para detener una campaña de LibAFL después de un minuto.
La generación de informes de cobertura a partir del corpus de una campaña de LibAFL se documenta en [Característica 8](#feature-8-generate-go-coverage-reports-from-fuzzing-campaign).
El fuzzing basado en gramáticas (Nautilus) se documenta en [Característica 7](#feature-7-grammar-based-fuzzing-nautilus).
<details>
<summary><strong>Cómo se conectan Go y LibAFL</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 │
└───────────────────────────────────────────────────────────────────────────┘
```
En el modo `--use-libafl`, gosentry construye `libharness.a` y el ejecutor Rust `golibafl` lo controla en el mismo proceso mediante los puntos de entrada de libFuzzer. Nota: `HARNESS_LIB` puede apuntar a cualquier nombre de archivo de harness (por ejemplo `libharness_race.a` usado por `--catch-races`).
</details>
<details>
<summary><strong>Cómo funciona la instrumentación de cobertura (LibAFL + objetivo Go)</strong></summary>
En el modo `--use-libafl`, gosentry compila el harness Go con la instrumentación de cobertura habilitada. Esto añade pequeños contadores al código que cambian cuando se ejecutan diferentes partes del programa. Cuando el harness se inicia dentro de `golibafl`, el runtime de Go expone estos contadores a LibAFL. LibAFL los lee después de cada entrada para ver qué código se ejecutó y utiliza esa cobertura para guiar las siguientes mutaciones.
</details>
##### Limitaciones
Hablemos de la motivación detrás del uso de LibAFL. Fuzzing con `go test -fuzz` está _muy_ por detrás de las técnicas de fuzzing de última generación. Un buen ejemplo de esto es CMPLOG/Redqueen de AFL++. Estas funcionalidades permiten a los fuzzers resolver ciertas restricciones. Supongamos el siguiente fragmento```go
if input == "IMARANDOMSTRINGJUSTCMPLOGMEMAN" {
panic("this string is illegal")
}
```
Los fuzzers de última generación como AFL++ o LibAFL encontrarían el panic al instante en ese caso. Sin embargo, el fuzzer nativo de Go no lo haría. Esa es una brecha enorme que restringe la exploración de cobertura en **gran medida**.
El benchmark a continuación muestra esos límites. Ten en cuenta que esos benchmarks pueden **reproducirse** y mejorarse mediante el [repositorio gosentry-bench-libafl](https://github.com/kevin-valerio/gosentry-bench-libafl/tree/main).
##### Benchmark 1:
El gráfico a continuación muestra la evolución del número de líneas cubiertas al fuzzear el [UUID](https://github.com/google/uuid) de Google usando LibAFL frente al fuzzer nativo de Go.

##### Benchmark 2:
El gráfico a continuación muestra la evolución del número de líneas cubiertas al fuzzear [go-ethereum](https://github.com/ethereum/go-ethereum) usando LibAFL frente al fuzzer nativo de Go.

#### Ejemplo
Puedes probarlo en algunos harnesses de fuzzing en `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
```
Detén la campaña de fuzzing con Ctrl+C.
##### Directorio de salida de LibAFL (identidad de campaña / reutilización de corpus)
gosentry almacena el estado de la campaña de LibAFL (corpus, crashes, etc.) bajo la raíz de caché de fuzzing de Go (aproximadamente `$(go env GOCACHE)/fuzz`), en un directorio determinista derivado del **mismo paquete + mismo objetivo de fuzzing** (y la misma raíz del proyecto).
Esto significa que detener (Ctrl+C) y reiniciar la misma campaña de fuzzing continuará, por defecto, desde el corpus `queue/` anterior de LibAFL.
La ruta se imprime al final de la ejecución:```text
libafl output dir: /full/path/to/.../fuzz/<pkg import path>/libafl/<project>/<harness>
```
Notes:
- `<harness>` es el nombre del objetivo de fuzzing cuando `-fuzz` es un identificador simple como `FuzzXxx` (o `^FuzzXxx$`); de lo contrario, es `pattern-<hash>`.
- La generación de cobertura (`--generate-coverage`) usa la misma regla para encontrar el corpus `queue/` correcto, por lo que debe ejecutarse desde el mismo paquete con el mismo `-fuzz=...`.
## Característica 5: Fuzzing orientado a git-blame (experimental)
#### Resumen
El fuzzing guiado por cobertura es excelente para explorar nuevas rutas, pero trata todo el código cubierto como igualmente interesante. Al hacer fuzzing en bases de código grandes, es posible que desees sesgar el fuzzer hacia el código modificado recientemente, donde es más probable que se introduzcan regresiones y errores. En el modo LibAFL, gosentry puede usar `git blame` para preferir entradas que ejecuten líneas modificadas recientemente (manteniendo la guía de cobertura como señal principal).
Este trabajo se basa en trabajos previos de [LibAFL-git-aware](https://github.com/kevin-valerio/LibAFL-git-aware). Todos los detalles técnicos en profundidad están documentados allí.
#### Cómo usarlo
Habilita la planificación consciente de git 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
```
Este modo necesita `git` (para ejecutar `git blame`) y `go tool addr2line` para mapear los contadores de cobertura de vuelta al `file:line` de origen.
<details>
<summary><strong>Cómo funciona el fuzzing orientado a git-blame</strong></summary>```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 │
└───────────────────────────────────────────────────────────────────────────┘
```
</details>
<details>
<summary><strong>Cómo gosentry construye <code>git_recency_map.bin</code></strong></summary>
`.go.fuzzcntrs` es la sección del enlazador que contiene los **contadores de cobertura de 8 bits** estilo libFuzzer de Go (habilitados mediante `-gcflags=all=-d=libfuzzer`); cada byte indica "cuántas veces se ha alcanzado este punto instrumentado". Cuando `--focus-on-new-code=true`, `golibafl` genera `git_recency_map.bin` de la siguiente manera:
1. Extrayendo `go.o` de `libharness.a`.
2. Leyendo el tamaño de la sección `.go.fuzzcntrs` para obtener el número de contadores `N`.
3. Escaneando las reubicaciones de `.text` que hacen referencia a los símbolos de `.go.fuzzcntrs` para recuperar la dirección de cada índice de contador.
4. Resolviendo cada dirección a `file:line` usando `go tool addr2line`.
5. Ejecutando `git blame --line-porcelain` para obtener `committer-time` por línea.
6. Escribiendo `git_recency_map.bin` como `u64 head_time` + `u64 N` + `N * u64 timestamps` (little-endian). Las entradas no mapeadas usan la marca de tiempo `0`.
</details>
<details>
<summary><strong>Benchmark 1 (go-ethereum / geth): línea base vs. git-aware</strong></summary>
Ejecutado 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
```
</details>
## Característica 6: Detectar condiciones de carrera, fugas de goroutines y cuelgues (timeouts) en tiempo de fuzzing
##### Detección de cuelgues confirmados (timeouts de LibAFL)
Al hacer fuzzing con LibAFL, una ejecución del harness puede **agotar el tiempo** (por ejemplo, debido a un deadlock / goroutines bloqueadas esperando, o una ruta extremadamente lenta).
Para reducir falsos positivos, gosentry trata un timeout como un candidato a cuelgue y lo confirma reproduciendo la entrada que agotó el tiempo unas cuantas veces con un timeout mayor. Ante un cuelgue confirmado, gosentry escribe la entrada en `<libafl output dir>/hangs/` y detiene la campaña de fuzzing (lo trata como un bug/crash).
Antes de salir, `golibafl` intenta minimizar la entrada que causa el crash/cuelgue (best-effort; los cuelgues están limitados a ~60s en total).
Nota: la confirmación de cuelgues también se ejecuta durante la importación/generación inicial del corpus, por lo que los objetivos que agotan el tiempo en cada entrada también pueden detectarse de forma determinista.
Esto se configura mediante `--libafl-config`:
- `catch_hangs` (por defecto: `true`)
- `hang_timeout_ms` (por defecto: `10000`)
- `hang_confirm_runs` (por defecto: `3`)
##### Detección de condiciones de carrera (`--catch-races`)
gosentry puede ejecutar un bucle de reproducción `-race` separado que vigila el directorio `queue/` de LibAFL y reproduce las semillas recién descubiertas con `GORACE=halt_on_error=1`.
El bucle de reproducción compila un archivo de harness `-race` separado solo para reproducción (sin instrumentación de cobertura de fuzzing).
Cuando se detecta una condición de carrera durante la reproducción, gosentry imprime el informe completo del detector de carreras antes del resumen `catch-races:` y el comando de reproducción.
Nota: el detector de carreras de Go solo detecta condiciones de carrera **dentro de una única ejecución del harness** (carreras entre goroutines en el mismo proceso que acceden a la misma memoria sin la sincronización adecuada). `--catch-races` omitirá carreras si la semilla no desencadena la concurrencia conflictiva, y no detecta carreras entre procesos.
<details>
<summary><strong>Cómo funciona el modo de detección de condiciones de carrera</strong></summary>
Este modo inicia un pequeño monitor dentro de `go test` (el mismo proceso padre) y se ejecuta durante toda la campaña de fuzzing.
- Cuándo: antes de que se inicie el proceso principal de fuzzing de LibAFL, gosentry compila el harness de reproducción y el runner.
- Supervisión: antes de que comience el fuzzing, gosentry captura el contenido inicial de `<libafl output dir>/queue/` en un conjunto `seen`. Luego, una goroutine consulta `<libafl output dir>/queue/` cada ~1s y solo reproduce las semillas recién creadas (omite los dotfiles y `*.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) │
```
---
[Read more](https://github.com/trailofbits/gosentry)