Commit afe76d47 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 5e46e4fc
...@@ -635,6 +635,10 @@ type OverflowCheck struct { ...@@ -635,6 +635,10 @@ type OverflowCheck struct {
// stack operated by {Push,Pop}Checked // stack operated by {Push,Pop}Checked
checkedStk []bool checkedStk []bool
// whether any 'goto overflow' has been emitted for
// current decoder
gotoEmitted bool
} }
// push/pop checked state // push/pop checked state
...@@ -819,6 +823,8 @@ func (d *decoderCommon) overflowCheck() { ...@@ -819,6 +823,8 @@ func (d *decoderCommon) overflowCheck() {
lendata = "uint64(" + lendata + ")" lendata = "uint64(" + lendata + ")"
} }
d.bufDone.emit("if %s < %v { goto overflow }", lendata, &d.overflow.checkSize) 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 // if size for overflow check was only numeric - just
// accumulate it at generation time // accumulate it at generation time
...@@ -901,7 +907,7 @@ func (d *decoderCommon) generatedCode() string { ...@@ -901,7 +907,7 @@ func (d *decoderCommon) generatedCode() string {
// `goto overflow` is not used only for empty structs // `goto overflow` is not used only for empty structs
// NOTE for >0 check actual X in StdSizes{X} does not particularly matter // 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("\noverflow:")
code.emit("return 0, ErrDecodeOverflow") code.emit("return 0, ErrDecodeOverflow")
} }
...@@ -1712,10 +1718,10 @@ func (d *decoderN) genStructHead(path string, typ *types.Struct, userType types. ...@@ -1712,10 +1718,10 @@ func (d *decoderN) genStructHead(path string, typ *types.Struct, userType types.
// M: array<nfields> // M: array<nfields>
func (s *sizerM) genStructHead(path string, typ *types.Struct, userType types.Type) { func (s *sizerM) genStructHead(path string, typ *types.Struct, userType types.Type) {
s.size.Add(1) // mfixarray|marray16|marray32 if !strings.Contains(path, ".") && !strings.Contains(path, "*") {
if typ.NumFields() > 0x0f { path = fmt.Sprintf("(*%s)", path)
panic("TODO: struct with > 15 elements")
} }
s.size.AddExpr("msgpack.ArrayHeadSize(reflect.TypeOf(%s).NumField())", path)
} }
func (e *encoderM) genStructHead(path string, typ *types.Struct, userType types.Type) { 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. ...@@ -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) { func (d *decoderM) genStructHead(path string, typ *types.Struct, userType types.Type) {
if typ.NumFields() > 0x0f { d.resetPos()
panic("TODO: struct with > 15 elements")
}
d.emit("if op, opOk := msgpack.Op(data[%v]), msgpack.FixArray_4 | %d ; op != opOk {", d.n, typ.NumFields()) // we are going to go into msgp - flush previously queued
d.emit("return 0, &mstructDecodeError{%q, op, opOk}", d.pathName(path)) // overflow checks; put place for next overflow check after
d.emit("}") // msgp is done.
d.overflowCheck()
defer d.overflowCheck()
d.n += 1 d.emit("{")
d.overflow.Add(1) 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