parser: fix panic when label exceeds 16MB

Signed-off-by: Meher Assel <asselmeher@gmail.com>
This commit is contained in:
Meher Assel 2026-02-02 18:57:52 +01:00
parent 44d772b4e7
commit 5b6048a755
No known key found for this signature in database
3 changed files with 38 additions and 5 deletions

View file

@ -16,6 +16,7 @@
package labels
import (
"errors"
"slices"
"strings"
"unsafe"
@ -540,13 +541,16 @@ func marshalLabelToSizedBuffer(m *Label, data []byte) int {
return len(data) - i
}
// ErrLabelTooLong is returned when a label name or value exceeds the maximum allowed size.
var ErrLabelTooLong = errors.New("string too long to encode as label")
func sizeWhenEncoded(x uint64) (n int) {
if x < 255 {
return 1
} else if x <= 1<<24 {
return 4
}
panic("String too long to encode as label.")
panic(ErrLabelTooLong)
}
func encodeSize(data []byte, offset, v int) int {

View file

@ -340,16 +340,23 @@ var errUnexpected = errors.New("unexpected error")
// recover is the handler that turns panics into returns from the top level of Parse.
func (*parser) recover(errp *error) {
e := recover()
switch _, ok := e.(runtime.Error); {
case ok:
if e == nil {
return
}
switch err := e.(type) {
case runtime.Error:
// Print the stack trace but do not inhibit the running application.
buf := make([]byte, 64<<10)
buf = buf[:runtime.Stack(buf, false)]
fmt.Fprintf(os.Stderr, "parser panic: %v\n%s", e, buf)
*errp = errUnexpected
case e != nil:
*errp = e.(error)
case error:
*errp = err
default:
// Handle non-error panic values (e.g., strings) gracefully.
*errp = fmt.Errorf("%v", e)
}
}

View file

@ -6004,6 +6004,28 @@ func TestRecoverParserError(t *testing.T) {
panic(e)
}
func TestRecoverParserString(t *testing.T) {
p := NewParser("foo bar")
var err error
defer func() {
require.Error(t, err)
require.EqualError(t, err, "some string panic")
}()
defer p.recover(&err)
panic("some string panic")
}
func TestParseLabelTooLong(t *testing.T) {
const maxLabelSize = 1 << 24
longValue := strings.Repeat("a", maxLabelSize+1)
input := `{job="` + longValue + `"}`
_, err := ParseMetric(input)
require.Error(t, err)
}
func TestExtractSelectors(t *testing.T) {
for _, tc := range [...]struct {
input string