-
Notifications
You must be signed in to change notification settings - Fork 51
Open
Description
Overview
The library will match key\u0000 with key when decoding structs. This poses security threats in protocols that require exact matching of JSON object keys.
Reproduction
package main
import (
"testing"
"github.com/segmentio/encoding/json"
)
func TestUnmarshalNullCharacter(t *testing.T) {
type Target struct {
Field string `json:"field"`
}
jsonText := `{"field\u0000": "value"}`
var got Target
if err := json.Unmarshal([]byte(jsonText), &got); err != nil {
t.Fatalf("Unmarshal failed: %v", err)
}
if got.Field != "" {
t.Errorf("Field was set!")
}
}Additional context
Here is some root cause analysis generated by my coding agent (may not be entirely correct):
Root Cause Analysis
The issue stems from an interaction between segmentio/encoding/json and its underlying assembly-optimized key lookup package, segmentio/asm/keyset.
- Zero-Padding in Comparison: The
keyset.Lookupfunction (implemented in assembly, e.g.,keyset_amd64.s) uses SIMD (AVX) instructions to compare keys. To handle keys shorter than 16 bytes, it loads the key and masks out (zeroes) the trailing bytes beyond the key's length. - Ambiguous Nulls: The keys stored in the keyset are also effectively zero-padded to 16 bytes.
- If you have a struct field named
"field"(length 5), it is compared asfieldfollowed by 11 null bytes. - If the input JSON key is
"field\u0000"(length 6), the parser decodes this to the byte slicefield\x00. - When Lookup processes
field\x00, it masks out bytes after index 6. The result in the register isfield+\x00+ 10 null bytes.
- If you have a struct field named
- False Positive Match: To the assembly comparison logic, the zero-padded
"field"(len 5) and the zero-padded"field\x00"(len 6) look identical (field\0\0...). The Lookup function returns the index for"field", leading to the incorrect match.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels