fix(promql): prevent panic in trimStringByBytes on invalid UTF-8

Add bounds check to prevent index out of range panic when
trimStringByBytes receives a string containing only UTF-8 continuation
bytes (0x80-0xBF). Previously, the loop would decrement size below 0
when no valid rune start byte was found, causing a panic.

A malicious query string with only continuation bytes could crash
the Prometheus server via the ActiveQueryTracker before the query
was parsed or validated.

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
Arve Knudsen 2025-12-24 11:52:37 +01:00
parent bec70227f1
commit 65f8482335
2 changed files with 42 additions and 1 deletions

View file

@ -164,7 +164,7 @@ func trimStringByBytes(str string, size int) string {
trimIndex := len(bytesStr)
if size < len(bytesStr) {
for !utf8.RuneStart(bytesStr[size]) {
for size > 0 && !utf8.RuneStart(bytesStr[size]) {
size--
}
trimIndex = size

View file

@ -127,6 +127,47 @@ func TestMMapFile(t *testing.T) {
require.Equal(t, []byte(data), bytes[:2], "Mmap failed")
}
func TestTrimStringByBytes(t *testing.T) {
for _, tc := range []struct {
name string
input string
size int
expected string
}{
{
name: "normal ASCII string",
input: "hello",
size: 3,
expected: "hel",
},
{
name: "no trimming needed",
input: "hi",
size: 10,
expected: "hi",
},
{
name: "UTF-8 multibyte character boundary",
input: "日本", // 6 bytes (3 bytes per character)
size: 4,
expected: "日", // trims back to complete character boundary
},
{
name: "invalid UTF-8 continuation-only bytes",
input: string([]byte{0x80, 0x81, 0x82, 0x83, 0x84}), // only continuation bytes
size: 4,
expected: "",
},
} {
t.Run(tc.name, func(t *testing.T) {
require.NotPanics(t, func() {
result := trimStringByBytes(tc.input, tc.size)
require.Equal(t, tc.expected, result)
})
})
}
}
func TestParseBrokenJSON(t *testing.T) {
for _, tc := range []struct {
b []byte