From ae7d48a284637db91cb35724d9d71211ad9dd70a Mon Sep 17 00:00:00 2001 From: Kale Blankenship Date: Sat, 13 Jan 2018 16:03:21 -0800 Subject: [PATCH] Add round trip tests for marshal/unmarshal. (#4) * Add missing marshal/unmarshal methods. * Fixed bugs revealed by the tests. * Update FuzzMarshal corpus from marshal test --- decode.go | 47 +- encode.go | 49 +- ...0599ed0e0d15c60a61a5a72de5e4f25413131a69-1 | Bin 0 -> 3 bytes ...9cb1825f3363f3fa2d272f674dd14b09c2833a6-21 | Bin 0 -> 12 bytes ...61adf360afe4f75b2acf0bcde6053dac3527c9a-20 | Bin 0 -> 17 bytes fuzz/marshal/corpus/Error.bin | Bin 0 -> 71 bytes fuzz/marshal/corpus/ErrorCondition.bin | 1 + fuzz/marshal/corpus/Message.bin | Bin 0 -> 227 bytes fuzz/marshal/corpus/MessageHeader.bin | Bin 0 -> 17 bytes fuzz/marshal/corpus/MessageProperties.bin | Bin 0 -> 101 bytes fuzz/marshal/corpus/ReceiverSettleMode.bin | 1 + fuzz/marshal/corpus/SenderSettleMode.bin | 1 + fuzz/marshal/corpus/UUID.bin | Bin 0 -> 17 bytes fuzz/marshal/corpus/bool.bin | 1 + ...7bb52bfd4bf54e5fcd9e5d9ab32b96a1e43f459-19 | Bin 0 -> 12 bytes ...d90d61f26b81e1f6016f4a982f6dfa1c584fb31-17 | Bin 0 -> 18 bytes fuzz/marshal/corpus/int16.bin | Bin 0 -> 3 bytes fuzz/marshal/corpus/int32.bin | Bin 0 -> 5 bytes fuzz/marshal/corpus/int64.bin | Bin 0 -> 9 bytes fuzz/marshal/corpus/int8.bin | 1 + fuzz/marshal/corpus/lifetimePolicy.bin | Bin 0 -> 4 bytes fuzz/marshal/corpus/mapAnyAny.bin | Bin 0 -> 24 bytes fuzz/marshal/corpus/mapStringAny.bin | Bin 0 -> 21 bytes fuzz/marshal/corpus/mapSymbolAny.bin | Bin 0 -> 21 bytes fuzz/marshal/corpus/milliseconds.bin | Bin 0 -> 5 bytes fuzz/marshal/corpus/performAttach.bin | Bin 0 -> 308 bytes fuzz/marshal/corpus/performBegin.bin | Bin 0 -> 65 bytes fuzz/marshal/corpus/performClose.bin | Bin 0 -> 77 bytes fuzz/marshal/corpus/performDetach.bin | Bin 0 -> 83 bytes fuzz/marshal/corpus/performDisposition.bin | Bin 0 -> 23 bytes fuzz/marshal/corpus/performEnd.bin | Bin 0 -> 77 bytes fuzz/marshal/corpus/performFlow.bin | Bin 0 -> 62 bytes fuzz/marshal/corpus/performOpen.bin | Bin 0 -> 94 bytes fuzz/marshal/corpus/performTransfer.bin | Bin 0 -> 64 bytes fuzz/marshal/corpus/role.bin | 1 + fuzz/marshal/corpus/saslInit.bin | Bin 0 -> 30 bytes fuzz/marshal/corpus/saslMechanisms.bin | Bin 0 -> 22 bytes fuzz/marshal/corpus/saslOutcome.bin | Bin 0 -> 37 bytes fuzz/marshal/corpus/source.bin | Bin 0 -> 132 bytes fuzz/marshal/corpus/stateAccepted.bin | Bin 0 -> 4 bytes fuzz/marshal/corpus/stateModified.bin | Bin 0 -> 30 bytes fuzz/marshal/corpus/stateReceived.bin | Bin 0 -> 17 bytes fuzz/marshal/corpus/stateRejected.bin | Bin 0 -> 77 bytes fuzz/marshal/corpus/stateReleased.bin | Bin 0 -> 4 bytes fuzz/marshal/corpus/symbol.bin | 1 + fuzz/marshal/corpus/target.bin | Bin 0 -> 71 bytes fuzz/marshal/corpus/uint16.bin | 1 + fuzz/marshal/corpus/uint32.bin | 1 + fuzz/marshal/corpus/uint64.bin | 1 + fuzz/marshal/corpus/uint8.bin | 1 + fuzz/marshal/corpus/unsettled.bin | Bin 0 -> 23 bytes marshal_test.go | 421 ++++++++++++++++++ sasl.go | 8 +- types.go | 64 ++- 54 files changed, 582 insertions(+), 18 deletions(-) create mode 100644 fuzz/marshal/corpus/0599ed0e0d15c60a61a5a72de5e4f25413131a69-1 create mode 100644 fuzz/marshal/corpus/09cb1825f3363f3fa2d272f674dd14b09c2833a6-21 create mode 100644 fuzz/marshal/corpus/961adf360afe4f75b2acf0bcde6053dac3527c9a-20 create mode 100644 fuzz/marshal/corpus/Error.bin create mode 100644 fuzz/marshal/corpus/ErrorCondition.bin create mode 100644 fuzz/marshal/corpus/Message.bin create mode 100644 fuzz/marshal/corpus/MessageHeader.bin create mode 100644 fuzz/marshal/corpus/MessageProperties.bin create mode 100644 fuzz/marshal/corpus/ReceiverSettleMode.bin create mode 100644 fuzz/marshal/corpus/SenderSettleMode.bin create mode 100644 fuzz/marshal/corpus/UUID.bin create mode 100644 fuzz/marshal/corpus/bool.bin create mode 100644 fuzz/marshal/corpus/d7bb52bfd4bf54e5fcd9e5d9ab32b96a1e43f459-19 create mode 100644 fuzz/marshal/corpus/ed90d61f26b81e1f6016f4a982f6dfa1c584fb31-17 create mode 100644 fuzz/marshal/corpus/int16.bin create mode 100644 fuzz/marshal/corpus/int32.bin create mode 100644 fuzz/marshal/corpus/int64.bin create mode 100644 fuzz/marshal/corpus/int8.bin create mode 100644 fuzz/marshal/corpus/lifetimePolicy.bin create mode 100644 fuzz/marshal/corpus/mapAnyAny.bin create mode 100644 fuzz/marshal/corpus/mapStringAny.bin create mode 100644 fuzz/marshal/corpus/mapSymbolAny.bin create mode 100644 fuzz/marshal/corpus/milliseconds.bin create mode 100644 fuzz/marshal/corpus/performAttach.bin create mode 100644 fuzz/marshal/corpus/performBegin.bin create mode 100644 fuzz/marshal/corpus/performClose.bin create mode 100644 fuzz/marshal/corpus/performDetach.bin create mode 100644 fuzz/marshal/corpus/performDisposition.bin create mode 100644 fuzz/marshal/corpus/performEnd.bin create mode 100644 fuzz/marshal/corpus/performFlow.bin create mode 100644 fuzz/marshal/corpus/performOpen.bin create mode 100644 fuzz/marshal/corpus/performTransfer.bin create mode 100644 fuzz/marshal/corpus/role.bin create mode 100644 fuzz/marshal/corpus/saslInit.bin create mode 100644 fuzz/marshal/corpus/saslMechanisms.bin create mode 100644 fuzz/marshal/corpus/saslOutcome.bin create mode 100644 fuzz/marshal/corpus/source.bin create mode 100644 fuzz/marshal/corpus/stateAccepted.bin create mode 100644 fuzz/marshal/corpus/stateModified.bin create mode 100644 fuzz/marshal/corpus/stateReceived.bin create mode 100644 fuzz/marshal/corpus/stateRejected.bin create mode 100644 fuzz/marshal/corpus/stateReleased.bin create mode 100644 fuzz/marshal/corpus/symbol.bin create mode 100644 fuzz/marshal/corpus/target.bin create mode 100644 fuzz/marshal/corpus/uint16.bin create mode 100644 fuzz/marshal/corpus/uint32.bin create mode 100644 fuzz/marshal/corpus/uint64.bin create mode 100644 fuzz/marshal/corpus/uint8.bin create mode 100644 fuzz/marshal/corpus/unsettled.bin create mode 100644 marshal_test.go diff --git a/decode.go b/decode.go index 1752e617..d11e73f6 100644 --- a/decode.go +++ b/decode.go @@ -107,6 +107,11 @@ type unmarshaler interface { unmarshal(r reader) error } +// constantUnmarshaler allows the returned type to be a non-pointer +type constantUnmarshaler interface { + unmarshalConstant(r reader) error +} + // unmarshal decodes AMQP encoded data into i. // // The decoding method is based on the type of i. @@ -136,12 +141,38 @@ func unmarshal(r reader, i interface{}) (isNull bool, err error) { switch t := i.(type) { case unmarshaler: return isNull, t.unmarshal(r) + case constantUnmarshaler: // must be after unmarshaler + return false, t.unmarshalConstant(r) case *int: val, err := readInt(r) if err != nil { return isNull, err } *t = val + case *int8: + val, err := readInt(r) + if err != nil { + return isNull, err + } + *t = int8(val) + case *int16: + val, err := readInt(r) + if err != nil { + return isNull, err + } + *t = int16(val) + case *int32: + val, err := readInt(r) + if err != nil { + return isNull, err + } + *t = int32(val) + case *int64: + val, err := readInt(r) + if err != nil { + return isNull, err + } + *t = int64(val) case *uint64: val, err := readUint(r) if err != nil { @@ -241,12 +272,12 @@ func unmarshal(r reader, i interface{}) (isNull bool, err error) { } *t = v default: - // handle both *T and **T + // handle **T v := reflect.Indirect(reflect.ValueOf(i)) // can't unmarshal into a non-pointer if v.Kind() != reflect.Ptr { - return isNull, errorErrorf("unable to unmarshal into non-pointer %T", i) + return isNull, errorErrorf("unable to unmarshal %T", i) } // if the value being unmarshaled is null, @@ -678,15 +709,23 @@ func readComposite(r reader) (interface{}, error) { } iface := construct() - return unmarshal(r, iface) + _, err = unmarshal(r, iface) + return iface, err } var compositeTypes = [256]func() interface{}{ - typeCodeError: func() interface{} { return new(Error) }, + typeCodeError: func() interface{} { return new(Error) }, + // Lifetime Policies typeCodeDeleteOnClose: func() interface{} { return deleteOnClose }, typeCodeDeleteOnNoMessages: func() interface{} { return deleteOnNoMessages }, typeCodeDeleteOnNoLinks: func() interface{} { return deleteOnNoLinks }, typeCodeDeleteOnNoLinksOrMessages: func() interface{} { return deleteOnNoLinksOrMessages }, + // Delivery States + typeCodeStateAccepted: func() interface{} { return new(stateAccepted) }, + typeCodeStateModified: func() interface{} { return new(stateModified) }, + typeCodeStateReceived: func() interface{} { return new(stateReceived) }, + typeCodeStateRejected: func() interface{} { return new(stateRejected) }, + typeCodeStateReleased: func() interface{} { return new(stateReleased) }, } func readTimestamp(r reader) (time.Time, error) { diff --git a/encode.go b/encode.go index 85e43451..d78d8a2b 100644 --- a/encode.go +++ b/encode.go @@ -109,6 +109,18 @@ func marshal(wr writer, i interface{}) error { return writeInt64(wr, int64(t)) } return writeInt32(wr, int32(t)) + case int8: + err = wr.WriteByte(byte(typeCodeByte)) + if err != nil { + return err + } + binary.Write(wr, binary.BigEndian, t) + case int16: + err = wr.WriteByte(byte(typeCodeShort)) + if err != nil { + return err + } + binary.Write(wr, binary.BigEndian, t) case int64: return writeInt64(wr, t) case int32: @@ -123,6 +135,10 @@ func marshal(wr writer, i interface{}) error { err = writeMap(wr, t) case map[string]interface{}: err = writeMap(wr, t) + case map[symbol]interface{}: + err = writeMap(wr, t) + case unsettled: + err = writeMap(wr, t) case time.Time: err = writeTimestamp(wr, t) default: @@ -132,7 +148,7 @@ func marshal(wr writer, i interface{}) error { } func writeInt32(wr writer, n int32) error { - if n < 256 { + if n < 128 && n >= -128 { _, err := wr.Write([]byte{byte(typeCodeSmallint), byte(n)}) return err } @@ -145,7 +161,7 @@ func writeInt32(wr writer, n int32) error { } func writeInt64(wr writer, n int64) error { - if n < 256 { + if n < 128 && n >= -128 { _, err := wr.Write([]byte{byte(typeCodeSmalllong), byte(n)}) return err } @@ -399,10 +415,7 @@ func writeSlice(wr writer, isArray bool, of amqpType, numFields int, size int) e switch { // list0 - case numFields == 0: - if isArray { - return errorNew("invalid array length 0") - } + case numFields == 0 && isArray: return wr.WriteByte(byte(typeCodeList0)) // list8 @@ -470,6 +483,30 @@ func writeMap(wr writer, m interface{}) error { return err } } + case map[symbol]interface{}: + length = len(m) + for key, val := range m { + err := marshal(buf, key) + if err != nil { + return err + } + err = marshal(buf, val) + if err != nil { + return err + } + } + case unsettled: + length = len(m) + for key, val := range m { + err := marshal(buf, key) + if err != nil { + return err + } + err = marshal(buf, val) + if err != nil { + return err + } + } default: return errorErrorf("unsupported type or map type %T", m) } diff --git a/fuzz/marshal/corpus/0599ed0e0d15c60a61a5a72de5e4f25413131a69-1 b/fuzz/marshal/corpus/0599ed0e0d15c60a61a5a72de5e4f25413131a69-1 new file mode 100644 index 0000000000000000000000000000000000000000..a1d3d016f25b6028f3229ac2ee032b37f68e4d57 GIT binary patch literal 3 KcmZPw)dc_m+5m$9 literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/09cb1825f3363f3fa2d272f674dd14b09c2833a6-21 b/fuzz/marshal/corpus/09cb1825f3363f3fa2d272f674dd14b09c2833a6-21 new file mode 100644 index 0000000000000000000000000000000000000000..73d2fa4b2e215f75c929ca30c2a4be3f4c90b8da GIT binary patch literal 12 QcmX@e?%==>s8a|c02OBf^Z)<= literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/961adf360afe4f75b2acf0bcde6053dac3527c9a-20 b/fuzz/marshal/corpus/961adf360afe4f75b2acf0bcde6053dac3527c9a-20 new file mode 100644 index 0000000000000000000000000000000000000000..f692207778300525f62f23467b55c1723f5da87e GIT binary patch literal 17 ScmX@e?%==>s8a|f8X5p7-~^=r literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/Error.bin b/fuzz/marshal/corpus/Error.bin new file mode 100644 index 0000000000000000000000000000000000000000..2bded560e9352f92efa0739879acc9c0e23c6f12 GIT binary patch literal 71 zcmZPwmObFayjUPHx3It}FTX@LF()U#JT+w@e_DRNLP~0Ja#3bMNoIcDK^c~XtobDw XsYMG}GV{{%7cwX2r8F{t0dqD0lTH`# literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/ErrorCondition.bin b/fuzz/marshal/corpus/ErrorCondition.bin new file mode 100644 index 00000000..61174eda --- /dev/null +++ b/fuzz/marshal/corpus/ErrorCondition.bin @@ -0,0 +1 @@ +£ the condition \ No newline at end of file diff --git a/fuzz/marshal/corpus/Message.bin b/fuzz/marshal/corpus/Message.bin new file mode 100644 index 0000000000000000000000000000000000000000..feea83cbd0494e25af0ffdc89ceeb6bcb6c98a46 GIT binary patch literal 227 zcmZPwE;zu$>KO2z%fXHA-M1$H&dwALbk-b;_}oYhTtMJe(`|>-i1t+`3snn z5~~(6<)$uVDK0IrU&vgUU)sn325lD?bCsl4l;{`aBxdFvm|NMUh zgmLfsjD;L&`T6ce`K1K~42&^v7IGvd7J<18!6iUT7lJKd2C88QE?pq)sF0VLoT`wM zSyGads*sXclBiISSecWbn4-rJTpp^$5L|Ikm}wzPMq+Ws0xkwdCM8xjb`DMk00ps2 A2><{9 literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/MessageHeader.bin b/fuzz/marshal/corpus/MessageHeader.bin new file mode 100644 index 0000000000000000000000000000000000000000..b112c4881e1c31edb5896118c0906a5daccdbd1c GIT binary patch literal 17 YcmZPwE;zu$>KO2z%fVx04rDoY5)KL literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/MessageProperties.bin b/fuzz/marshal/corpus/MessageProperties.bin new file mode 100644 index 0000000000000000000000000000000000000000..9809896400ff2a18a2f6aaa2b64aa6a7c6dee714 GIT binary patch literal 101 zcmZPwE68(al#LT?K vtf3)px)#k042%iypZ|}5Fz#KSv5+GzKi|D5zqFu$fidRILXM=wA}|*KBtj)= literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/ReceiverSettleMode.bin b/fuzz/marshal/corpus/ReceiverSettleMode.bin new file mode 100644 index 00000000..a221c07d --- /dev/null +++ b/fuzz/marshal/corpus/ReceiverSettleMode.bin @@ -0,0 +1 @@ +P \ No newline at end of file diff --git a/fuzz/marshal/corpus/SenderSettleMode.bin b/fuzz/marshal/corpus/SenderSettleMode.bin new file mode 100644 index 00000000..a221c07d --- /dev/null +++ b/fuzz/marshal/corpus/SenderSettleMode.bin @@ -0,0 +1 @@ +P \ No newline at end of file diff --git a/fuzz/marshal/corpus/UUID.bin b/fuzz/marshal/corpus/UUID.bin new file mode 100644 index 0000000000000000000000000000000000000000..01f85b7836d4e85736e4b64481fe3719d779f422 GIT binary patch literal 17 YcmbQi$i&RT%Er#Y#m&RZ$1lJD01a#b7ytkO literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/bool.bin b/fuzz/marshal/corpus/bool.bin new file mode 100644 index 00000000..8c7e5a66 --- /dev/null +++ b/fuzz/marshal/corpus/bool.bin @@ -0,0 +1 @@ +A \ No newline at end of file diff --git a/fuzz/marshal/corpus/d7bb52bfd4bf54e5fcd9e5d9ab32b96a1e43f459-19 b/fuzz/marshal/corpus/d7bb52bfd4bf54e5fcd9e5d9ab32b96a1e43f459-19 new file mode 100644 index 0000000000000000000000000000000000000000..ef54980c415a2388024b7836eaf2616cb494caa6 GIT binary patch literal 12 TcmX@e?%==>s8i^`&`<~f777Ds literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/ed90d61f26b81e1f6016f4a982f6dfa1c584fb31-17 b/fuzz/marshal/corpus/ed90d61f26b81e1f6016f4a982f6dfa1c584fb31-17 new file mode 100644 index 0000000000000000000000000000000000000000..1b24f56f43c643a98aedb847f23b8b5e2a3449bf GIT binary patch literal 18 QcmX@e;KC3r>)?PO04E&-)&Kwi literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/int16.bin b/fuzz/marshal/corpus/int16.bin new file mode 100644 index 0000000000000000000000000000000000000000..d3855907c8ef74ff4e7f9a970643f83b1dd8877a GIT binary patch literal 3 KcmYdnU;qFDCII39 literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/int32.bin b/fuzz/marshal/corpus/int32.bin new file mode 100644 index 0000000000000000000000000000000000000000..de436b100946e989a277aa4e9120d905154ac3a5 GIT binary patch literal 5 McmXSHU|?VX00cS!@&Et; literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/int64.bin b/fuzz/marshal/corpus/int64.bin new file mode 100644 index 0000000000000000000000000000000000000000..dfc7cefb24b9af8bc1261c14bc3b434ebc586512 GIT binary patch literal 9 LcmZo{V1NJs2$BH; literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/int8.bin b/fuzz/marshal/corpus/int8.bin new file mode 100644 index 00000000..28a81742 --- /dev/null +++ b/fuzz/marshal/corpus/int8.bin @@ -0,0 +1 @@ +Q€ \ No newline at end of file diff --git a/fuzz/marshal/corpus/lifetimePolicy.bin b/fuzz/marshal/corpus/lifetimePolicy.bin new file mode 100644 index 0000000000000000000000000000000000000000..dcf663340883f5b34fea0153dcb645a94407da29 GIT binary patch literal 4 LcmZPw)^-H|0hj>9 literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/mapAnyAny.bin b/fuzz/marshal/corpus/mapAnyAny.bin new file mode 100644 index 0000000000000000000000000000000000000000..ba7b06f49892af576ea628f7245f78d16b855867 GIT binary patch literal 24 ccmX>o#?<&941Vh^;9_88QetIe=ip=j0HFy9Z~y=R literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/mapStringAny.bin b/fuzz/marshal/corpus/mapStringAny.bin new file mode 100644 index 0000000000000000000000000000000000000000..99e10a73ceb26a765e0c44b3bb782a5feba348d9 GIT binary patch literal 21 ccmX>o%(RdtBe6JR0T%-!lM*W%I|nBN06tm-5&!@I literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/mapSymbolAny.bin b/fuzz/marshal/corpus/mapSymbolAny.bin new file mode 100644 index 0000000000000000000000000000000000000000..92c8b1970b8526310c364bfa8f9c37efb9806737 GIT binary patch literal 21 ccmX>o%(R##Be6JR0T%-!lM*W%I|nBN06u;M6aWAK literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/milliseconds.bin b/fuzz/marshal/corpus/milliseconds.bin new file mode 100644 index 0000000000000000000000000000000000000000..7d75f178d5af8cb6df6ceacfc7e7c905e2790113 GIT binary patch literal 5 McmXS5U{DtT00NT$r~m)} literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/performAttach.bin b/fuzz/marshal/corpus/performAttach.bin new file mode 100644 index 0000000000000000000000000000000000000000..d552b6b9d9b4c10ca12676edf9adb84521c66a07 GIT binary patch literal 308 zcmZPw7P`Q|z^KE(z`(bVJuN@qFEKZ@fPrl}pHl!+03$=N#({cnu!LhuN>LEgV(y&G zylmZ+)RM&Hi~2a~9_V1#{!fe7Pbp~T$60;|O2+-t=LRHW4vI1@*_eZaxEm@O?o-#M`W%uGrw0x}Qs QFfC>WiUt(r7li5p0JFjso&W#< literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/performClose.bin b/fuzz/marshal/corpus/performClose.bin new file mode 100644 index 0000000000000000000000000000000000000000..d3742c2e8852ffc25d3077d27b2ba58cb7f23318 GIT binary patch literal 77 zcmZPwmN?+S$Pg@hz=?UWKw@rTfmL39iEd&}PJVf6%0m9M{CtI!)Z*l#%z~24{JeuQ dEDKrlOEOZ67P4gKrR6VVPRvVbWB>!^YykgI8Dsze literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/performDetach.bin b/fuzz/marshal/corpus/performDetach.bin new file mode 100644 index 0000000000000000000000000000000000000000..182e27d7cb9db9732ebdc38c57a1fb790db8df79 GIT binary patch literal 83 zcmZPw7CYd_T)@B}$l%BjEPKF-d9grZZef8{UVe#gVopwed1}f+{!^YykfS8Djtd literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/performFlow.bin b/fuzz/marshal/corpus/performFlow.bin new file mode 100644 index 0000000000000000000000000000000000000000..4f4e02ac2cdb37f4aa835df1a0ce309687bb276a GIT binary patch literal 62 zcmZPw7CvCfUBJM=m;@vQ844H}eSHcTi~n*JFtD1b1I4OxfrRX(0tVi+kB*K9d6*Wn Or{(7d6y+C$>H+}65D;ewU9Y2KYt-dQeu%_Mt*Tg0Rw|TMgjw`!vk){#hgGXpZw&+oK!Fms0Pa8 gU|h@wlygojcmQGoC4kI>JWPw(fuaFL`30f60Jl&dyZ`_I literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/performTransfer.bin b/fuzz/marshal/corpus/performTransfer.bin new file mode 100644 index 0000000000000000000000000000000000000000..7240de8e134add192e07d176e5ea569b711748ff GIT binary patch literal 64 zcmZPw7CE5GUBJN5u^dP+nJi#W%gpMgao@Q?=tkF{Z`*IhkpxC7HRY zx&`?;naPz5!P>5iIg9gw0=fAqsRt#P7IOj3uu99!DM>9_$eEN_q)?WaQ=01VK!kCz hP-1RjfmLF1a%w?IYKm1(W^u^_4#vf7Kvm9(1pv~YF0uds literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/stateAccepted.bin b/fuzz/marshal/corpus/stateAccepted.bin new file mode 100644 index 0000000000000000000000000000000000000000..2c76fcb53916b30b1c330a21745942b2293f7cea GIT binary patch literal 4 LcmZPwR&fOY0g3>< literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/stateModified.bin b/fuzz/marshal/corpus/stateModified.bin new file mode 100644 index 0000000000000000000000000000000000000000..dcea9403d4be3e22b2210a793a40de5d80dc0285 GIT binary patch literal 30 lcmZPwRzD!g?C5w!^Yyb!O8HfM? literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/stateReleased.bin b/fuzz/marshal/corpus/stateReleased.bin new file mode 100644 index 0000000000000000000000000000000000000000..da56ff166cab3146eece6bfaab9d05fb37f96e66 GIT binary patch literal 4 LcmZPwR&xaa0geE_ literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/symbol.bin b/fuzz/marshal/corpus/symbol.bin new file mode 100644 index 00000000..993b25ea --- /dev/null +++ b/fuzz/marshal/corpus/symbol.bin @@ -0,0 +1 @@ +£a symbol \ No newline at end of file diff --git a/fuzz/marshal/corpus/target.bin b/fuzz/marshal/corpus/target.bin new file mode 100644 index 0000000000000000000000000000000000000000..1c4ea6faf0790e80dde8fdb7c9cd10240aa0bed1 GIT binary patch literal 71 zcmZPw);!?EzK}gFKi@GWr6`DLF?UX8Ubb#ZYDr>pMgao@Q?=tkF{Z`*IhkpxC7HRY bx&`?;naPz5!P>45I2aeRB_$R)Cl&wzL^T(d literal 0 HcmV?d00001 diff --git a/fuzz/marshal/corpus/uint16.bin b/fuzz/marshal/corpus/uint16.bin new file mode 100644 index 00000000..1823ec6b --- /dev/null +++ b/fuzz/marshal/corpus/uint16.bin @@ -0,0 +1 @@ +`ÿÿ \ No newline at end of file diff --git a/fuzz/marshal/corpus/uint32.bin b/fuzz/marshal/corpus/uint32.bin new file mode 100644 index 00000000..f5f7b669 --- /dev/null +++ b/fuzz/marshal/corpus/uint32.bin @@ -0,0 +1 @@ +pÿÿÿÿ \ No newline at end of file diff --git a/fuzz/marshal/corpus/uint64.bin b/fuzz/marshal/corpus/uint64.bin new file mode 100644 index 00000000..d777e4e7 --- /dev/null +++ b/fuzz/marshal/corpus/uint64.bin @@ -0,0 +1 @@ +€ÿÿÿÿÿÿÿÿ \ No newline at end of file diff --git a/fuzz/marshal/corpus/uint8.bin b/fuzz/marshal/corpus/uint8.bin new file mode 100644 index 00000000..7fe5fb98 --- /dev/null +++ b/fuzz/marshal/corpus/uint8.bin @@ -0,0 +1 @@ +Pÿ \ No newline at end of file diff --git a/fuzz/marshal/corpus/unsettled.bin b/fuzz/marshal/corpus/unsettled.bin new file mode 100644 index 0000000000000000000000000000000000000000..bafd31919b9ca26b9eaca6eca7cf485a45224a99 GIT binary patch literal 23 ecmX>o%CwL#EkEBSH7BzywWu;AF`Xe;#T5W!QU~Y& literal 0 HcmV?d00001 diff --git a/marshal_test.go b/marshal_test.go new file mode 100644 index 00000000..f73814a1 --- /dev/null +++ b/marshal_test.go @@ -0,0 +1,421 @@ +package amqp + +import ( + "bytes" + "fmt" + "io/ioutil" + "math" + "os" + "path/filepath" + "reflect" + "strings" + "testing" + "time" + + "github.com/google/go-cmp/cmp" +) + +func TestFraming(t *testing.T) { + t.Skip("TODO: add frame marshaling tests") + // frameHeader{}, + // protoHeader{}, + // frame{}, +} + +func TestMarshalUnmarshal(t *testing.T) { + _, updateFuzzCorpus := os.LookupEnv("UPDATE_FUZZ_CORPUS") + + types := []interface{}{ + &performOpen{ + ContainerID: "foo", + Hostname: "bar.host", + MaxFrameSize: 4200, + ChannelMax: 13, + OutgoingLocales: []symbol{"fooLocale"}, + IncomingLocales: []symbol{"barLocale"}, + OfferedCapabilities: []symbol{"fooCap"}, + DesiredCapabilities: []symbol{"barCap"}, + Properties: map[symbol]interface{}{ + "fooProp": 45, + }, + }, + &performBegin{ + RemoteChannel: 4321, + NextOutgoingID: 730000, + IncomingWindow: 9876654, + OutgoingWindow: 123555, + HandleMax: 9757, + OfferedCapabilities: []symbol{"fooCap"}, + DesiredCapabilities: []symbol{"barCap"}, + Properties: map[symbol]interface{}{ + "fooProp": 45, + }, + }, + &performAttach{ + Name: "fooName", + Handle: 435982, + Role: roleSender, + SenderSettleMode: sndSettle(ModeMixed), + ReceiverSettleMode: rcvSettle(ModeSecond), + Source: &source{ + Address: "fooAddr", + Durable: 2, + ExpiryPolicy: "link-detach", + Timeout: 635, + Dynamic: true, + DynamicNodeProperties: map[symbol]interface{}{ + "lifetime-policy": deleteOnClose, + }, + DistributionMode: "some-mode", + Filter: map[symbol]interface{}{ + "foo:filter": "bar value", + }, + Outcomes: []symbol{"amqp:accepted:list"}, + Capabilities: []symbol{"barCap"}, + }, + Target: &target{ + Address: "fooAddr", + Durable: 2, + ExpiryPolicy: "link-detach", + Timeout: 635, + Dynamic: true, + DynamicNodeProperties: map[symbol]interface{}{ + "lifetime-policy": deleteOnClose, + }, + Capabilities: []symbol{"barCap"}, + }, + Unsettled: unsettled{ + "fooDeliveryTag": &stateAccepted{}, + }, + IncompleteUnsettled: true, + InitialDeliveryCount: 3184, + MaxMessageSize: 75983, + OfferedCapabilities: []symbol{"fooCap"}, + DesiredCapabilities: []symbol{"barCap"}, + Properties: map[symbol]interface{}{ + "fooProp": 45, + }, + }, + role(true), + &unsettled{ + "fooDeliveryTag": &stateAccepted{}, + }, + &source{ + Address: "fooAddr", + Durable: 2, + ExpiryPolicy: "link-detach", + Timeout: 635, + Dynamic: true, + DynamicNodeProperties: map[symbol]interface{}{ + "lifetime-policy": deleteOnClose, + }, + DistributionMode: "some-mode", + Filter: map[symbol]interface{}{ + "foo:filter": "bar value", + }, + Outcomes: []symbol{"amqp:accepted:list"}, + Capabilities: []symbol{"barCap"}, + }, + &target{ + Address: "fooAddr", + Durable: 2, + ExpiryPolicy: "link-detach", + Timeout: 635, + Dynamic: true, + DynamicNodeProperties: map[symbol]interface{}{ + "lifetime-policy": deleteOnClose, + }, + Capabilities: []symbol{"barCap"}, + }, + &performFlow{ + NextIncomingID: uint32Ptr(354), + IncomingWindow: 4352, + NextOutgoingID: 85324, + OutgoingWindow: 24378634, + Handle: uint32Ptr(341543), + DeliveryCount: uint32Ptr(31341), + LinkCredit: uint32Ptr(7634), + Available: uint32Ptr(878321), + Drain: true, + Echo: true, + Properties: map[symbol]interface{}{ + "fooProp": 45, + }, + }, + &performTransfer{ + Handle: 34983, + DeliveryID: uint32Ptr(564), + DeliveryTag: []byte("foo tag"), + MessageFormat: uint32Ptr(34), + Settled: true, + More: true, + ReceiverSettleMode: rcvSettle(ModeSecond), + State: &stateReceived{}, + Resume: true, + Aborted: true, + Batchable: true, + Payload: []byte("very important payload"), + }, + &performDisposition{ + Role: roleSender, + First: 5644444, + Last: uint32Ptr(423), + Settled: true, + State: &stateReleased{}, + Batchable: true, + }, + &performDetach{ + Handle: 4352, + Closed: true, + Error: &Error{ + Condition: ErrorNotAllowed, + Description: "foo description", + Info: map[string]interface{}{ + "other": "info", + "and": 875, + }, + }, + }, + ErrorCondition("the condition"), + &Error{ + Condition: ErrorNotAllowed, + Description: "foo description", + Info: map[string]interface{}{ + "other": "info", + "and": 875, + }, + }, + &performEnd{ + Error: &Error{ + Condition: ErrorNotAllowed, + Description: "foo description", + Info: map[string]interface{}{ + "other": "info", + "and": 875, + }, + }, + }, + &performClose{ + Error: &Error{ + Condition: ErrorNotAllowed, + Description: "foo description", + Info: map[string]interface{}{ + "other": "info", + "and": 875, + }, + }, + }, + &Message{ + Header: &MessageHeader{ + Durable: true, + Priority: 234, + TTL: 10 * time.Second, + FirstAcquirer: true, + DeliveryCount: 32, + }, + DeliveryAnnotations: map[interface{}]interface{}{ + 42: "answer", + }, + Annotations: map[interface{}]interface{}{ + 42: "answer", + }, + Properties: &MessageProperties{ + MessageID: "yo", + UserID: []byte("baz"), + To: "me", + Subject: "sup?", + ReplyTo: "you", + CorrelationID: 34513, + ContentType: "text/plain", + ContentEncoding: "UTF-8", + AbsoluteExpiryTime: time.Date(2018, 01, 13, 14, 24, 07, 0, time.UTC), + CreationTime: time.Date(2018, 01, 13, 14, 14, 07, 0, time.UTC), + GroupID: "fooGroup", + GroupSequence: 89324, + ReplyToGroupID: "barGroup", + }, + ApplicationProperties: map[string]interface{}{ + "baz": "foo", + }, + Data: []byte("A nice little data payload."), + Value: 42, + Footer: map[interface{}]interface{}{ + "hash": []uint8{0, 1, 2, 34, 5, 6, 7, 8, 9, 0}, + }, + }, + &MessageHeader{ + Durable: true, + Priority: 234, + TTL: 10 * time.Second, + FirstAcquirer: true, + DeliveryCount: 32, + }, + &MessageProperties{ + MessageID: "yo", + UserID: []byte("baz"), + To: "me", + Subject: "sup?", + ReplyTo: "you", + CorrelationID: 34513, + ContentType: "text/plain", + ContentEncoding: "UTF-8", + AbsoluteExpiryTime: time.Date(2018, 01, 13, 14, 24, 07, 0, time.UTC), + CreationTime: time.Date(2018, 01, 13, 14, 14, 07, 0, time.UTC), + GroupID: "fooGroup", + GroupSequence: 89324, + ReplyToGroupID: "barGroup", + }, + &stateReceived{ + SectionNumber: 234, + SectionOffset: 8973, + }, + &stateAccepted{}, + &stateRejected{ + Error: &Error{ + Condition: ErrorStolen, + Description: "foo description", + Info: map[string]interface{}{ + "other": "info", + "and": 875, + }, + }, + }, + &stateReleased{}, + &stateModified{ + DeliveryFailed: true, + UndeliverableHere: true, + MessageAnnotations: map[symbol]interface{}{ + "more": "annotations", + }, + }, + &saslInit{ + Mechanism: "FOO", + InitialResponse: []byte("BAR\x00RESPONSE\x00"), + Hostname: "me", + }, + &saslMechanisms{ + Mechanisms: []symbol{"FOO", "BAR", "BAZ"}, + }, + &saslOutcome{ + Code: codeSASLSysPerm, + AdditionalData: []byte("here's some info for you..."), + }, + symbol("a symbol"), + milliseconds(10 * time.Second), + &mapAnyAny{ + -1234: []uint8{0, 1, 2, 34, 5, 6, 7, 8, 9, 0}, + }, + &mapStringAny{ + "hash": []uint8{0, 1, 2, 34, 5, 6, 7, 8, 9, 0}, + }, + &mapSymbolAny{ + "hash": []uint8{0, 1, 2, 34, 5, 6, 7, 8, 9, 0}, + }, + UUID{1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16}, + lifetimePolicy(typeCodeDeleteOnClose), + SenderSettleMode(1), + ReceiverSettleMode(1), + bool(true), + int8(math.MaxInt8), + int8(math.MinInt8), + int16(math.MaxInt16), + int16(math.MinInt16), + int32(math.MaxInt32), + int32(math.MinInt32), + int64(math.MaxInt64), + int64(math.MinInt64), + uint8(math.MaxUint8), + uint16(math.MaxUint16), + uint32(math.MaxUint32), + uint64(math.MaxUint64), + } + + for _, typ := range types { + t.Run(fmt.Sprintf("%T", typ), func(t *testing.T) { + var buf bytes.Buffer + err := marshal(&buf, typ) + if err != nil { + t.Error(fmt.Sprintf("%+v", err)) + } + + if updateFuzzCorpus { + name := fmt.Sprintf("%T.bin", typ) + name = strings.TrimPrefix(name, "amqp.") + name = strings.TrimPrefix(name, "*amqp.") + path := filepath.Join("fuzz/marshal/corpus", name) + err = ioutil.WriteFile(path, buf.Bytes(), 0644) + if err != nil { + t.Error(err) + } + } + + newTyp := reflect.New(reflect.TypeOf(typ)) + _, err = unmarshal(&buf, newTyp.Interface()) + if err != nil { + t.Error(fmt.Sprintf("%v", err)) + return + } + + cmpTyp := reflect.Indirect(newTyp).Interface() + cmpOpts := cmp.Options{ + DeepAllowUnexported(typ, cmpTyp), + } + if !cmp.Equal(typ, cmpTyp, cmpOpts...) { + t.Errorf("Roundtrip produced different results:\n %s", cmp.Diff(typ, cmpTyp, cmpOpts...)) + } + }) + } +} + +func sndSettle(m SenderSettleMode) *SenderSettleMode { + return &m +} +func rcvSettle(m ReceiverSettleMode) *ReceiverSettleMode { + return &m +} + +func uint32Ptr(u uint32) *uint32 { + return &u +} + +// from https://github.com/google/go-cmp/issues/40 +func DeepAllowUnexported(vs ...interface{}) cmp.Option { + m := make(map[reflect.Type]struct{}) + for _, v := range vs { + structTypes(reflect.ValueOf(v), m) + } + var typs []interface{} + for t := range m { + typs = append(typs, reflect.New(t).Elem().Interface()) + } + return cmp.AllowUnexported(typs...) +} + +func structTypes(v reflect.Value, m map[reflect.Type]struct{}) { + if !v.IsValid() { + return + } + switch v.Kind() { + case reflect.Ptr: + if !v.IsNil() { + structTypes(v.Elem(), m) + } + case reflect.Interface: + if !v.IsNil() { + structTypes(v.Elem(), m) + } + case reflect.Slice, reflect.Array: + for i := 0; i < v.Len(); i++ { + structTypes(v.Index(i), m) + } + case reflect.Map: + for _, k := range v.MapKeys() { + structTypes(v.MapIndex(k), m) + } + case reflect.Struct: + m[v.Type()] = struct{}{} + for i := 0; i < v.NumField(); i++ { + structTypes(v.Field(i), m) + } + } +} diff --git a/sasl.go b/sasl.go index b55bdd91..69b1ddbd 100644 --- a/sasl.go +++ b/sasl.go @@ -15,10 +15,14 @@ const ( saslMechanismANONYMOUS symbol = "ANONYMOUS" ) -type saslCode int +type saslCode uint8 + +func (s saslCode) marshal(wr writer) error { + return marshal(wr, uint8(s)) +} func (s *saslCode) unmarshal(r reader) error { - _, err := unmarshal(r, (*int)(s)) + _, err := unmarshal(r, (*uint8)(s)) return err } diff --git a/types.go b/types.go index 24fb7460..5247df0e 100644 --- a/types.go +++ b/types.go @@ -314,6 +314,7 @@ func (b *performBegin) marshal(wr writer) error { {value: b.HandleMax, omit: b.HandleMax == 0}, {value: b.OfferedCapabilities, omit: len(b.OfferedCapabilities) == 0}, {value: b.DesiredCapabilities, omit: len(b.DesiredCapabilities) == 0}, + {value: b.Properties, omit: b.Properties == nil}, }...) } @@ -570,6 +571,10 @@ type deliveryState interface{} // TODO: http://docs.oasis-open.org/amqp/core/v1. type unsettled map[string]deliveryState +func (u unsettled) marshal(wr writer) error { + return writeMap(wr, u) +} + func (u *unsettled) unmarshal(r reader) error { mr, err := newMapReader(r) if err != nil { @@ -2184,6 +2189,14 @@ func (si *saslInit) marshal(wr writer) error { }...) } +func (si *saslInit) unmarshal(r reader) error { + return unmarshalComposite(r, typeCodeSASLInit, []unmarshalField{ + {field: &si.Mechanism, handleNull: required("saslInit.Mechanism")}, + {field: &si.InitialResponse}, + {field: &si.Hostname}, + }...) +} + /* @@ -2195,6 +2208,12 @@ type saslMechanisms struct { Mechanisms []symbol } +func (sm saslMechanisms) marshal(wr writer) error { + return marshalComposite(wr, typeCodeSASLMechanism, []marshalField{ + {value: sm.Mechanisms, omit: false}, + }...) +} + func (sm *saslMechanisms) unmarshal(r reader) error { return unmarshalComposite(r, typeCodeSASLMechanism, unmarshalField{field: &sm.Mechanisms, handleNull: required("SASLMechanisms.Mechanisms")}, @@ -2218,6 +2237,13 @@ type saslOutcome struct { AdditionalData []byte } +func (so saslOutcome) marshal(wr writer) error { + return marshalComposite(wr, typeCodeSASLOutcome, []marshalField{ + {value: so.Code, omit: false}, + {value: so.AdditionalData, omit: len(so.AdditionalData) == 0}, + }...) +} + func (so *saslOutcome) unmarshal(r reader) error { return unmarshalComposite(r, typeCodeSASLOutcome, []unmarshalField{ {field: &so.Code, handleNull: required("SASLOutcome.Code")}, @@ -2272,6 +2298,10 @@ func (m *milliseconds) unmarshal(r reader) error { // inconsistently typed. type mapAnyAny map[interface{}]interface{} +func (m mapAnyAny) marshal(wr writer) error { + return writeMap(wr, map[interface{}]interface{}(m)) +} + func (m *mapAnyAny) unmarshal(r reader) error { mr, err := newMapReader(r) if err != nil { @@ -2306,6 +2336,10 @@ func (m *mapAnyAny) unmarshal(r reader) error { // mapStringAny is used to decode AMQP maps that have string keys type mapStringAny map[string]interface{} +func (m mapStringAny) marshal(wr writer) error { + return writeMap(wr, map[string]interface{}(m)) +} + func (m *mapStringAny) unmarshal(r reader) error { mr, err := newMapReader(r) if err != nil { @@ -2329,6 +2363,10 @@ func (m *mapStringAny) unmarshal(r reader) error { // mapStringAny is used to decode AMQP maps that have Symbol keys type mapSymbolAny map[symbol]interface{} +func (m mapSymbolAny) marshal(wr writer) error { + return writeMap(wr, map[symbol]interface{}(m)) +} + func (f *mapSymbolAny) unmarshal(r reader) error { mr, err := newMapReader(r) if err != nil { @@ -2396,12 +2434,26 @@ func (p lifetimePolicy) marshal(wr writer) error { return err } -// unmarshal isn't defined on a pointer because concrete types should -// always be created by the compositeTypes constructor functions. -func (p lifetimePolicy) unmarshal(r reader) error { - data := r.Next(4) // constructor + empty list - if len(data) != 4 { - return errorErrorf("invalid size %d for lifetime-policy", len(data)) +func (p *lifetimePolicy) unmarshal(r reader) error { + typ, fields, err := readCompositeHeader(r) + if err != nil { + return err + } + if fields != 0 { + return errorErrorf("invalid size %d for lifetime-policy") + } + *p = lifetimePolicy(typ) + return nil +} + +func (p lifetimePolicy) unmarshalConstant(r reader) error { + var tmp [4]byte + n, err := r.Read(tmp[:]) + if err != nil { + return err + } + if n != 4 { + return errorErrorf("invalid size %d for lifetime-policy") } return nil }