Commit d5603f3d authored by Levin Zimmermann's avatar Levin Zimmermann

go/neo/proto/msgpack: Fix long 'types.Struct' decoding

In NEO/go protocol, we describe some parameters that can
be send via NEO packages with structures. On the wire all of
these structures are encoded as msgpack arrays [1]. Msgpack
arrays can have 1 or more bytes as a header [2]. Therefore
it's better to use "ReadArrayHeaderBytes" than using

  >>> data = data[1:]

which fails in case we have array with 3 or 5 header bytes.

[1] One could also declare a protocol where these parameters aren't
    send as msgpack arrays but as msgpack maps. See here for the
    msgpack map specification:

    https://github.com/msgpack/msgpack/blob/9aa092d6ca81/spec.md?plain=1#L338

[2] https://github.com/msgpack/msgpack/blob/9aa092d6ca81/spec.md?plain=1#L315
parent 9aa78799
......@@ -635,6 +635,10 @@ type OverflowCheck struct {
// stack operated by {Push,Pop}Checked
checkedStk []bool
// whether any 'goto overflow' has been emitted for
// current decoder
gotoEmitted bool
}
// push/pop checked state
......@@ -819,6 +823,8 @@ func (d *decoderCommon) overflowCheck() {
lendata = "uint64(" + lendata + ")"
}
d.bufDone.emit("if %s < %v { goto overflow }", lendata, &d.overflow.checkSize)
// Flag that we already committed a 'goto overflow' statement in current decoder
d.overflow.gotoEmitted = true
// if size for overflow check was only numeric - just
// accumulate it at generation time
......@@ -901,7 +907,7 @@ func (d *decoderCommon) generatedCode() string {
// `goto overflow` is not used only for empty structs
// NOTE for >0 check actual X in StdSizes{X} does not particularly matter
if (&types.StdSizes{8, 8}).Sizeof(d.typ) > 0 || d.enc != 'N' {
if ((&types.StdSizes{8, 8}).Sizeof(d.typ) > 0 || d.enc != 'N') && d.overflow.gotoEmitted {
code.emit("\noverflow:")
code.emit("return 0, ErrDecodeOverflow")
}
......@@ -1712,10 +1718,10 @@ func (d *decoderN) genStructHead(path string, typ *types.Struct, userType types.
// M: array<nfields>
func (s *sizerM) genStructHead(path string, typ *types.Struct, userType types.Type) {
s.size.Add(1) // mfixarray|marray16|marray32
if typ.NumFields() > 0x0f {
panic("TODO: struct with > 15 elements")
if !strings.Contains(path, ".") && !strings.Contains(path, "*") {
path = fmt.Sprintf("(*%s)", path)
}
s.size.AddExpr("msgpack.ArrayHeadSize(reflect.TypeOf(%s).NumField())", path)
}
func (e *encoderM) genStructHead(path string, typ *types.Struct, userType types.Type) {
......@@ -1727,16 +1733,22 @@ func (e *encoderM) genStructHead(path string, typ *types.Struct, userType types.
}
func (d *decoderM) genStructHead(path string, typ *types.Struct, userType types.Type) {
if typ.NumFields() > 0x0f {
panic("TODO: struct with > 15 elements")
}
d.resetPos()
d.emit("if op, opOk := msgpack.Op(data[%v]), msgpack.FixArray_4 | %d ; op != opOk {", d.n, typ.NumFields())
d.emit("return 0, &mstructDecodeError{%q, op, opOk}", d.pathName(path))
d.emit("}")
// we are going to go into msgp - flush previously queued
// overflow checks; put place for next overflow check after
// msgp is done.
d.overflowCheck()
defer d.overflowCheck()
d.n += 1
d.overflow.Add(1)
d.emit("{")
d.emit("_, tail, err := msgp.ReadArrayHeaderBytes(data)")
d.emit("if err != nil {")
d.emit(fmt.Sprintf("return 0, mdecodeErr(%q, err)", d.pathName(path)))
d.emit("}")
d.emit("%v += uint64(len(data) - len(tail))", d.var_("nread"))
d.emit("data = tail")
d.emit("}")
}
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment