-
Notifications
You must be signed in to change notification settings - Fork 338
Description
Problem
If the io.Reader passed to expfmt.NewDecoder with the expfmt.FmtText format is a *bufio.Reader, then Decoder.Decode will infinitely recurse in the bufio.(*Reader).Read function.
Explanation
expfmt.(*textDecoder).Decode calls expfmt.(*TextParser).TextToMetricFamilies passing its same io.Reader each time, which is then passed to expfmt.(*TextParser).reset, which contains the following snippet:
if p.buf == nil {
p.buf = bufio.NewReader(in)
} else {
p.buf.Reset(in)
}The problem is that bufio.NewReader will return the given io.Reader without wrapping it, if it happens to already be a *bufio.Reader of sufficient size. All good on the first call from Decode, but the second call ends up setting the *bufio.Reader as its own underlying io.Reader and we get an infinite recursion.
A reasonable person might argue that this is a bug in bufio, but we can still fix it in expfmt.
Repro
package main
import (
"bufio"
"fmt"
"io"
"os"
"strings"
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
)
const example = `
# TYPE foo gauge
foo 0
`
func main() {
r := bufio.NewReader(strings.NewReader(example))
dec := expfmt.NewDecoder(r, expfmt.FmtText)
for {
var mf dto.MetricFamily
if err := dec.Decode(&mf); err != nil {
if err == io.EOF {
return
}
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
}