newArrayParser indexes b[0] without length check, panicking on empty scan input
Summary
newArrayParser in dialect/pgdialect/array_parser.go indexes b[0] without checking length, so scanning an empty (zero-length) value panics with index out of range instead of returning a parse error. Scan paths must never panic.
Location
- File:
dialect/pgdialect/array_parser.go - Function:
newArrayParser(b []byte) - Callers:
dialect/pgdialect/array.go—arrayScannerclosure plusscanStringSliceValue/decodeStringSlice-adjacent slice scanners (all dop := newArrayParser(b)aftertoBytes(src))
Relevant code path (static analysis of current master):
func newArrayParser(b []byte) *arrayParser {
p := new(arrayParser)
if b[0] == 'n' { // panics when len(b) == 0
...
}
if len(b) < 2 || ... {
p.err = ...
}
...
}
Contrast with newHStoreParser in the same package, which correctly guards: if len(b) != 0 && (len(b) < 6 || b[0] != '"').
Problem
src == nil is handled before toBytes, but src == "" (empty string) or empty bytes are not nil. toBytes("") yields a zero-length slice, and newArrayParser immediately dereferences b[0], panicking. Since this sits in the sql.Scanner path (arrayScanner → newArrayParser(b) → for p.Next()), a single unexpected empty value from the driver crashes the whole process instead of surfacing p.Err().
Trigger / Reproduction
Based on static analysis (no execution performed):
- Scan a value where
toBytes(src)returns zero-lengthb(e.g.srcis""/ empty[]byte) into a slice/array destination via the pgdialect array scanner. newArrayParser(b)evaluatesb[0]withlen(b)==0→ runtime panic:index out of range [0] with length 0.
Existing TestArrayParser covers {}, {""}, [], etc., but has no empty-input case, consistent with the missing guard.
Note: this is a static-analysis finding; I did not run a live PostgreSQL instance.
Expected Behavior
Empty input should produce a controlled parse error (via p.err, surfaced through p.Err()) or be treated as an empty/NULL array per package convention — never a panic. Scan must always return error on bad input.
Actual Behavior
Empty input causes an unrecoverable panic in the scan path.
Impact
- Any application scanning rows can be crashed by one unexpected empty array value (fail-closed data becomes fail-crash process).
- Inconsistent with the sibling hstore parser and with Go
sql.Scannercontracts.
Suggested Direction
- Add a
len(b) == 0guard at the top ofnewArrayParserthat setsp.err(e.g.pgdialect: can't parse array: "") and returns early, mirroring the existinglen(b) < 2error path. Optionally add an empty-input case toTestArrayParserassertingErr() != niland no panic.
Evidence
- Source via API:
array_parser.go:newArrayParserunguardedb[0];hstore_parser.go:newHStoreParserguardedlen(b)check;array.goshows all scan closures funnel throughnewArrayParser(b)after only asrc == nilcheck;array_parser_test.goshows no empty-input coverage. - Duplicate check: issue search for
arrayParser empty panicreturnstotal_count: 0, and the open-issue list contains no array-parser panic report — no apparent duplicate.
Classification
- FACT:
newArrayParserdereferencesb[0]before any length check (verified in source via API). - INFERENCE: zero-length scan input therefore panics instead of returning an error.
- HYPOTHESIS: a length guard returning a parse error fixes the crash with no behavior change for valid inputs.
Source: uptrace/bun