Skip to content

Commit

Permalink
Check overwritten value in parentUpdater callback
Browse files Browse the repository at this point in the history
Currently, in parentUpdater callback, parent array/map resets same
child value.

Child value ID should match overwritten SlabIDStorable
or Slab.SlabID().

This commit adds check to make sure same child value is being reset.
  • Loading branch information
fxamacker committed Sep 17, 2023
1 parent d6f3daa commit 6f25137
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 5 deletions.
35 changes: 33 additions & 2 deletions array.go
Original file line number Diff line number Diff line change
Expand Up @@ -2725,8 +2725,39 @@ func (a *Array) setCallbackWithChild(i uint64, child Value) {
return err
}

if existingValueStorable == nil {
return NewFatalError(fmt.Errorf("failed to reset child value in parent updater callback because previous value is nil"))
// Verify overwritten storable has identical value ID.

switch x := existingValueStorable.(type) {
case SlabIDStorable:
sid := SlabID(x)
if !vid.equal(sid) {
return NewFatalError(
fmt.Errorf(
"failed to reset child value in parent updater callback: overwritten SlabIDStorable %s != value ID %s",
sid,
vid))
}

case Slab:
sid := x.SlabID()
if !vid.equal(sid) {
return NewFatalError(
fmt.Errorf(
"failed to reset child value in parent updater callback: overwritten Slab ID %s != value ID %s",
sid,
vid))
}

case nil:
return NewFatalError(
fmt.Errorf(
"failed to reset child value in parent updater callback: overwritten value is nil"))

default:
return NewFatalError(
fmt.Errorf(
"failed to reset child value in parent updater callback: overwritten value is wrong type %T",
existingValueStorable))
}

return nil
Expand Down
38 changes: 35 additions & 3 deletions map.go
Original file line number Diff line number Diff line change
Expand Up @@ -4523,6 +4523,8 @@ func (m *OrderedMap) setCallbackWithChild(
return
}

vid := c.ValueID()

c.setParentUpdater(func() error {
// Set child value with parent map using same key.
// Set() calls c.Storable() which returns inlined or not-inlined child storable.
Expand All @@ -4531,10 +4533,40 @@ func (m *OrderedMap) setCallbackWithChild(
return err
}

if existingValueStorable == nil {
return NewFatalError(fmt.Errorf("failed to reset child value in parent updater callback because previous value is nil"))
}
// Verify overwritten storable has identical value ID.

switch x := existingValueStorable.(type) {
case SlabIDStorable:
sid := SlabID(x)
if !vid.equal(sid) {
return NewFatalError(
fmt.Errorf(
"failed to reset child value in parent updater callback: overwritten SlabIDStorable %s != value ID %s",
sid,
vid))
}

case Slab:
sid := x.SlabID()
if !vid.equal(sid) {
return NewFatalError(
fmt.Errorf(
"failed to reset child value in parent updater callback: overwritten Slab ID %s != value ID %s",
sid,
vid))
}

case nil:
return NewFatalError(
fmt.Errorf(
"failed to reset child value in parent updater callback: overwritten value is nil"))

default:
return NewFatalError(
fmt.Errorf(
"failed to reset child value in parent updater callback: overwritten value is wrong type %T",
existingValueStorable))
}
return nil
})
}
Expand Down
13 changes: 13 additions & 0 deletions storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ func slabIDToValueID(sid SlabID) ValueID {
return id
}

func (vid ValueID) equal(sid SlabID) bool {
return bytes.Equal(vid[:8], sid.address[:]) &&
bytes.Equal(vid[8:], sid.index[:])
}

func (vid ValueID) String() string {
return fmt.Sprintf(
"0x%x.%d",
binary.BigEndian.Uint64(vid[:8]),
binary.BigEndian.Uint64(vid[8:]),
)
}

type (
Address [8]byte
SlabIndex [8]byte
Expand Down

0 comments on commit 6f25137

Please sign in to comment.