Skip to content

Commit 2bcbc39

Browse files
common/json: fix out-of-bounds slice in the comment scanner
skipJSONString advances i by 2 on a backslash escape; when the backslash is the last byte of the input, i is pushed past len(data) and returned unchanged. A caller then slices data[keyStart:keyEnd] with keyEnd > len(data), panicking with "slice bounds out of range". This is reachable from any untrusted JSON parsed by the comment-aware decoder (e.g. a sing-box config or rule-set), e.g. the 12-byte input {0000000#00000\n\ crashes it. Clamp the returned index to len(data). Add FuzzUnmarshalExtended.
1 parent 59379fe commit 2bcbc39

2 files changed

Lines changed: 23 additions & 0 deletions

File tree

common/json/fuzz_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package json_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/sagernet/sing/common/json"
7+
)
8+
9+
// FuzzUnmarshalExtended fuzzes the comment-aware JSON decoder, which parses untrusted JSON
10+
// (e.g. sing-box config / rule-sets). It guards against panics such as the slice-bounds bug in
11+
// the comment scanner when a string escape runs off the end of the input.
12+
func FuzzUnmarshalExtended(f *testing.F) {
13+
f.Add([]byte("{\"a\":1 // c\n}"))
14+
f.Add([]byte("{\"a\\"))
15+
f.Fuzz(func(t *testing.T, data []byte) {
16+
_, _ = json.UnmarshalExtended[any](data)
17+
})
18+
}

common/json/internal/contextjson/comment.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,11 @@ func skipJSONString(data []byte, start int) int {
211211
i++
212212
}
213213
}
214+
// A trailing backslash escape (i += 2 on the last byte) can push i past the end; clamp so
215+
// callers that slice data[:skipJSONString(...)] cannot go out of range.
216+
if i > len(data) {
217+
i = len(data)
218+
}
214219
return i
215220
}
216221

0 commit comments

Comments
 (0)