Merge pull request #18415 from roidelapluie/roidelapluie/proto-fuzz

fuzzing: add FuzzParseProtobuf fuzz target and corpus generation
This commit is contained in:
Julien
2026-04-01 14:41:42 +02:00
committed by GitHub
4 changed files with 2002 additions and 2 deletions

View File

@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
fuzz_test: [FuzzParseMetricText, FuzzParseOpenMetric, FuzzParseMetricSelector, FuzzParseExpr, FuzzXORChunk, FuzzXOR2Chunk]
fuzz_test: [FuzzParseMetricText, FuzzParseOpenMetric, FuzzParseMetricSelector, FuzzParseExpr, FuzzXORChunk, FuzzXOR2Chunk, FuzzParseProtobuf]
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -21,7 +21,7 @@ jobs:
with:
go-version: 1.26.x
- name: Run Fuzzing
run: go test -fuzz=${{ matrix.fuzz_test }}$ -fuzztime=5m ./util/fuzzing
run: go test -fuzz=${{ matrix.fuzz_test }}$ -fuzztime=4m ./util/fuzzing
continue-on-error: true
id: fuzz
- name: Upload Crash Artifacts

View File

@@ -23,6 +23,7 @@ import (
"os"
"path/filepath"
"sort"
"strconv"
"github.com/prometheus/prometheus/util/fuzzing"
)
@@ -81,6 +82,16 @@ func run() error {
}
fmt.Printf("Generated fuzzXOR2Chunk_seed_corpus.zip with %d entries.\n", len(xor2Seeds))
// Generate FuzzParseProtobuf seed corpus.
protobufSeeds, err := fuzzing.GetCorpusForFuzzParseProtobuf()
if err != nil {
return fmt.Errorf("failed to get corpus for FuzzParseProtobuf: %w", err)
}
if err := generateZipFromProtobufSeeds("fuzzParseProtobuf", protobufSeeds); err != nil {
return fmt.Errorf("failed to generate fuzzParseProtobuf_seed_corpus.zip: %w", err)
}
fmt.Printf("Generated fuzzParseProtobuf_seed_corpus.zip with %d entries.\n", len(protobufSeeds))
return nil
}
@@ -178,3 +189,19 @@ func generateZipFromXOR2ChunkSeeds(fuzzName string, seeds []fuzzing.XOR2ChunkFuz
}
return generateZipFromSeedEntries(fuzzName, entries)
}
// generateZipFromProtobufSeeds creates a seed corpus ZIP file for FuzzParseProtobuf.
func generateZipFromProtobufSeeds(fuzzName string, seeds []fuzzing.ProtobufCorpusSeed) error {
entries := make([][]byte, len(seeds))
for i, s := range seeds {
entries[i] = []byte(fmt.Sprintf(
"go test fuzz v1\n[]byte(%s)\nbool(%v)\nbool(%v)\nbool(%v)\nbool(%v)\n",
strconv.Quote(string(s.Data)),
s.IgnoreNative,
s.ParseClassic,
s.ConvertNHCB,
s.TypeAndUnit,
))
}
return generateZipFromSeedEntries(fuzzName, entries)
}

File diff suppressed because it is too large Load Diff

View File

@@ -20,6 +20,7 @@ import (
"math/rand"
"testing"
"github.com/prometheus/prometheus/model/exemplar"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/model/textparse"
"github.com/prometheus/prometheus/model/value"
@@ -284,6 +285,67 @@ func FuzzXOR2Chunk(f *testing.F) {
})
}
// FuzzParseProtobuf fuzzes the protobuf exposition-format parser. The four bool
// parameters exercise different combinations of parser options:
//
// - ignoreNative: ignore native histogram parts of the payload
// - parseClassic: also emit the classic representation when a native histogram is present
// - convertNHCB: convert classic histograms to native histograms with custom buckets
// - typeAndUnit: include type and unit labels on each series
func FuzzParseProtobuf(f *testing.F) {
corpus, err := GetCorpusForFuzzParseProtobuf()
if err != nil {
f.Fatal(err)
}
for _, s := range corpus {
f.Add(s.Data, s.IgnoreNative, s.ParseClassic, s.ConvertNHCB, s.TypeAndUnit)
}
f.Fuzz(func(t *testing.T, in []byte, ignoreNative, parseClassic, convertNHCB, typeAndUnit bool) {
if len(in) > maxInputSize {
t.Skip()
}
p := textparse.NewProtobufParser(in, ignoreNative, parseClassic, convertNHCB, typeAndUnit, symbolTable)
var err error
for {
entry, nextErr := p.Next()
err = nextErr
if err != nil {
break
}
switch entry {
case textparse.EntryHelp:
_, _ = p.Help()
case textparse.EntryType:
_, _ = p.Type()
case textparse.EntryUnit:
_, _ = p.Unit()
case textparse.EntrySeries:
var lbs labels.Labels
p.Labels(&lbs)
_, _, _ = p.Series()
_ = p.StartTimestamp()
var ex exemplar.Exemplar
for p.Exemplar(&ex) {
}
case textparse.EntryHistogram:
var lbs labels.Labels
p.Labels(&lbs)
_, _, _, _ = p.Histogram()
_ = p.StartTimestamp()
var ex exemplar.Exemplar
for p.Exemplar(&ex) {
}
}
}
if errors.Is(err, io.EOF) {
err = nil
}
// We don't care about errors, just that we don't panic.
_ = err
})
}
// FuzzParseExpr fuzzes the expression parser.
func FuzzParseExpr(f *testing.F) {
// Add seed corpus from built-in test expressions