mirror of
https://github.com/prometheus/prometheus.git
synced 2026-02-03 20:39:32 -05:00
Fuzzing: Move to go fuzzing
Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com>
This commit is contained in:
parent
9cb3641ccd
commit
de0a864b5c
7 changed files with 448 additions and 19 deletions
40
.github/workflows/fuzzing.yml
vendored
40
.github/workflows/fuzzing.yml
vendored
|
|
@ -1,30 +1,32 @@
|
||||||
name: CIFuzz
|
name: Fuzzing
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Fuzzing:
|
fuzzing:
|
||||||
|
name: Run Go Fuzz Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
fuzz_test: [FuzzParseMetricText, FuzzParseOpenMetric, FuzzParseMetricSelector, FuzzParseExpr]
|
||||||
steps:
|
steps:
|
||||||
- name: Build Fuzzers
|
- name: Checkout repository
|
||||||
id: build
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@4bf20ff8dfda18ad651583ebca9fb17a7ce1940a # master
|
|
||||||
with:
|
with:
|
||||||
oss-fuzz-project-name: "prometheus"
|
persist-credentials: false
|
||||||
dry-run: false
|
- name: Install Go
|
||||||
- name: Run Fuzzers
|
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||||
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@4bf20ff8dfda18ad651583ebca9fb17a7ce1940a # master
|
|
||||||
# Note: Regularly check for updates to the pinned commit hash at:
|
|
||||||
# https://github.com/google/oss-fuzz/tree/master/infra/cifuzz/actions/run_fuzzers
|
|
||||||
with:
|
with:
|
||||||
oss-fuzz-project-name: "prometheus"
|
go-version: 1.25.x
|
||||||
fuzz-seconds: 600
|
- name: Run Fuzzing
|
||||||
dry-run: false
|
run: go test -fuzz=${{ matrix.fuzz_test }}$ -fuzztime=5m ./util/fuzzing
|
||||||
- name: Upload Crash
|
continue-on-error: true
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
id: fuzz
|
||||||
if: failure() && steps.build.outcome == 'success'
|
- name: Upload Crash Artifacts
|
||||||
|
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||||
|
if: failure()
|
||||||
with:
|
with:
|
||||||
name: artifacts
|
name: fuzz-artifacts-${{ matrix.fuzz_test }}
|
||||||
path: ./out/artifacts
|
path: promql/testdata/fuzz/${{ matrix.fuzz_test }}
|
||||||
|
|
|
||||||
5
Makefile
5
Makefile
|
|
@ -220,3 +220,8 @@ check-node-version:
|
||||||
bump-go-version:
|
bump-go-version:
|
||||||
@echo ">> bumping Go minor version"
|
@echo ">> bumping Go minor version"
|
||||||
@./scripts/bump_go_version.sh
|
@./scripts/bump_go_version.sh
|
||||||
|
|
||||||
|
.PHONY: generate-fuzzing-seed-corpus
|
||||||
|
generate-fuzzing-seed-corpus:
|
||||||
|
@echo ">> Generating fuzzing seed corpus"
|
||||||
|
@$(GO) generate -tags fuzzing ./util/fuzzing/corpus_gen
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,39 @@ func NewTestEngineWithOpts(tb testing.TB, opts promql.EngineOpts) *promql.Engine
|
||||||
return ng
|
return ng
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBuiltInExprs returns all the eval statement expressions from the built-in test files.
|
||||||
|
func GetBuiltInExprs() ([]string, error) {
|
||||||
|
files, err := fs.Glob(testsFs, "*/*.test")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var exprs []string
|
||||||
|
for _, fn := range files {
|
||||||
|
content, err := fs.ReadFile(testsFs, fn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a minimal test struct just for parsing
|
||||||
|
testInstance := &test{
|
||||||
|
cmds: []testCommand{},
|
||||||
|
}
|
||||||
|
if err := testInstance.parse(string(content)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract expressions from eval commands
|
||||||
|
for _, cmd := range testInstance.cmds {
|
||||||
|
if evalCmd, ok := cmd.(*evalCmd); ok {
|
||||||
|
exprs = append(exprs, evalCmd.expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return exprs, nil
|
||||||
|
}
|
||||||
|
|
||||||
// RunBuiltinTests runs an acceptance test suite against the provided engine.
|
// RunBuiltinTests runs an acceptance test suite against the provided engine.
|
||||||
func RunBuiltinTests(t TBRun, engine promql.QueryEngine) {
|
func RunBuiltinTests(t TBRun, engine promql.QueryEngine) {
|
||||||
RunBuiltinTestsWithStorage(t, engine, newTestStorage)
|
RunBuiltinTestsWithStorage(t, engine, newTestStorage)
|
||||||
|
|
|
||||||
1
util/fuzzing/.gitignore
vendored
Normal file
1
util/fuzzing/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Fuzz*_seed_corpus.zip
|
||||||
122
util/fuzzing/corpus.go
Normal file
122
util/fuzzing/corpus.go
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
// Copyright The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package fuzzing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/prometheus/prometheus/promql/parser"
|
||||||
|
"github.com/prometheus/prometheus/promql/promqltest"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetCorpusForFuzzParseMetricText returns the seed corpus for FuzzParseMetricText.
|
||||||
|
func GetCorpusForFuzzParseMetricText() [][]byte {
|
||||||
|
return [][]byte{
|
||||||
|
[]byte(""),
|
||||||
|
[]byte("metric_name 1.0"),
|
||||||
|
[]byte("# HELP metric_name help text\n# TYPE metric_name counter\nmetric_name 1.0"),
|
||||||
|
[]byte("o { quantile = \"1.0\", a = \"b\" } 8.3835e-05"),
|
||||||
|
[]byte("# HELP api_http_request_count The total number of HTTP requests.\n# TYPE api_http_request_count counter\nhttp_request_count{method=\"post\",code=\"200\"} 1027 1395066363000"),
|
||||||
|
[]byte("msdos_file_access_time_ms{path=\"C:\\\\DIR\\\\FILE.TXT\",error=\"Cannot find file:\\n\\\"FILE.TXT\\\"\"} 1.234e3"),
|
||||||
|
[]byte("metric_without_timestamp_and_labels 12.47"),
|
||||||
|
[]byte("something_weird{problem=\"division by zero\"} +Inf -3982045"),
|
||||||
|
[]byte("http_request_duration_seconds_bucket{le=\"+Inf\"} 144320"),
|
||||||
|
[]byte("go_gc_duration_seconds{ quantile=\"0.9\", a=\"b\"} 8.3835e-05"),
|
||||||
|
[]byte("go_gc_duration_seconds{ quantile=\"1.0\", a=\"b\" } 8.3835e-05"),
|
||||||
|
[]byte("go_gc_duration_seconds{ quantile = \"1.0\", a = \"b\" } 8.3835e-05"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCorpusForFuzzParseOpenMetric returns the seed corpus for FuzzParseOpenMetric.
|
||||||
|
func GetCorpusForFuzzParseOpenMetric() [][]byte {
|
||||||
|
return [][]byte{
|
||||||
|
[]byte(""),
|
||||||
|
[]byte("# TYPE metric_name counter\nmetric_name_total 1.0"),
|
||||||
|
[]byte("# HELP metric_name help text\n# TYPE metric_name counter\nmetric_name_total 1.0\n# EOF"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCorpusForFuzzParseMetricSelector returns the seed corpus for FuzzParseMetricSelector.
|
||||||
|
func GetCorpusForFuzzParseMetricSelector() []string {
|
||||||
|
return []string{
|
||||||
|
"",
|
||||||
|
"metric_name",
|
||||||
|
`metric_name{label="value"}`,
|
||||||
|
`{label="value"}`,
|
||||||
|
`metric_name{label=~"val.*"}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCorpusForFuzzParseExpr returns the seed corpus for FuzzParseExpr.
|
||||||
|
func GetCorpusForFuzzParseExpr() ([]string, error) {
|
||||||
|
// Enable experimental features to parse all test expressions.
|
||||||
|
parser.EnableExperimentalFunctions = true
|
||||||
|
parser.ExperimentalDurationExpr = true
|
||||||
|
parser.EnableExtendedRangeSelectors = true
|
||||||
|
defer func() {
|
||||||
|
parser.EnableExperimentalFunctions = false
|
||||||
|
parser.ExperimentalDurationExpr = false
|
||||||
|
parser.EnableExtendedRangeSelectors = false
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Get built-in test expressions.
|
||||||
|
builtInExprs, err := promqltest.GetBuiltInExprs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add additional seed corpus.
|
||||||
|
additionalExprs := []string{
|
||||||
|
"",
|
||||||
|
"1",
|
||||||
|
"metric_name",
|
||||||
|
`"str"`,
|
||||||
|
// Numeric literals
|
||||||
|
".5",
|
||||||
|
"5.",
|
||||||
|
"123.4567",
|
||||||
|
"5e3",
|
||||||
|
"5e-3",
|
||||||
|
"+5.5e-3",
|
||||||
|
"0xc",
|
||||||
|
"0755",
|
||||||
|
"-0755",
|
||||||
|
"+Inf",
|
||||||
|
"-Inf",
|
||||||
|
// Basic binary operations
|
||||||
|
"1 + 1",
|
||||||
|
"1 - 1",
|
||||||
|
"1 * 1",
|
||||||
|
"1 / 1",
|
||||||
|
"1 % 1",
|
||||||
|
// Comparison operators
|
||||||
|
"1 == 1",
|
||||||
|
"1 != 1",
|
||||||
|
"1 > 1",
|
||||||
|
"1 >= 1",
|
||||||
|
"1 < 1",
|
||||||
|
"1 <= 1",
|
||||||
|
// Operations with identifiers
|
||||||
|
"foo == 1",
|
||||||
|
"foo * bar",
|
||||||
|
"2.5 / bar",
|
||||||
|
"foo and bar",
|
||||||
|
"foo or bar",
|
||||||
|
// Complex expressions
|
||||||
|
"+1 + -2 * 1",
|
||||||
|
"1 + 2/(3*1)",
|
||||||
|
// Comment
|
||||||
|
"#comment",
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(builtInExprs, additionalExprs...), nil
|
||||||
|
}
|
||||||
116
util/fuzzing/corpus_gen/main.go
Normal file
116
util/fuzzing/corpus_gen/main.go
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
// Copyright The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build fuzzing
|
||||||
|
|
||||||
|
//go:generate go run -tags fuzzing .
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/prometheus/prometheus/util/fuzzing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if err := run(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Println("Successfully generated all seed corpus ZIP files.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func run() error {
|
||||||
|
// Generate FuzzParseExpr seed corpus.
|
||||||
|
exprs, err := fuzzing.GetCorpusForFuzzParseExpr()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get corpus for FuzzParseExpr: %w", err)
|
||||||
|
}
|
||||||
|
if err := generateZipFromStrings("fuzzParseExpr", exprs); err != nil {
|
||||||
|
return fmt.Errorf("failed to generate FuzzParseExpr_seed_corpus.zip: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Generated fuzzParseExpr_seed_corpus.zip with %d entries.\n", len(exprs))
|
||||||
|
|
||||||
|
// Generate FuzzParseMetricSelector seed corpus.
|
||||||
|
selectors := fuzzing.GetCorpusForFuzzParseMetricSelector()
|
||||||
|
if err := generateZipFromStrings("fuzzParseMetricSelector", selectors); err != nil {
|
||||||
|
return fmt.Errorf("failed to generate FuzzParseMetricSelector_seed_corpus.zip: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Generated fuzzParseMetricSelector_seed_corpus.zip with %d entries.\n", len(selectors))
|
||||||
|
|
||||||
|
// Generate FuzzParseMetricText seed corpus.
|
||||||
|
metrics := fuzzing.GetCorpusForFuzzParseMetricText()
|
||||||
|
if err := generateZipFromBytes("fuzzParseMetricText", metrics); err != nil {
|
||||||
|
return fmt.Errorf("failed to generate FuzzParseMetricText_seed_corpus.zip: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Generated fuzzParseMetricText_seed_corpus.zip with %d entries.\n", len(metrics))
|
||||||
|
|
||||||
|
// Generate FuzzParseOpenMetric seed corpus.
|
||||||
|
openMetrics := fuzzing.GetCorpusForFuzzParseOpenMetric()
|
||||||
|
if err := generateZipFromBytes("fuzzParseOpenMetric", openMetrics); err != nil {
|
||||||
|
return fmt.Errorf("failed to generate FuzzParseOpenMetric_seed_corpus.zip: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Generated fuzzParseOpenMetric_seed_corpus.zip with %d entries.\n", len(openMetrics))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateZipFromBytes creates a seed corpus ZIP file from a slice of byte slices.
|
||||||
|
func generateZipFromBytes(fuzzName string, corpus [][]byte) error {
|
||||||
|
// Sort corpus deterministically.
|
||||||
|
sorted := make([][]byte, len(corpus))
|
||||||
|
copy(sorted, corpus)
|
||||||
|
sort.Slice(sorted, func(i, j int) bool {
|
||||||
|
return string(sorted[i]) < string(sorted[j])
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create ZIP file in parent directory.
|
||||||
|
zipPath := filepath.Join("..", fuzzName+"_seed_corpus.zip")
|
||||||
|
zipFile, err := os.Create(zipPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create zip file: %w", err)
|
||||||
|
}
|
||||||
|
defer zipFile.Close()
|
||||||
|
|
||||||
|
zipWriter := zip.NewWriter(zipFile)
|
||||||
|
defer zipWriter.Close()
|
||||||
|
|
||||||
|
// Add each corpus entry as a file.
|
||||||
|
for i, entry := range sorted {
|
||||||
|
fileName := fmt.Sprintf("expr%d", i)
|
||||||
|
writer, err := zipWriter.Create(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create zip entry %s: %w", fileName, err)
|
||||||
|
}
|
||||||
|
if _, err := writer.Write(entry); err != nil {
|
||||||
|
return fmt.Errorf("failed to write zip entry %s: %w", fileName, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateZipFromStrings creates a seed corpus ZIP file from a slice of strings.
|
||||||
|
func generateZipFromStrings(fuzzName string, corpus []string) error {
|
||||||
|
// Convert []string to [][]byte and delegate to generateZipFromBytes
|
||||||
|
byteCorpus := make([][]byte, len(corpus))
|
||||||
|
for i, s := range corpus {
|
||||||
|
byteCorpus[i] = []byte(s)
|
||||||
|
}
|
||||||
|
return generateZipFromBytes(fuzzName, byteCorpus)
|
||||||
|
}
|
||||||
150
util/fuzzing/fuzz_test.go
Normal file
150
util/fuzzing/fuzz_test.go
Normal file
|
|
@ -0,0 +1,150 @@
|
||||||
|
// Copyright The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package fuzzing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/prometheus/prometheus/model/labels"
|
||||||
|
"github.com/prometheus/prometheus/model/textparse"
|
||||||
|
"github.com/prometheus/prometheus/promql/parser"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Input size above which we know that Prometheus would consume too much
|
||||||
|
// memory. The recommended way to deal with it is check input size.
|
||||||
|
// https://google.github.io/oss-fuzz/getting-started/new-project-guide/#input-size
|
||||||
|
maxInputSize = 10240
|
||||||
|
)
|
||||||
|
|
||||||
|
// Use package-scope symbol table to avoid memory allocation on every fuzzing operation.
|
||||||
|
var symbolTable = labels.NewSymbolTable()
|
||||||
|
|
||||||
|
// FuzzParseMetricText fuzzes the metric parser with "text/plain" content type.
|
||||||
|
//
|
||||||
|
// Note that this is not the parser for the text-based exposition-format; that
|
||||||
|
// lives in github.com/prometheus/client_golang/text.
|
||||||
|
func FuzzParseMetricText(f *testing.F) {
|
||||||
|
// Add seed corpus
|
||||||
|
for _, corpus := range GetCorpusForFuzzParseMetricText() {
|
||||||
|
f.Add(corpus)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, in []byte) {
|
||||||
|
p, warning := textparse.New(in, "text/plain", symbolTable, textparse.ParserOptions{})
|
||||||
|
if p == nil || warning != nil {
|
||||||
|
// An invalid content type is being passed, which should not happen
|
||||||
|
// in this context.
|
||||||
|
t.Skip()
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
for {
|
||||||
|
_, err = p.Next()
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't care about errors, just that we don't panic.
|
||||||
|
_ = err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuzzParseOpenMetric fuzzes the metric parser with "application/openmetrics-text" content type.
|
||||||
|
func FuzzParseOpenMetric(f *testing.F) {
|
||||||
|
// Add seed corpus
|
||||||
|
for _, corpus := range GetCorpusForFuzzParseOpenMetric() {
|
||||||
|
f.Add(corpus)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, in []byte) {
|
||||||
|
p, warning := textparse.New(in, "application/openmetrics-text", symbolTable, textparse.ParserOptions{})
|
||||||
|
if p == nil || warning != nil {
|
||||||
|
// An invalid content type is being passed, which should not happen
|
||||||
|
// in this context.
|
||||||
|
t.Skip()
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
for {
|
||||||
|
_, err = p.Next()
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't care about errors, just that we don't panic.
|
||||||
|
_ = err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuzzParseMetricSelector fuzzes the metric selector parser.
|
||||||
|
func FuzzParseMetricSelector(f *testing.F) {
|
||||||
|
// Add seed corpus
|
||||||
|
for _, corpus := range GetCorpusForFuzzParseMetricSelector() {
|
||||||
|
f.Add(corpus)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, in string) {
|
||||||
|
if len(in) > maxInputSize {
|
||||||
|
t.Skip()
|
||||||
|
}
|
||||||
|
_, err := parser.ParseMetricSelector(in)
|
||||||
|
// We don't care about errors, just that we don't panic.
|
||||||
|
_ = err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuzzParseExpr fuzzes the expression parser.
|
||||||
|
func FuzzParseExpr(f *testing.F) {
|
||||||
|
parser.EnableExperimentalFunctions = true
|
||||||
|
parser.ExperimentalDurationExpr = true
|
||||||
|
parser.EnableExtendedRangeSelectors = true
|
||||||
|
f.Cleanup(func() {
|
||||||
|
parser.EnableExperimentalFunctions = false
|
||||||
|
parser.ExperimentalDurationExpr = false
|
||||||
|
parser.EnableExtendedRangeSelectors = false
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add seed corpus from built-in test expressions
|
||||||
|
corpus, err := GetCorpusForFuzzParseExpr()
|
||||||
|
if err != nil {
|
||||||
|
f.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(corpus) < 1000 {
|
||||||
|
f.Fatalf("loading exprs is likely broken: got %d expressions, expected at least 1000", len(corpus))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, expr := range corpus {
|
||||||
|
f.Add(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, in string) {
|
||||||
|
if len(in) > maxInputSize {
|
||||||
|
t.Skip()
|
||||||
|
}
|
||||||
|
_, err := parser.ParseExpr(in)
|
||||||
|
// We don't care about errors, just that we don't panic.
|
||||||
|
_ = err
|
||||||
|
})
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue