[fidl][golang] Start Union Tags at 1 instead of 0
This doesn't affect the wire format, only the representation of FIDL
unions and only in Go. It will help detect two kinds of errors:
1. Uninitialized unions were previously of the first member, now they
are errors when marshalling:
union Union1 {
int64 a;
int64 b;
};
u1 := Union1{} // This was previously of type "a" with value 0, now it is an error whenmarshalled.
2. Not using setters properly will be caught as errors when
marshalling:
u1 := Union1{}
u1.a = 12345 // This is wrong, it should have been u1.SetA(12345)
Both new and old encodings are modified and interoperable.
TESTED=fx run-test go_fidl_test
Change-Id: Ida12a5b8eb7cbc2a00f0661028381adfe3a0a2c6
diff --git a/src/syscall/zx/fidl/bindingstest/impl.go b/src/syscall/zx/fidl/bindingstest/impl.go
index 6175c70..71fcbe5 100644
--- a/src/syscall/zx/fidl/bindingstest/impl.go
+++ b/src/syscall/zx/fidl/bindingstest/impl.go
@@ -586,10 +586,11 @@
type Union1Tag uint32
const (
- Union1A Union1Tag = iota
- Union1B Union1Tag = iota
- Union1C Union1Tag = iota
- Union1D Union1Tag = iota
+ _ Union1Tag = iota
+ Union1A
+ Union1B
+ Union1C
+ Union1D
)
// Union1 is a FIDL union.
diff --git a/src/syscall/zx/fidl/encoding.go b/src/syscall/zx/fidl/encoding.go
index bba6e0d..4645128 100644
--- a/src/syscall/zx/fidl/encoding.go
+++ b/src/syscall/zx/fidl/encoding.go
@@ -364,13 +364,12 @@
// Expects the Type t and Value v to refer to a golang struct value, not a
// pointer. The alignment field is used to align the union's field.
func (e *encoder) marshalUnion(t reflect.Type, v reflect.Value, alignment int) error {
- kind := v.Field(0).Uint()
-
- // Index into the fields of the struct, adding 1 for the tag.
- fieldIndex := int(kind) + 1
- if fieldIndex >= t.NumField() {
- return newValueError(ErrInvalidUnionTag, kind)
+ fieldIndex := int(v.Field(0).Uint()) // Union tag values start at 1.
+ if fieldIndex < 1 || fieldIndex >= t.NumField() {
+ return newValueError(ErrInvalidUnionTag, fieldIndex)
}
+
+ kind := uint64(fieldIndex - 1) // Wire format tags start at 0.
// Save the head for proper padding.
head := e.head
e.writeUint(kind, 4)
@@ -748,7 +747,7 @@
if fieldIndex >= t.NumField() {
return ErrInvalidUnionTag
}
- v.Field(0).SetUint(kind)
+ v.Field(0).SetUint(kind + 1)
f := t.Field(fieldIndex)
var n nestedTypeData
diff --git a/src/syscall/zx/fidl/encoding_new.go b/src/syscall/zx/fidl/encoding_new.go
index 0d47a71..4bbe04e 100644
--- a/src/syscall/zx/fidl/encoding_new.go
+++ b/src/syscall/zx/fidl/encoding_new.go
@@ -417,7 +417,7 @@
func (m mUnion) marshal(v reflect.Value, out *encoder) error {
// Kind.
- kind := int(v.Field(0).Uint())
+ kind := int(v.Field(0).Uint()) - 1
if kind < 0 || len(m.fields) <= kind {
return newValueError(ErrInvalidUnionTag, kind)
}
@@ -449,7 +449,7 @@
if kind < 0 || len(m.fields) <= kind {
return newValueError(ErrInvalidUnionTag, kind)
}
- v.Field(0).SetUint(uint64(kind))
+ v.Field(0).SetUint(uint64(kind) + 1)
// Re-align to the union's alignement before writing its field.
in.head = align(in.head, m.alignment)
diff --git a/src/syscall/zx/fidl/errors.go b/src/syscall/zx/fidl/errors.go
index 1529e7f..85d58f1 100644
--- a/src/syscall/zx/fidl/errors.go
+++ b/src/syscall/zx/fidl/errors.go
@@ -107,6 +107,8 @@
return "message too small to have FIDL header"
case ErrStructIsNotPayload:
return "golang struct type must implement Payload"
+ case ErrInvalidUnionTag:
+ return "union tag out of bounds"
default:
return "unknown error code"
}
diff --git a/src/syscall/zx/fidl/fidl_test/encoding_new_test.go b/src/syscall/zx/fidl/fidl_test/encoding_new_test.go
index 9ce083a..9e49dfb 100644
--- a/src/syscall/zx/fidl/fidl_test/encoding_new_test.go
+++ b/src/syscall/zx/fidl/fidl_test/encoding_new_test.go
@@ -301,6 +301,7 @@
}
func (c checker) check(t *testing.T, input Message, expectSize int, output Message) {
+ t.Helper()
var respb [zx.ChannelMaxMessageBytes]byte
var resph [zx.ChannelMaxMessageHandles]zx.Handle
nb, nh, err := c.marshalFunc.fn(input, respb[:], resph[:])
@@ -362,6 +363,10 @@
C: []int32{99},
D: &v1, // limit is 2, provided is 3
}, ErrVectorTooLong},
+ {"union1-A-uninitialized", &TestUnion1{
+ A: Union1{}, // Intentionally don't set any members of the union.
+ B: nil,
+ }, ErrInvalidUnionTag},
}
for _, ex := range cases {
t.Run(ex.name, func(t *testing.T) {
diff --git a/src/syscall/zx/fidl/fidl_test/encoding_test.go b/src/syscall/zx/fidl/fidl_test/encoding_test.go
index 96d1f59..097ce20 100644
--- a/src/syscall/zx/fidl/fidl_test/encoding_test.go
+++ b/src/syscall/zx/fidl/fidl_test/encoding_test.go
@@ -16,6 +16,7 @@
)
func testIdentity(t *testing.T, input Payload, expectSize int, output Payload) {
+ t.Helper()
var respb [zx.ChannelMaxMessageBytes]byte
var resph [zx.ChannelMaxMessageHandles]zx.Handle
nb, nh, err := Marshal(input, respb[:], resph[:])
@@ -152,6 +153,20 @@
B: []*Union1{&u1, nil, nil},
}, 120, &TestUnion2{})
})
+ t.Run("Uninitialized unions", func(t *testing.T) {
+ u1 := Union1{}
+ // Intentionally don't set any members of the union.
+ tu1 := TestUnion1{
+ A: u1,
+ B: nil,
+ }
+ var respb [zx.ChannelMaxMessageBytes]byte
+ var resph [zx.ChannelMaxMessageHandles]zx.Handle
+ _, _, err := Marshal(&tu1, respb[:], resph[:])
+ if err == nil {
+ t.Errorf("got %v when marshalling invalid union but expected ErrInvalidUnionTag", err)
+ }
+ })
t.Run("Handles", func(t *testing.T) {
vmo, err := zx.NewVMO(10, 0)
if err != nil {
diff --git a/src/syscall/zx/io/impl.go b/src/syscall/zx/io/impl.go
index fed9159..2c649b8 100644
--- a/src/syscall/zx/io/impl.go
+++ b/src/syscall/zx/io/impl.go
@@ -297,12 +297,13 @@
type NodeInfoTag uint32
const (
- NodeInfoService NodeInfoTag = iota
- NodeInfoFile NodeInfoTag = iota
- NodeInfoDirectory NodeInfoTag = iota
- NodeInfoPipe NodeInfoTag = iota
- NodeInfoVmofile NodeInfoTag = iota
- NodeInfoDevice NodeInfoTag = iota
+ _ NodeInfoTag = iota
+ NodeInfoService
+ NodeInfoFile
+ NodeInfoDirectory
+ NodeInfoPipe
+ NodeInfoVmofile
+ NodeInfoDevice
)
// NodeInfo is a FIDL union.
diff --git a/src/syscall/zx/net/impl.go b/src/syscall/zx/net/impl.go
index cd0bcfc..b9fd6d5 100644
--- a/src/syscall/zx/net/impl.go
+++ b/src/syscall/zx/net/impl.go
@@ -294,8 +294,9 @@
type IpAddressTag uint32
const (
- IpAddressIpv4 IpAddressTag = iota
- IpAddressIpv6 IpAddressTag = iota
+ _ IpAddressTag = iota
+ IpAddressIpv4
+ IpAddressIpv6
)
// IpAddress is a FIDL union.