diff --git a/pkg/textparse/lex.l b/pkg/textparse/lex.l index f0342d4d9..7f6aaf99d 100644 --- a/pkg/textparse/lex.l +++ b/pkg/textparse/lex.l @@ -18,6 +18,7 @@ import ( "fmt" "math" "strconv" + "unicode/utf8" "github.com/prometheus/prometheus/pkg/value" ) @@ -82,6 +83,10 @@ M [a-zA-Z_:] l.offsets = append(l.offsets, l.i-1) \"(\\.|[^\\"]|\0)*\" s = lstateLabels + if !utf8.Valid(l.b[l.offsets[len(l.offsets)-1]+2:l.i-1]) { + l.err = fmt.Errorf("Invalid UTF-8 label value.") + return -1 + } l.offsets = append(l.offsets, l.i-1) \'(\\.|[^\\']|\0)*\' s = lstateLabels l.offsets = append(l.offsets, l.i-1) diff --git a/pkg/textparse/lex.l.go b/pkg/textparse/lex.l.go index 4742c20d8..37fec2b1b 100644 --- a/pkg/textparse/lex.l.go +++ b/pkg/textparse/lex.l.go @@ -19,6 +19,7 @@ import ( "fmt" "math" "strconv" + "unicode/utf8" "github.com/prometheus/prometheus/pkg/value" ) @@ -413,6 +414,10 @@ yyrule9: // {S}({L}|{D})*= yyrule10: // \"(\\.|[^\\"]|\0)*\" { s = lstateLabels + if !utf8.Valid(l.b[l.offsets[len(l.offsets)-1]+2 : l.i-1]) { + l.err = fmt.Errorf("Invalid UTF-8 label value.") + return -1 + } l.offsets = append(l.offsets, l.i-1) goto yystate0 } diff --git a/pkg/textparse/parse_test.go b/pkg/textparse/parse_test.go index 52284d282..8e338cdb7 100644 --- a/pkg/textparse/parse_test.go +++ b/pkg/textparse/parse_test.go @@ -104,6 +104,37 @@ go_goroutines 33 123123` } +func TestParseErrors(t *testing.T) { + cases := []struct { + input string + err string + }{ + { + input: "a", + err: "no token found", + }, + { + input: "a{\xff=\"foo\"} 1\n", + err: "no token found", + }, + { + input: "a{b=\"\xff\"} 1\n", + err: "Invalid UTF-8 label value.", + }, + { + input: "a true\n", + err: "strconv.ParseFloat: parsing \"true\": invalid syntax", + }, + } + + for _, c := range cases { + p := New([]byte(c.input)) + for p.Next() { + } + require.Equal(t, c.err, p.Err().Error()) + } +} + const ( testdataSampleCount = 410 ) diff --git a/retrieval/scrape_test.go b/retrieval/scrape_test.go index dc507b931..adcd37fe8 100644 --- a/retrieval/scrape_test.go +++ b/retrieval/scrape_test.go @@ -844,6 +844,29 @@ func TestScrapeLoopRunReportsTargetDownOnScrapeError(t *testing.T) { } } +func TestScrapeLoopRunReportsTargetDownOnInvalidUTF8(t *testing.T) { + var ( + scraper = &testScraper{} + reportAppender = &collectResultAppender{} + reportApp = func() storage.Appender { return reportAppender } + ) + + ctx, cancel := context.WithCancel(context.Background()) + sl := newScrapeLoop(ctx, scraper, func() storage.Appender { return nopAppender{} }, reportApp, nil) + + scraper.scrapeFunc = func(ctx context.Context, w io.Writer) error { + cancel() + w.Write([]byte("a{l=\"\xff\"} 0\n")) + return nil + } + + sl.run(10*time.Millisecond, time.Hour, nil) + + if reportAppender.result[0].v != 0 { + t.Fatalf("bad 'up' value; want 0, got %v", reportAppender.result[0].v) + } +} + type errorAppender struct { collectResultAppender }