Skip to content

Commit 415d6fa

Browse files
committed
Merge pull request #75 from crast/fix-specific-array-crash
Fix a panic decoding arrays with SpecificDatumReader
2 parents 7a2b3e2 + 8397c63 commit 415d6fa

File tree

2 files changed

+55
-5
lines changed

2 files changed

+55
-5
lines changed

datum_reader.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -211,12 +211,16 @@ func (reader sDatumReader) mapArray(field Schema, reflectField reflect.Value, de
211211
return reflect.ValueOf(arrayLength), err
212212
}
213213

214-
if pointer && val.Kind() != reflect.Ptr {
215-
val = val.Addr()
216-
} else if !pointer && val.Kind() == reflect.Ptr {
217-
val = val.Elem()
214+
// The only time `val` would not be valid is if it's an explicit null value.
215+
// Since the default value is the zero value, we can simply just not set the value
216+
if val.IsValid() {
217+
if pointer && val.Kind() != reflect.Ptr {
218+
val = val.Addr()
219+
} else if !pointer && val.Kind() == reflect.Ptr {
220+
val = val.Elem()
221+
}
222+
current.Set(val)
218223
}
219-
current.Set(val)
220224
}
221225
//concatenate arrays
222226
if array.Len() == 0 {

datum_reader_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,52 @@ func TestComplexOfComplexBinding(t *testing.T) {
269269
}
270270
}
271271

272+
// TestSpecificArrayCrash tests against regression of a crash scenario
273+
// The crash occurs when an array decodes an explicitly nil value (like in a
274+
// type union). The type union works fine as a raw field but not in an array.
275+
func TestSpecificArrayCrash(t *testing.T) {
276+
schema := MustParseSchema(`{
277+
"type": "record",
278+
"name": "Rec",
279+
"fields": [{
280+
"name": "a",
281+
"type": {
282+
"type": "array",
283+
"items": ["null", "string", "long", "float"]
284+
}
285+
}]
286+
}`)
287+
type Rec struct {
288+
A []interface{} `avro:"a"`
289+
}
290+
// Write some bytes
291+
var buf bytes.Buffer
292+
writer := NewSpecificDatumWriter()
293+
writer.SetSchema(schema)
294+
prims := []interface{}{
295+
"foo",
296+
nil,
297+
int64(7),
298+
}
299+
writer.Write(&Rec{prims}, NewBinaryEncoder(&buf))
300+
301+
// Now do the read. This will crash if there's any null setting issue.
302+
var dest Rec
303+
reader := NewSpecificDatumReader()
304+
reader.SetSchema(schema)
305+
err := reader.Read(&dest, NewBinaryDecoder(buf.Bytes()))
306+
if err != nil {
307+
t.Fatal(err)
308+
}
309+
if len(dest.A) != 3 {
310+
t.Fatalf("A must be 3, got %d", len(dest.A))
311+
}
312+
assert(t, dest.A[0], "foo")
313+
assert(t, dest.A[1], nil)
314+
assert(t, dest.A[2], int64(7))
315+
316+
}
317+
272318
func TestGenericDatumReaderEmptyMap(t *testing.T) {
273319
sch, err := ParseSchema(`{
274320
"type": "record",

0 commit comments

Comments
 (0)