go-capnp icon indicating copy to clipboard operation
go-capnp copied to clipboard

Setting a field in the data section of an unmarshaled struct created with a previous version that did not have any data section fields panics

Open xortive opened this issue 2 years ago • 0 comments

@0xbf613bdc8c0f6ae4;
struct OldStruct @0xee1b66e79b51bbbf {  # 0 bytes, 2 ptrs
  foo @0 :Data;  # ptr[0]
  bar @1 :Data;  # ptr[1]
}
struct NewStruct @0xb78a3d22d4285878 {  # 8 bytes, 2 ptrs
  foo @0 :Data;  # ptr[0]
  bar @1 :Data;  # ptr[1]
  newField @2 :Bool;  # bits[0, 1)
}

If one were to serialize OldStruct with go-capnp, deserialize it, and set newField, it will panic here: https://github.com/capnproto/go-capnp/blob/0d218d2660ffa094198d7aba689c4eb04ff6ae18/struct.go#L201

I think go-capnp should allocate a data section in this scenario to match the C++ implementation.

reproduction test:

func TestDataSectionBug(t *testing.T) {
	arena := capnp.SingleSegment(nil)
	oldMsg, seg, err := capnp.NewMessage(arena)
	assert.NoError(t, err)

	oldStruct, err := schema.NewOldStruct(seg)
	assert.NoError(t, err)

	err = oldStruct.SetFoo([]byte{255, 255})
	assert.NoError(t, err)
	err = oldStruct.SetFoo([]byte{127, 127})
	assert.NoError(t, err)

	oldSerialized, err := oldMsg.Marshal()
	assert.NoError(t, err)

	newMsg, err := capnp.Unmarshal(oldSerialized)
	assert.NoError(t, err)

	newStruct, err := schema.ReadRootNewStruct(newMsg)
	assert.NoError(t, err)

	newStruct.SetNewField(true) // panics
}

xortive avatar Apr 29 '24 14:04 xortive