Skip to content
KitploitKITPLOIT
도구익스플로잇블로그
Log in
제출
도구익스플로잇블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

피드문의개인정보© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
도구/GitHubGitHub/trailofbits/gosentry
Static AnalysisDynamic Analysis (Sandboxing)Vulnerability AnalysisCode AnalysisFuzzingDevSecOpsBinary AnalysisLearning & Education
GitHubtrailofbits/gosentry

gosentry

보안 지향 Go 툴체인으로, 최첨단 퍼징 기능에 중점을 둡니다.

저장소 보기
1173351개월 전Kitploit 검토 완료

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유
# gosentry

[![integration tests](https://github.com/trailofbits/gosentry/actions/workflows/go.yml/badge.svg?branch=master)](https://github.com/trailofbits/gosentry/actions/workflows/go.yml)

gosentry는 Go 코드베이스에 대한 최첨단 퍼징 캠페인을 위해 다양한 기능을 통합한 보안 중심의 Go 툴체인 포크입니다. 이전에 `go test -fuzz`를 사용하고 있었다면 gosentry를 대체 도구로 사용해야 합니다.
여기에는 Go 툴체인에 기본적으로 없는 다양한 퍼징 개선 사항과 버그 탐지 기능이 포함되어 있습니다. 아래 TLDR;을 참조하세요. 관련 블로그 기사는 [여기](https://blog.trailofbits.com/2026/05/12/go-fuzzing-was-missing-half-the-toolkit.-we-forked-the-toolchain-to-fix-it./)에서 읽을 수 있습니다.

**TLDR (기능 및 옵션)**:

- `struct` 입력을 직접 퍼징합니다 (사용자 정의 파서 불필요). `f.Add(Input{N: 7, S: "hi"})`로 시드를 추가한 다음 `f.Fuzz(func(t *testing.T, in Input) { ... })`를 실행하세요.
- 정수 오버플로우 발생 시 패닉을 일으키고 산술 문제를 탐지합니다
- 경로 제약 해결과 같은 최첨단 퍼징 기법을 위해 LibAFL로 퍼징합니다
- 문법에서 입력을 생성/변이시켜 무의미한 변이를 방지합니다. 변이는 유효한 수학 연산을 생성합니다. 예: `X + Y - Z`는 `X / U + Z - 14`가 될 수 있지만 `X + Yè - Z`는 되지 않습니다
- 선택된 함수(예: 치명적 오류 로거)에서 패닉을 일으키고 호출 시 크래시합니다
- 최근 변경된 라인과 새로운 커버리지에 퍼저를 집중시켜 주로 새 커밋을 대상으로 합니다
- 퍼징 시점에 데이터 레이스를 포착합니다
- 퍼징 시점에 Go 누수(leak)를 포착합니다
- 퍼징 시점에 타임아웃으로 중단된 실행을 포착합니다
- 단일 CLI로 퍼징 캠페인 코퍼스에서 HTML 커버리지 보고서를 생성합니다

## 목차

- [빌드](#build)
  - [기능 1: 구조체 인식 퍼징 (입력으로서의 구조체 퍼징)](#feature-1-struct-aware-fuzzing-fuzz-structs-as-inputs)
  - [기능 2: 정수 오버플로우 및 잘림 문제 탐지](#feature-2-integer-overflow-and-truncation-issues-detection)
  - [기능 3: 선택된 함수에 대한 패닉](#feature-3-panic-on-selected-functions)
  - [기능 4: LibAFL 최첨단 퍼징](#feature-4-libafl-state-of-the-art-fuzzing)
  - [기능 5: Git-blame 지향 퍼징 (실험적)](#feature-5-git-blame-oriented-fuzzing-experimental)
  - [기능 6: 퍼징 시점에 경쟁 조건, 고루틴 누수, 중단(hang) 탐지](#feature-6-detect-race-conditions-goroutine-leaks-and-hangs-at-fuzz-time)
  - [기능 7: 문법 기반 퍼징 (Nautilus)](#feature-7-grammar-based-fuzzing-nautilus)
  - [기능 8: 캠페인에서 퍼징 커버리지 보고서 생성](#feature-8-generate-go-coverage-reports-from-fuzzing-campaign)
- [트로피](#trophies)
- [크레딧](#credits)

## 빌드```bash
cd src && ./make.bash # Produces `../bin/go`. See `GOFLAGS` below.
```
> [!TIP]
> 기여자 문서:  `docs/gosentry/index.md`를 읽고 코드 맵, 권장 개발 루프, CI 진입점 및 벤치마크 스크립트를 확인하세요.
> 이 포크는 Pull GitHub App을 사용하여 `golang/go:master`에서 `master`로 PR을 열고 자동 병합하므로 Go 툴체인 최신 업데이트에서 뒤처지지 않습니다.

## 기능 1: 구조체 인식 퍼징 (구조체를 입력으로 퍼징)

#### 개요

Go의 기본 퍼징(`go test -fuzz=...`)은 퍼징 매개변수로 소수의 스칼라 타입(`[]byte`, `string`, 숫자, ...)만 지원합니다. gosentry에서는 이러한 스칼라로 구성된 **복합 타입**(구조체, 배열, 슬라이스, 포인터)도 퍼징할 수 있습니다.
이 기능은 코드가 자연스럽게 구조화된 입력을 받고, 코퍼스를 시드하고 변형하기 위해 맞춤형 인코더/디코더를 만들고 싶지 않을 때 유용합니다.
예시는 `test/gosentry/examples/multiargs`와 `test/gosentry/examples/composite`를 참조하세요.

#### 간단한 예제```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>구조체 시드(<code>f.Add</code>) 및 구조체 퍼징이 작동하는 방식 (만들어진 접착 계층)</strong></summary>

Go의 기본 퍼저는 `struct` 값을 직접 퍼징할 수 없습니다(소수의 스칼라 타입 목록만 변형할 수 있음). gosentry는 작은 접착 계층을 추가합니다: 퍼즈 타겟이 (`Input` 같은) 복합 타입을 사용할 때, gosentry는 내부적으로 단일 `[]byte`를 퍼징합니다. 매 실행마다 그 바이트를 구조체로 **디코딩**하고(필드 단위로, 슬라이스/배열/포인터에 대해 재귀적으로), 그런 다음 디코딩된 값으로 `f.Fuzz` 콜백을 호출합니다. 시드에도 동일한 **인코딩**이 사용되므로 `f.Add(Input{...})`는 인코딩된 `[]byte` 코퍼스 항목이 되어 퍼저가 다른 시드처럼 재사용하고 변형할 수 있습니다.

퍼저(LibAFL 포함)는 원시 바이트를 변형하므로, **어떤** 바이트 슬라이스든 "어떤" 구조체 값으로 바꾸고 계속 진행할 수 있는 디코더가 필요합니다. JSON/`gob`는 대부분의 임의 입력을 거부하고(커버리지에 나쁨), 내보내지지 않은(unexported) 필드도 채우지 않으며, 퍼징은 종종 불변식을 깨뜨리는 데서 이점을 얻습니다. 이 사용자 정의 형식은 작고, 빠르고, 결정적이며, 잘못된 데이터에 관대합니다.

내부적으로 이는 gosentry 자체의 단순 바이너리 형식(`gob`도 JSON도 아님)을 사용합니다. 코드는 `src/testing/libafl.go`에 있습니다:
- 인코딩: `libaflMarshalInputs` / `libaflAppendValue`
- 디코딩: `libaflUnmarshalArgs` / `libaflDecodeValue`

인코딩 규칙(높은 수준):
- `bool`: 1바이트 (`0` 또는 `1`)
- 정수: 리틀엔디언 바이트 (`int`/`uint`는 8바이트)
- 부동소수점: 리틀엔디언 IEEE-754 비트 (`float32` = 4바이트, `float64` = 8바이트)
- `string`: `uvarint(len)` 다음에 원시 문자열 바이트
- `[]byte`: `uvarint(len)` 다음에 원시 바이트
- 기타 슬라이스: `uvarint(len)` 다음에 각 요소 인코딩
- 구조체: 선언 순서대로 필드 인코딩
- 포인터: 1바이트 (`0` = nil, `1` = 존재함) 다음에 포인팅하는 값 인코딩

 
</details>

## 기능 2: 정수 오버플로 및 절단(truncation) 문제 탐지

#### 개요

이 작업은 이전에 개발된 [go-panikint](https://github.com/trailofbits/go-panikint)에서 영감을 받았습니다. 정수 산술 연산에 대한 오버플로/언더플로 탐지와 (선택적으로) 정수 변환에 대한 타입 절단 탐지를 추가합니다. 오버플로나 절단이 감지되면 관련된 특정 연산 유형과 정수 타입을 포함한 상세한 오류 메시지와 함께 패닉이 발생합니다.

_산술 연산_: 부호 있는 정수와 부호 없는 정수 타입 모두에 대해 덧셈 `+`, 뺄셈 `-`, 곱셈 `*`, 나눗셈 `/`을 처리합니다. 부호 있는 정수는 `int8`, `int16`, `int32`를 다룹니다. 부호 없는 정수는 `uint8`, `uint16`, `uint32`, `uint64`를 다룹니다. 나눗셈의 경우 부호 있는 정수에 대한 `MIN_INT / -1` 오버플로 조건을 특별히 감지합니다. `int64`와 `uintptr`은 산술 연산에 대해 검사하지 않습니다.

_타입 절단 탐지_: 데이터 손실이 발생할 수 있는 정수 타입 변환을 감지합니다. 모든 정수 타입을 다룹니다: `int8`, `int16`, `int32`, `int64`, `uint8`, `uint16`, `uint32`, `uint64`. 플랫폼 의존적 사용으로 인해 `uintptr`은 제외합니다. 이 기능은 기본적으로 비활성화되어 있습니다.

오버플로 탐지는 기본적으로 활성화되어 있습니다. 비활성화하려면 `./make.bash` 앞에 `GOFLAGS='-gcflags=-overflowdetect=false'`를 추가하세요. 절단 문제 검사기도 다음과 같이 활성화할 수 있습니다: `-gcflags=-truncationdetect=true`

#### 작동 방식

이 기능은 컴파일러 SSA 생성을 패치하여 정수 산술 연산과 정수 변환에 추가 런타임 검사를 삽입합니다. 이 검사는 런타임을 호출하여 버그가 감지되면 상세한 오류 메시지와 함께 패닉을 발생시킵니다. 검사는 소스 위치 기반 필터링을 사용하여 적용되므로 사용자 코드는 계측되지만 표준 라이브러리 파일과 의존성(모듈 캐시 및 `vendor/`)은 건너뜁니다.

이에 대한 관련 블로그 게시물은 [**여기**](https://blog.trailofbits.com/2025/12/31/detect-gos-silent-arithmetic-bugs-with-go-panikint/)에서 읽을 수 있습니다.

#### 오탐(false positive) 억제

특정 보고를 억제하려면 연산과 같은 줄이나 바로 위 줄에 마커를 추가하세요:

- 오버플로/언더플로: `overflow_false_positive`
- 절단: `truncation_false_positive`

예시:```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
```
때로는 이것이 작동하지 않을 수 있는데, 그 이유는 Go가 함수를 인라인 처리하기 때문입니다. `// overflow_false_positive`만으로 충분하지 않다면 함수 시그니처 앞에 `//go:noinline`을 추가하세요.

## 기능 3: 선택된 함수에서 패닉 발생

퍼징 대상에서 특정 함수가 호출될 때 패닉을 유발하는 것에 관심이 있을 수 있습니다. 예를 들어, 일부 소프트웨어는 패닉을 일으키는 대신 `log.error` 메시지를 출력할 수 있는데, 이러한 조건은 종종 보안 연구자들이 퍼징 중에 탐지하고자 하는 상태를 나타냅니다.
그러나 이러한 오류는 일반적으로 내부적으로 처리되므로(예: 재시도 또는 일시 중지 메커니즘을 통해, 또는 로그에 메시지를 출력하여) 퍼저가 거의 인식하지 못합니다. 이 기능의 목적은 이 문제를 해결하는 것입니다.

#### 사용 방법

gosentry를 컴파일한 후 `--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"
```
위 예제는 `(*Logger).Warning` 또는 `(*Logger).Error`가 호출될 때 패닉이 발생합니다(쉼표로 구분된 목록).

<details>
<summary><strong>선택한 함수에서 패닉이 발생하는 기능의 작동 방식</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"                              │
└───────────────────────────────────────────────────────────────────────────┘
```
In practice, this makes any matched call site behave like a crash/panic for fuzzers (note: only static call sites can be trapped).
</details>

## 기능 4: LibAFL 최첨단 퍼징
 
LibAFL은 기존 Go 퍼저보다 *훨씬* 더 나은 성능을 제공합니다. 퍼징(`go test -fuzz=...`) 시 gosentry는 [LibAFL](https://github.com/AFLplusplus/LibAFL)을 **기본적으로** 사용합니다 (런너는 `golibafl/`에 있음).
 
안정성 참고 사항: LibAFL 모드에서 gosentry는 `GODEBUG=updatemaxprocs=0`을 강제하여 (런타임 자동 GOMAXPROCS 업데이트를 비활성화) 간헐적인 Linux CI 충돌("sync: inconsistent mutex state")을 방지합니다. 자세한 내용은 `misc/gosentry/USE_LIBAFL.md`에 있습니다.
 
LibAFL(기본값)을 사용할 때는 git 인식 스케줄링을 활성화할지 여부를 명시적으로 선택해야 합니다: `--focus-on-new-code=true|false`. 더 많은 문서는 [이 Markdown 파일.](https://github.com/trailofbits/gosentry/blob/master/misc/gosentry/USE_LIBAFL.md)에 있습니다.
선택적으로 LibAFL용 JSONC 구성 파일을 전달할 수도 있습니다 (문법 퍼징 옵션 포함), [여기.](https://github.com/trailofbits/gosentry/blob/master/misc/gosentry/libafl.config.jsonc)를 참조하세요.
`"stop_all_fuzzers_on_panic": false` 설정 시 LibAFL은 각 충돌을 저장하고 클라이언트를 다시 시작하여 퍼징을 계속합니다.```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
```
`-fuzztime=1m`을 사용하면 1분 후에 LibAFL 캠페인이 중지됩니다.

LibAFL 캠페인 코퍼스에서 커버리지 리포트를 생성하는 방법은 [Feature 8](#feature-8-generate-go-coverage-reports-from-fuzzing-campaign)에 문서화되어 있습니다.

문법 기반 퍼징(Nautilus)은 [Feature 7](#feature-7-grammar-based-fuzzing-nautilus)에 문서화되어 있습니다.

<details>
<summary><strong>Go + 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     │
└───────────────────────────────────────────────────────────────────────────┘
```
In `--use-libafl` 모드에서 gosentry는 `libharness.a`를 빌드하고, Rust `golibafl` 러너가 libFuzzer 진입점을 통해 이를 프로세스 내에서 구동합니다. 참고: `HARNESS_LIB`는 임의의 하네스 아카이브 이름을 가리킬 수 있습니다(예: `--catch-races`에서 사용하는 `libharness_race.a`).
</details>

<details>
<summary><strong>커버리지 계측 작동 방식(LibAFL + Go 타겟)</strong></summary>

`--use-libafl` 모드에서 gosentry는 커버리지 계측을 활성화한 상태로 Go 하네스를 컴파일합니다. 그러면 프로그램의 서로 다른 부분이 실행될 때 변경되는 작은 카운터가 코드에 추가됩니다. 하네스가 `golibafl` 내부에서 시작되면 Go 런타임이 이러한 카운터를 LibAFL에 노출합니다. LibAFL은 각 입력 후 카운터를 읽어 어떤 코드가 실행되었는지 확인하고, 그 커버리지를 사용해 다음 변이를 안내합니다.

</details>



##### 제한 사항
LibAFL을 사용하는 동기에 대해 이야기해 보겠습니다. `go test -fuzz`를 사용한 퍼징은 최신 퍼징 기법보다 _훨씬_ 뒤떨어져 있습니다. 좋은 예로 AFL++의 CMPLOG/Redqueen이 있습니다. 이러한 기능은 퍼저가 특정 제약 조건을 해결할 수 있게 해줍니다. 다음 스니펫을 가정해 보겠습니다.```go
if input == "IMARANDOMSTRINGJUSTCMPLOGMEMAN" {
	panic("this string is illegal")
}
```
SOTA 퍼저(AFL++ 또는 LibAFL 등)는 그 경우 패닉을 즉시 발견할 것입니다. 하지만 Go 네이티브 퍼저는 그렇지 않습니다. 이는 커버리지 탐색을 **크게** 제한하는 거대한 격차입니다.

아래 벤치마크는 이러한 한계를 보여줍니다. 해당 벤치마크는 [gosentry-bench-libafl 저장소](https://github.com/kevin-valerio/gosentry-bench-libafl/tree/main)를 통해 **재현**하고 개선할 수 있습니다.

##### 벤치마크 1:

아래 차트는 LibAFL과 Go 네이티브 퍼저를 사용하여 Google의 [UUID](https://github.com/google/uuid)를 퍼징하는 동안 커버된 라인 수의 변화를 보여줍니다.
![BENCH1](https://assets.kitploit.com/production/public/readmes/41918/7b1ea005996846c9ee4d7c0d42fee26731050d10b6b21d59c551e4b1b956923e.png "BENCH1")

##### 벤치마크 2:

아래 차트는 LibAFL과 Go 네이티브 퍼저를 사용하여 [go-ethereum](https://github.com/ethereum/go-ethereum)을 퍼징하는 동안 커버된 라인 수의 변화를 보여줍니다.
![BENCH2](https://assets.kitploit.com/production/public/readmes/41918/61aab8540ab8d339307854de6737a405056c3c8ad83a822df40f74e362b93021.png "BENCH2")



#### 예제
`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
```
Ctrl+C로 퍼즈 캠페인을 중지합니다.

##### LibAFL 출력 디렉터리 (캠페인 식별 / 코퍼스 재사용)

gosentry는 LibAFL의 캠페인 상태(코퍼스, 크래시 등)를 Go의 퍼즈 캐시 루트(대략 `$(go env GOCACHE)/fuzz`) 아래에 저장하며, **동일한 패키지 + 동일한 퍼즈 타깃**(및 동일한 프로젝트 루트)에서 파생된 결정적 디렉터리에 저장합니다.

즉, 동일한 퍼즈 캠페인을 중지(Ctrl+C)했다가 다시 시작하면 기본적으로 이전 LibAFL `queue/` 코퍼스에서 계속 진행됩니다.

경로는 실행 종료 시 출력됩니다:```text
libafl output dir: /full/path/to/.../fuzz/<pkg import path>/libafl/<project>/<harness>
```
참고:
- `<harness>`는 `-fuzz`가 `FuzzXxx`(또는 `^FuzzXxx$`)와 같은 단순 식별자일 때의 퍼즈 타깃 이름이며, 그 외의 경우에는 `pattern-<hash>`입니다.
- 커버리지 생성(`--generate-coverage`)은 올바른 `queue/` 코퍼스를 찾기 위해 동일한 규칙을 사용하므로, 동일한 패키지에서 동일한 `-fuzz=...`로 실행해야 합니다.

## 기능 5: Git-blame 지향 퍼징(실험적)

#### 개요

커버리지 기반 퍼징은 새로운 경로를 탐색하는 데 뛰어나지만, 모든 커버된 코드를 동등하게 흥미로운 것으로 취급합니다. 대규모 코드베이스를 퍼징할 때는 회귀 및 버그가 더 자주 도입될 수 있는 최근 수정된 코드 쪽으로 퍼저를 편향시키고 싶을 수 있습니다. LibAFL 모드에서 gosentry는 `git blame`을 사용하여 최근에 변경된 줄을 실행하는 입력을 우선시할 수 있습니다(커버리지 가이던스를 기본 신호로 유지하면서).

이 작업은 [LibAFL-git-aware](https://github.com/kevin-valerio/LibAFL-git-aware)의 이전 작업을 기반으로 합니다. 모든 기술적 세부 사항은 해당 문서에 정리되어 있습니다.

#### 사용 방법

`--focus-on-new-code=true`로 git-aware 스케줄링을 활성화하세요:```bash
./bin/go test -fuzz=FuzzHarness --use-libafl --focus-on-new-code=true --catch-races=false --catch-leaks=false
```
이 모드는 커버리지 카운터를 소스 `file:line`으로 다시 매핑하기 위해 `git`(`git blame` 실행용)과 `go tool addr2line`이 필요합니다.

<details>
<summary><strong>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>gosentry가 <code>git_recency_map.bin</code>을 빌드하는 방법</strong></summary>

`.go.fuzzcntrs`는 Go의 libFuzzer 스타일 **8비트 커버리지 카운터**를 보관하는 링커 섹션입니다(`-gcflags=all=-d=libfuzzer`로 활성화). 각 바이트는 "이 계측된 지점이 얼마나 많이 적중되었는지"를 나타냅니다. `--focus-on-new-code=true`인 경우 `golibafl`은 다음 단계로 `git_recency_map.bin`을 생성합니다:
  1. `libharness.a`에서 `go.o`를 추출합니다.
  2. `.go.fuzzcntrs` 섹션 크기를 읽어 카운터 개수 `N`을 구합니다.
  3. `.go.fuzzcntrs` 심볼을 참조하는 `.text` 재배치를 스캔하여 각 카운터 인덱스의 주소를 복구합니다.
  4. `go tool addr2line`을 사용하여 각 주소를 `file:line`으로 변환합니다.
  5. `git blame --line-porcelain`을 실행하여 각 줄의 `committer-time`을 가져옵니다.
  6. `git_recency_map.bin`을 `u64 head_time` + `u64 N` + `N * u64 timestamps`(리틀 엔디언) 형식으로 작성합니다. 매핑되지 않은 항목은 타임스탬프 `0`을 사용합니다.

</details>

<details>
<summary><strong>벤치마크 1 (go-ethereum / geth): 기준(baseline) 대비 git 인식(git-aware)</strong></summary>

`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
```
### Feature 6: 퍼즈 시점에 레이스 조건, 고루틴 누수, 행(hangs, 타임아웃) 감지

##### 확인된 행(hangs) 포착 (LibAFL 타임아웃)

LibAFL로 퍼징할 때 하네스 실행이 **타임아웃**될 수 있습니다 (예: 교착 상태, 대기 중 멈춘 고루틴, 또는 매우 느린 경로).

오탐을 줄이기 위해 gosentry는 타임아웃을 행(hang) 후보로 간주하고, 더 큰 타임아웃으로 해당 입력을 여러 번 재실행하여 확인합니다. 행이 확인되면 gosentry는 입력을 `<libafl output dir>/hangs/`에 기록하고 퍼즈 캠페인을 중단합니다 (버그/크래시로 취급).

종료 전에 `golibafl`은 크래시/행 입력을 최소화하려 시도합니다 (가능한 선에서; 행은 총 ~60초로 제한).

참고: 행 확인은 초기 코퍼스 가져오기/생성 중에도 실행되므로, 모든 입력에서 타임아웃되는 타깃도 결정적으로 감지할 수 있습니다.

이는 `--libafl-config`로 구성됩니다:
- `catch_hangs` (기본값: `true`)
- `hang_timeout_ms` (기본값: `10000`)
- `hang_confirm_runs` (기본값: `3`)

##### 데이터 레이스 포착 (`--catch-races`)

gosentry는 LibAFL `queue/` 디렉터리를 감시하고 `GORACE=halt_on_error=1`로 새로 발견된 시드를 재실행하는 별도의 `-race` 재생 루프를 실행할 수 있습니다.

재생 루프는 재생 전용(퍼즈 커버리지 계측 없음)을 위해 별도의 `-race` 하네스 아카이브를 빌드합니다.

재생 중 데이터 레이스가 감지되면 gosentry는 `catch-races:` 요약 및 재현 명령 앞에 전체 레이스 감지기 보고서를 출력합니다.

참고: Go의 레이스 감지기는 **단일 하네스 실행 내**에서만 데이터 레이스를 감지합니다 (동일 프로세스의 고루틴들이 적절한 동기화 없이 동일 메모리에 접근하는 레이스). `--catch-races`는 시드가 레이스가 있는 동시성을 유발하지 않으면 레이스를 놓칠 수 있으며, 프로세스 간 레이스는 감지하지 못합니다.

<details>
<summary><strong>데이터 레이스 모드 작동 방식</strong></summary>

이 모드는 `go test` 내부(동일한 부모 프로세스)에 작은 모니터를 시작하며, 전체 퍼즈 캠페인 동안 실행됩니다.

- 시점: 메인 LibAFL 퍼징 프로세스가 시작되기 전에 gosentry는 재생 하네스 + 러너를 빌드합니다.
- 모니터링: 퍼징이 시작되기 전에 gosentry는 `<libafl output dir>/queue/`의 초기 내용을 `seen` 집합으로 스냅샷합니다. 그런 다음 고루틴이 약 1초마다 `<libafl output dir>/queue/`를 폴링하여 새로 생성된 시드만 재생합니다 (점파일과 `*.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>`       │
```

---

[Read more](https://github.com/trailofbits/gosentry)
도구 다운로드