internal/impl: check for required fields in missing map value

If a map value is a message with required fields, the validator should
note that it is uninitialized if a map item contains no value. In this
case, the value is an empty message which obviously does not have the
required field set.

Change-Id: I7698e60765e3c95478f293e121bba3ad7fc88e27
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/213900
Reviewed-by: Joe Tsai <joetsai@google.com>
This commit is contained in:
Damien Neil 2020-01-08 17:53:16 -08:00
parent b02b6d1da5
commit 54a0a0476a
2 changed files with 28 additions and 5 deletions

View File

@ -266,6 +266,7 @@ State:
case 2: case 2:
vi.typ = st.valType vi.typ = st.valType
vi.mi = st.mi vi.mi = st.mi
vi.requiredIndex = 1
} }
default: default:
var f *coderFieldInfo var f *coderFieldInfo
@ -436,15 +437,23 @@ State:
} }
b = st.tail b = st.tail
PopState: PopState:
numRequiredFields := 0
switch st.typ { switch st.typ {
case validationTypeMessage, validationTypeGroup: case validationTypeMessage, validationTypeGroup:
numRequiredFields = int(st.mi.numRequiredFields)
case validationTypeMap:
// If this is a map field with a message value that contains
// required fields, require that the value be present.
if st.mi != nil && st.mi.numRequiredFields > 0 {
numRequiredFields = 1
}
}
// If there are more than 64 required fields, this check will // If there are more than 64 required fields, this check will
// always fail and we will report that the message is potentially // always fail and we will report that the message is potentially
// uninitialized. // uninitialized.
if st.mi.numRequiredFields > 0 && bits.OnesCount64(st.requiredMask) != int(st.mi.numRequiredFields) { if numRequiredFields > 0 && bits.OnesCount64(st.requiredMask) != numRequiredFields {
initialized = false initialized = false
} }
}
states = states[:len(states)-1] states = states[:len(states)-1]
} }
if !initialized { if !initialized {

View File

@ -1270,6 +1270,20 @@ var testValidMessages = []testProto{
}), }),
}.Marshal(), }.Marshal(),
}, },
{
desc: "required field in absent map message value",
partial: true,
decodeTo: []proto.Message{&testpb.TestRequiredForeign{
MapMessage: map[int32]*testpb.TestRequired{
2: {},
},
}},
wire: pack.Message{
pack.Tag{3, pack.BytesType}, pack.LengthPrefix(pack.Message{
pack.Tag{1, pack.VarintType}, pack.Varint(2),
}),
}.Marshal(),
},
{ {
desc: "required field in map message set", desc: "required field in map message set",
decodeTo: []proto.Message{&testpb.TestRequiredForeign{ decodeTo: []proto.Message{&testpb.TestRequiredForeign{