prometheus
125 строк · 3.5 Кб
1// Copyright 2015 The Prometheus Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14// Only build when go-fuzz is in use
15//go:build gofuzz
16
17package promql
18
19import (
20"errors"
21"io"
22
23"github.com/prometheus/prometheus/model/labels"
24"github.com/prometheus/prometheus/model/textparse"
25"github.com/prometheus/prometheus/promql/parser"
26)
27
28// PromQL parser fuzzing instrumentation for use with
29// https://github.com/dvyukov/go-fuzz.
30//
31// Fuzz each parser by building appropriately instrumented parser, ex.
32// FuzzParseMetric and execute it with it's
33//
34// go-fuzz-build -func FuzzParseMetric -o FuzzParseMetric.zip github.com/prometheus/prometheus/promql
35//
36// And then run the tests with the appropriate inputs
37//
38// go-fuzz -bin FuzzParseMetric.zip -workdir fuzz-data/ParseMetric
39//
40// Further input samples should go in the folders fuzz-data/ParseMetric/corpus.
41//
42// Repeat for FuzzParseOpenMetric, FuzzParseMetricSelector and FuzzParseExpr.
43
44// Tuning which value is returned from Fuzz*-functions has a strong influence
45// on how quick the fuzzer converges on "interesting" cases. At least try
46// switching between fuzzMeh (= included in corpus, but not a priority) and
47// fuzzDiscard (=don't use this input for re-building later inputs) when
48// experimenting.
49const (
50fuzzInteresting = 1
51fuzzMeh = 0
52fuzzDiscard = -1
53
54// Input size above which we know that Prometheus would consume too much
55// memory. The recommended way to deal with it is check input size.
56// https://google.github.io/oss-fuzz/getting-started/new-project-guide/#input-size
57maxInputSize = 10240
58)
59
60// Use package-scope symbol table to avoid memory allocation on every fuzzing operation.
61var symbolTable = labels.NewSymbolTable()
62
63func fuzzParseMetricWithContentType(in []byte, contentType string) int {
64p, warning := textparse.New(in, contentType, false, symbolTable)
65if warning != nil {
66// An invalid content type is being passed, which should not happen
67// in this context.
68panic(warning)
69}
70
71var err error
72for {
73_, err = p.Next()
74if err != nil {
75break
76}
77}
78if errors.Is(err, io.EOF) {
79err = nil
80}
81
82if err == nil {
83return fuzzInteresting
84}
85
86return fuzzMeh
87}
88
89// Fuzz the metric parser.
90//
91// Note that this is not the parser for the text-based exposition-format; that
92// lives in github.com/prometheus/client_golang/text.
93func FuzzParseMetric(in []byte) int {
94return fuzzParseMetricWithContentType(in, "")
95}
96
97func FuzzParseOpenMetric(in []byte) int {
98return fuzzParseMetricWithContentType(in, "application/openmetrics-text")
99}
100
101// Fuzz the metric selector parser.
102func FuzzParseMetricSelector(in []byte) int {
103if len(in) > maxInputSize {
104return fuzzMeh
105}
106_, err := parser.ParseMetricSelector(string(in))
107if err == nil {
108return fuzzInteresting
109}
110
111return fuzzMeh
112}
113
114// Fuzz the expression parser.
115func FuzzParseExpr(in []byte) int {
116if len(in) > maxInputSize {
117return fuzzMeh
118}
119_, err := parser.ParseExpr(string(in))
120if err == nil {
121return fuzzInteresting
122}
123
124return fuzzMeh
125}
126