Skip to content

Commit

Permalink
Fix hydrator path overflow bug (#592)
Browse files Browse the repository at this point in the history
* fix hydrator overflow bug and add new test case
  • Loading branch information
StephenCathcart authored Aug 9, 2024
1 parent fb020f8 commit 190a74b
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 3 deletions.
6 changes: 3 additions & 3 deletions neo4j/internal/bolt/hydrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ func (h *hydrator) path(n uint32) any {
}
// Array of nodes
h.unp.Next()
num := h.unp.Int()
num := h.unp.Len()
nodes := make([]dbtype.Node, num)
for i := range nodes {
h.unp.Next()
Expand All @@ -675,7 +675,7 @@ func (h *hydrator) path(n uint32) any {
}
// Array of relnodes
h.unp.Next()
num = h.unp.Int()
num = h.unp.Len()
rnodes := make([]*relNode, num)
for i := range rnodes {
h.unp.Next()
Expand All @@ -692,7 +692,7 @@ func (h *hydrator) path(n uint32) any {
}
// Array of indexes
h.unp.Next()
num = h.unp.Int()
num = h.unp.Len()
indexes := make([]int, num)
for i := range indexes {
h.unp.Next()
Expand Down
68 changes: 68 additions & 0 deletions neo4j/internal/bolt/hydrator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ package bolt

import (
"fmt"
"math"
"reflect"
"runtime/debug"
"testing"
"time"

Expand Down Expand Up @@ -1067,6 +1069,72 @@ func TestHydratorBolt5(outer *testing.T) {
}
}

// TestHydratorPathWithEdgeCaseSizes ensures that the hydrator does not panic due to integer overflow
// when handling the size of nodes, unbound relationships, and indices that are between the upper bounds of
// signed and unsigned integers. This test case was created due to a bug identified in
// GitHub issue #590 (https://github.com/neo4j/neo4j-go-driver/issues/590).
func TestHydratorPathIntegerOverflowScenarios(outer *testing.T) {
type hydratorPathTestCase struct {
name string
size int
}

cases := []hydratorPathTestCase{
{name: "int8-overflow", size: math.MaxInt8 + 1},
{name: "int16-overflow", size: math.MaxInt16 + 1},
{name: "uint8-boundary", size: math.MaxUint8 - 1}, // Indices requires even size
{name: "uint16-boundary", size: math.MaxUint16 - 1}, // Indices requires even size
}

for _, c := range cases {
outer.Run(c.name, func(t *testing.T) {
defer func() {
if r := recover(); r != nil {
t.Fatalf("Error hydrating path: %s : %s", r, string(debug.Stack()))
}
}()

packer := packstream.Packer{}
hydrator := hydrator{}

packer.Begin([]byte{})
packer.StructHeader(byte(msgRecord), 1)
packer.ArrayHeader(1)
packer.StructHeader('P', 3)
// Nodes
packer.ArrayHeader(c.size)
for i := 1; i <= c.size; i++ {
packer.StructHeader('N', 3)
packer.Int64(int64(i))
packer.ArrayHeader(0)
packer.MapHeader(0)
}
// Unbound Relationships
packer.ArrayHeader(c.size)
for i := 1; i <= c.size; i++ {
packer.StructHeader('r', 3)
packer.Int(i)
packer.String("type")
packer.MapHeader(0)
}
// Indices
packer.ArrayHeader(c.size)
for i := 1; i <= c.size; i++ {
packer.Int(1)
}
// Test we can hydrate without a panic from int overflows.
buf, err := packer.End()
if err != nil {
t.Fatal("Build error")
}
_, err = hydrator.hydrate(buf)
if err != nil {
t.Fatal("Hydrate error")
}
})
}
}

func TestUtcDateTime(outer *testing.T) {
// Thu Jun 16 2022 13:00:00 UTC
secondsSinceEpoch := int64(1655384400)
Expand Down

0 comments on commit 190a74b

Please sign in to comment.