Skip to content

Commit

Permalink
Enforce maximum reflection decoding depth (#18)
Browse files Browse the repository at this point in the history
Use a default depth of 200
  • Loading branch information
2opremio authored Sep 19, 2023
1 parent 8017fc4 commit 6c7b684
Show file tree
Hide file tree
Showing 14 changed files with 460 additions and 336 deletions.
15 changes: 10 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
name: Go
on: [push, pull_request]

on:
push:
branches: [master]
pull_request:

jobs:
build:
name: Build
strategy:
matrix:
go: [1.13, 1.14]
go: ["1.20", "1.21"]
runs-on: ubuntu-latest
container: golang:${{ matrix.go }}-stretch
container: golang:${{ matrix.go }}-bookworm
steps:
- run: go get golang.org/x/tools/cmd/goimports
- run: go get golang.org/x/lint/golint
- run: go install golang.org/x/tools/cmd/goimports@latest
- run: go install golang.org/x/lint/golint@latest
- uses: actions/checkout@v1
- run: ./gotest.sh
80 changes: 47 additions & 33 deletions xdr/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ by v and performs a mapping of underlying XDR types to Go types as follows:
Notes and Limitations:
* Automatic unmarshalling of variable and fixed-length arrays of uint8s
requires a special struct tag `xdropaque:"false"` since byte slices and
byte arrays are assumed to be opaque data and byte is a Go alias for uint8
thus indistinguishable under reflection
* Cyclic data structures are not supported and will result in infinite loops
- Automatic unmarshalling of variable and fixed-length arrays of uint8s
requires a special struct tag `xdropaque:"false"` since byte slices and
byte arrays are assumed to be opaque data and byte is a Go alias for uint8
thus indistinguishable under reflection
- Cyclic data structures are not supported and will result in infinite loops
If any issues are encountered during the unmarshalling process, an
UnmarshalError is returned with a human readable description as well as
Expand Down Expand Up @@ -120,8 +120,9 @@ type Decoder struct {
// An UnmarshalError is returned if there are insufficient bytes remaining.
//
// Reference:
// RFC Section 4.1 - Integer
// 32-bit big-endian signed integer in range [-2147483648, 2147483647]
//
// RFC Section 4.1 - Integer
// 32-bit big-endian signed integer in range [-2147483648, 2147483647]
func (d *Decoder) DecodeInt() (rv int32, err error) {
data := d.data
if len(data) < 4 {
Expand All @@ -142,8 +143,9 @@ func (d *Decoder) DecodeInt() (rv int32, err error) {
// An UnmarshalError is returned if there are insufficient bytes remaining.
//
// Reference:
// RFC Section 4.2 - Unsigned Integer
// 32-bit big-endian unsigned integer in range [0, 4294967295]
//
// RFC Section 4.2 - Unsigned Integer
// 32-bit big-endian unsigned integer in range [0, 4294967295]
func (d *Decoder) DecodeUint() (rv uint32, err error) {
data := d.data
if len(data) < 4 {
Expand All @@ -166,8 +168,9 @@ func (d *Decoder) DecodeUint() (rv uint32, err error) {
// the parsed enumeration value is not one of the provided valid values.
//
// Reference:
// RFC Section 4.3 - Enumeration
// Represented as an XDR encoded signed integer
//
// RFC Section 4.3 - Enumeration
// Represented as an XDR encoded signed integer
func (d *Decoder) DecodeEnum(validEnums map[int32]bool) (rv int32, err error) {
val, err := d.DecodeInt()
if err != nil {
Expand All @@ -187,8 +190,9 @@ func (d *Decoder) DecodeEnum(validEnums map[int32]bool) (rv int32, err error) {
// the parsed value is not a 0 or 1.
//
// Reference:
// RFC Section 4.4 - Boolean
// Represented as an XDR encoded enumeration where 0 is false and 1 is true
//
// RFC Section 4.4 - Boolean
// Represented as an XDR encoded enumeration where 0 is false and 1 is true
func (d *Decoder) DecodeBool() (rv bool, err error) {
val, err := d.DecodeInt()
if err != nil {
Expand All @@ -210,8 +214,9 @@ func (d *Decoder) DecodeBool() (rv bool, err error) {
// An UnmarshalError is returned if there are insufficient bytes remaining.
//
// Reference:
// RFC Section 4.5 - Hyper Integer
// 64-bit big-endian signed integer in range [-9223372036854775808, 9223372036854775807]
//
// RFC Section 4.5 - Hyper Integer
// 64-bit big-endian signed integer in range [-9223372036854775808, 9223372036854775807]
func (d *Decoder) DecodeHyper() (rv int64, err error) {
data := d.data
if len(data) < 8 {
Expand All @@ -234,8 +239,9 @@ func (d *Decoder) DecodeHyper() (rv int64, err error) {
// An UnmarshalError is returned if there are insufficient bytes remaining.
//
// Reference:
// RFC Section 4.5 - Unsigned Hyper Integer
// 64-bit big-endian unsigned integer in range [0, 18446744073709551615]
//
// RFC Section 4.5 - Unsigned Hyper Integer
// 64-bit big-endian unsigned integer in range [0, 18446744073709551615]
func (d *Decoder) DecodeUhyper() (rv uint64, err error) {
data := d.data
if len(data) < 8 {
Expand All @@ -258,8 +264,9 @@ func (d *Decoder) DecodeUhyper() (rv uint64, err error) {
// An UnmarshalError is returned if there are insufficient bytes remaining.
//
// Reference:
// RFC Section 4.6 - Floating Point
// 32-bit single-precision IEEE 754 floating point
//
// RFC Section 4.6 - Floating Point
// 32-bit single-precision IEEE 754 floating point
func (d *Decoder) DecodeFloat() (rv float32, err error) {
data := d.data
if len(data) < 4 {
Expand All @@ -280,8 +287,9 @@ func (d *Decoder) DecodeFloat() (rv float32, err error) {
// An UnmarshalError is returned if there are insufficient bytes remaining.
//
// Reference:
// RFC Section 4.7 - Double-Precision Floating Point
// 64-bit double-precision IEEE 754 floating point
//
// RFC Section 4.7 - Double-Precision Floating Point
// 64-bit double-precision IEEE 754 floating point
func (d *Decoder) DecodeDouble() (rv float64, err error) {
data := d.data
if len(data) < 8 {
Expand Down Expand Up @@ -310,8 +318,9 @@ func (d *Decoder) DecodeDouble() (rv float64, err error) {
// multiple of 4.
//
// Reference:
// RFC Section 4.9 - Fixed-Length Opaque Data
// Fixed-length uninterpreted data zero-padded to a multiple of four
//
// RFC Section 4.9 - Fixed-Length Opaque Data
// Fixed-length uninterpreted data zero-padded to a multiple of four
func (d *Decoder) DecodeFixedOpaque(size int32) (rv []byte, err error) {
if size == 0 {
return
Expand Down Expand Up @@ -341,8 +350,9 @@ func (d *Decoder) DecodeFixedOpaque(size int32) (rv []byte, err error) {
// the opaque data is larger than the max length of a Go slice.
//
// Reference:
// RFC Section 4.10 - Variable-Length Opaque Data
// Unsigned integer length followed by fixed opaque data of that length
//
// RFC Section 4.10 - Variable-Length Opaque Data
// Unsigned integer length followed by fixed opaque data of that length
func (d *Decoder) DecodeOpaque() (rv []byte, err error) {
dataLen, err := d.DecodeUint()
if err != nil {
Expand All @@ -365,8 +375,9 @@ func (d *Decoder) DecodeOpaque() (rv []byte, err error) {
// the string data is larger than the max length of a Go slice.
//
// Reference:
// RFC Section 4.11 - String
// Unsigned integer length followed by bytes zero-padded to a multiple of four
//
// RFC Section 4.11 - String
// Unsigned integer length followed by bytes zero-padded to a multiple of four
func (d *Decoder) DecodeString() (rv string, err error) {
dataLen, err := d.DecodeUint()
if err != nil {
Expand Down Expand Up @@ -394,8 +405,9 @@ func (d *Decoder) DecodeString() (rv string, err error) {
// the array elements.
//
// Reference:
// RFC Section 4.12 - Fixed-Length Array
// Individually XDR encoded array elements
//
// RFC Section 4.12 - Fixed-Length Array
// Individually XDR encoded array elements
func (d *Decoder) decodeFixedArray(v reflect.Value, ignoreOpaque bool) (err error) {
// Treat [#]byte (byte is alias for uint8) as opaque data unless ignored.
if !ignoreOpaque && v.Type().Elem().Kind() == reflect.Uint8 {
Expand Down Expand Up @@ -428,8 +440,9 @@ func (d *Decoder) decodeFixedArray(v reflect.Value, ignoreOpaque bool) (err erro
// the array elements.
//
// Reference:
// RFC Section 4.13 - Variable-Length Array
// Unsigned integer length followed by individually XDR encoded array elements
//
// RFC Section 4.13 - Variable-Length Array
// Unsigned integer length followed by individually XDR encoded array elements
func (d *Decoder) decodeArray(v reflect.Value, ignoreOpaque bool) (err error) {
dataLen, err := d.DecodeUint()
if err != nil {
Expand Down Expand Up @@ -479,8 +492,9 @@ func (d *Decoder) decodeArray(v reflect.Value, ignoreOpaque bool) (err error) {
// the elements.
//
// Reference:
// RFC Section 4.14 - Structure
// XDR encoded elements in the order of their declaration in the struct
//
// RFC Section 4.14 - Structure
// XDR encoded elements in the order of their declaration in the struct
func (d *Decoder) decodeStruct(v reflect.Value) (err error) {
vt := v.Type()
for i := 0; i < v.NumField(); i++ {
Expand Down
38 changes: 18 additions & 20 deletions xdr/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ capabilities of Go as described below.
This package provides two approaches for encoding and decoding XDR data:
1) Marshal/Unmarshal functions which automatically map between XDR and Go types
2) Individual Encoder/Decoder objects to manually work with XDR primitives
1. Marshal/Unmarshal functions which automatically map between XDR and Go types
2. Individual Encoder/Decoder objects to manually work with XDR primitives
For the Marshal/Unmarshal functions, Go reflection capabilities are used to choose
the type of the underlying XDR data based upon the Go type to encode or the target
Expand All @@ -45,7 +45,7 @@ encode / decode the XDR data thereby eliminating the need to write a lot of
boilerplate code to encode/decode and error check each piece of XDR data as is
typically required with C based XDR libraries.
Go Type to XDR Type Mappings
# Go Type to XDR Type Mappings
The following chart shows an overview of how Go types are mapped to XDR types
for automatic marshalling and unmarshalling. The documentation for the Marshal
Expand All @@ -72,21 +72,21 @@ and Unmarshal functions has specific details of how the mapping proceeds.
Notes and Limitations:
* Automatic marshalling and unmarshalling of variable and fixed-length arrays
of uint8s require a special struct tag `xdropaque:"false"` since byte
slices and byte arrays are assumed to be opaque data and byte is a Go
alias for uint8 thus indistinguishable under reflection
* Channel, complex, and function types cannot be encoded
* Interfaces without a concrete value cannot be encoded
* Cyclic data structures are not supported and will result in infinite loops
* Strings are marshalled and unmarshalled with UTF-8 character encoding
which differs from the XDR specification of ASCII, however UTF-8 is
backwards compatible with ASCII so this should rarely cause issues
- Automatic marshalling and unmarshalling of variable and fixed-length arrays
of uint8s require a special struct tag `xdropaque:"false"` since byte
slices and byte arrays are assumed to be opaque data and byte is a Go
alias for uint8 thus indistinguishable under reflection
- Channel, complex, and function types cannot be encoded
- Interfaces without a concrete value cannot be encoded
- Cyclic data structures are not supported and will result in infinite loops
- Strings are marshalled and unmarshalled with UTF-8 character encoding
which differs from the XDR specification of ASCII, however UTF-8 is
backwards compatible with ASCII so this should rarely cause issues
Encoding
# Encoding
To encode XDR data, use the Marshal function.
func Marshal(v interface{}) (rv []byte, err error)
For example, given the following code snippet:
Expand All @@ -110,17 +110,16 @@ sequence:
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x0A
In addition, while the automatic marshalling discussed above will work for the
vast majority of cases, an Encoder object is provided that can be used to
manually encode XDR primitives for complex scenarios where automatic
reflection-based encoding won't work. The included examples provide a sample of
manual usage via an Encoder.
Decoding
# Decoding
To decode XDR data, use the Unmarshal function.
func Unmarshal(data []byte, v interface{}) (rest []byte, err error)
For example, given the following code snippet:
Expand Down Expand Up @@ -156,14 +155,13 @@ manually decode XDR primitives for complex scenarios where automatic
reflection-based decoding won't work. The included examples provide a sample of
manual usage via an Decoder.
Errors
# Errors
All errors are either of type UnmarshalError or MarshalError. Both provide
human readable output as well as an ErrorCode field which can be inspected by
sophisticated callers if necessary
See the documentation of UnmarshalError, MarshalError, and ErrorCode for further
details.
*/
package xdr
Loading

0 comments on commit 6c7b684

Please sign in to comment.