textparse: fix panic in protobuf parser for summary with no quantiles

getMagicLabel had no bounds check on the quantile slice for the Summary
case. fieldsDone for an empty-quantile summary is set inside Series(),
not getMagicLabel. A caller driving Next() without calling Series() at
the _sum step would allow fieldPos to advance to 0 and index into an
empty slice.

Add the same out-of-bounds guard that the histogram branch already has,
and a regression test that exercises Next()-only iteration over a
summary with no quantiles.

Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com>
This commit is contained in:
Julien Pivotto
2026-03-27 10:37:32 +01:00
parent 5b1d22e2ce
commit 7f82c5f52c
2 changed files with 30 additions and 0 deletions

View File

@@ -674,6 +674,10 @@ func (p *ProtobufParser) getMagicLabel() (bool, string, string) {
switch p.dec.GetType() {
case dto.MetricType_SUMMARY:
qq := p.dec.GetSummary().GetQuantile()
if p.fieldPos >= len(qq) {
p.fieldsDone = true
return false, "", ""
}
q := qq[p.fieldPos]
p.fieldsDone = p.fieldPos == len(qq)-1
return true, model.QuantileLabel, labels.FormatOpenMetricsFloat(q.GetQuantile())

View File

@@ -5911,6 +5911,32 @@ func generateValidMetricName(r *rand.Rand) string {
return generateString(r, validFirstRunes, validMetricNameRunes)
}
// TestProtobufParseSummaryNoQuantilesNoPanic is a regression test for a panic
// when Next() is called without Series() on a summary with no quantiles.
func TestProtobufParseSummaryNoQuantilesNoPanic(t *testing.T) {
buf := metricFamiliesToProtobuf(t, []string{`
name: "no_quantile_summary"
help: "A summary with no quantile entries."
type: SUMMARY
metric: <
summary: <
sample_count: 10
sample_sum: 1.5
>
>
`})
p := NewProtobufParser(buf.Bytes(), false, false, false, false, labels.NewSymbolTable())
require.NotPanics(t, func() {
for {
_, err := p.Next()
if errors.Is(err, io.EOF) || err != nil {
break
}
}
})
}
func generateString(r *rand.Rand, firstRunes, restRunes []rune) string {
result := make([]rune, 1+r.Intn(20))
for i := range result {