Skip to content

Commit

Permalink
nbt/encoding.go: Return correct error for varint not ending after 5/1…
Browse files Browse the repository at this point in the history
…0 bytes.
  • Loading branch information
Sandertv committed Jul 28, 2023
1 parent e7e88ba commit 381bd5b
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 37 deletions.
64 changes: 37 additions & 27 deletions minecraft/nbt/encoding.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package nbt

import (
"errors"
"math"
"unsafe"
)
Expand Down Expand Up @@ -46,6 +45,8 @@ var (
_ Encoding = BigEndian
)

const maxStringSize = math.MaxInt16

type networkLittleEndian struct{ littleEndian }

// WriteInt32 ...
Expand Down Expand Up @@ -86,8 +87,8 @@ func (networkLittleEndian) WriteInt64(w *offsetWriter, x int64) error {

// WriteString ...
func (networkLittleEndian) WriteString(w *offsetWriter, x string) error {
if len(x) > math.MaxUint16 {
return InvalidStringError{Off: w.off, N: uint(len(x)), Err: errors.New("string length exceeds maximum length prefix")}
if len(x) > maxStringSize {
return InvalidStringError{Off: w.off, N: uint(len(x)), Err: errStringTooLong}
}
ux := uint32(len(x))
for ux >= 0x80 {
Expand Down Expand Up @@ -116,14 +117,14 @@ func (networkLittleEndian) Int32(r *offsetReader) (int32, error) {
}
ux |= uint32(b&0x7f) << i
if b&0x80 == 0 {
break
x := int32(ux >> 1)
if ux&1 != 0 {
x = ^x
}
return x, nil
}
}
x := int32(ux >> 1)
if ux&1 != 0 {
x = ^x
}
return x, nil
return 0, InvalidVarintError{N: 5, Off: r.off}
}

// Int64 ...
Expand All @@ -136,31 +137,24 @@ func (networkLittleEndian) Int64(r *offsetReader) (int64, error) {
}
ux |= uint64(b&0x7f) << i
if b&0x80 == 0 {
break
x := int64(ux >> 1)
if ux&1 != 0 {
x = ^x
}
return x, nil
}
}
x := int64(ux >> 1)
if ux&1 != 0 {
x = ^x
}
return x, nil
return 0, InvalidVarintError{N: 10, Off: r.off}
}

// String ...
func (e networkLittleEndian) String(r *offsetReader) (string, error) {
var length uint32
for i := uint(0); i < 35; i += 7 {
b, err := r.ReadByte()
if err != nil {
return "", BufferOverrunError{Op: "String"}
}
length |= uint32(b&0x7f) << i
if b&0x80 == 0 {
break
}
length, err := e.stringLength(r)
if err != nil {
return "", err
}
if length > math.MaxUint16 {
return "", InvalidStringError{N: uint(length), Off: r.off, Err: errors.New("string length exceeds maximum length prefix")}
if length > maxStringSize {
return "", InvalidStringError{N: uint(length), Off: r.off, Err: errStringTooLong}
}
data := make([]byte, length)
if _, err := r.Read(data); err != nil {
Expand All @@ -169,6 +163,22 @@ func (e networkLittleEndian) String(r *offsetReader) (string, error) {
return *(*string)(unsafe.Pointer(&data)), nil
}

// stringLength reads the length of a string as a varuint32.
func (networkLittleEndian) stringLength(r *offsetReader) (uint32, error) {
var ux uint32
for i := uint(0); i < 35; i += 7 {
b, err := r.ReadByte()
if err != nil {
return 0, BufferOverrunError{Op: "StringLength"}
}
ux |= uint32(b&0x7f) << i
if b&0x80 == 0 {
return ux, nil
}
}
return 0, InvalidVarintError{N: 5, Off: r.off}
}

// Int32Slice ...
func (e networkLittleEndian) Int32Slice(r *offsetReader) ([]int32, error) {
n, err := e.Int32(r)
Expand Down
9 changes: 4 additions & 5 deletions minecraft/nbt/encoding_big_endian.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ package nbt

import (
"encoding/binary"
"errors"
"math"
"unsafe"
)
Expand Down Expand Up @@ -58,8 +57,8 @@ func (bigEndian) WriteFloat64(w *offsetWriter, x float64) error {

// WriteString ...
func (e bigEndian) WriteString(w *offsetWriter, x string) error {
if len(x) > math.MaxUint16 {
return InvalidStringError{Off: w.off, N: uint(len(x)), Err: errors.New("string length exceeds maximum length prefix")}
if len(x) > maxStringSize {
return InvalidStringError{Off: w.off, N: uint(len(x)), Err: errStringTooLong}
}
if err := e.WriteInt16(w, int16(uint16(len(x)))); err != nil {
return FailedWriteError{Op: "WriteString", Off: w.off}
Expand Down Expand Up @@ -216,8 +215,8 @@ func (littleEndian) WriteFloat64(w *offsetWriter, x float64) error {

// WriteString ...
func (e littleEndian) WriteString(w *offsetWriter, x string) error {
if len(x) > math.MaxUint16 {
return InvalidStringError{Off: w.off, N: uint(len(x)), Err: errors.New("string length exceeds maximum length prefix")}
if len(x) > maxStringSize {
return InvalidStringError{Off: w.off, N: uint(len(x)), Err: errStringTooLong}
}
if err := e.WriteInt16(w, int16(uint16(len(x)))); err != nil {
return FailedWriteError{Op: "WriteString", Off: w.off}
Expand Down
9 changes: 4 additions & 5 deletions minecraft/nbt/encoding_little_endian.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ package nbt

import (
"encoding/binary"
"errors"
"math"
"unsafe"
)
Expand Down Expand Up @@ -58,8 +57,8 @@ func (littleEndian) WriteFloat64(w *offsetWriter, x float64) error {

// WriteString ...
func (e littleEndian) WriteString(w *offsetWriter, x string) error {
if len(x) > math.MaxUint16 {
return InvalidStringError{Off: w.off, N: uint(len(x)), Err: errors.New("string length exceeds maximum length prefix")}
if len(x) > maxStringSize {
return InvalidStringError{Off: w.off, N: uint(len(x)), Err: errStringTooLong}
}
if err := e.WriteInt16(w, int16(uint16(len(x)))); err != nil {
return FailedWriteError{Op: "WriteString", Off: w.off}
Expand Down Expand Up @@ -216,8 +215,8 @@ func (bigEndian) WriteFloat64(w *offsetWriter, x float64) error {

// WriteString ...
func (e bigEndian) WriteString(w *offsetWriter, x string) error {
if len(x) > math.MaxUint16 {
return InvalidStringError{Off: w.off, N: uint(len(x)), Err: errors.New("string length exceeds maximum length prefix")}
if len(x) > maxStringSize {
return InvalidStringError{Off: w.off, N: uint(len(x)), Err: errStringTooLong}
}
if err := e.WriteInt16(w, int16(uint16(len(x)))); err != nil {
return FailedWriteError{Op: "WriteString", Off: w.off}
Expand Down
15 changes: 15 additions & 0 deletions minecraft/nbt/err.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package nbt

import (
"errors"
"fmt"
"reflect"
)
Expand Down Expand Up @@ -117,6 +118,8 @@ func (err IncompatibleTypeError) Error() string {
return fmt.Sprintf("nbt: value type %v (%v) cannot be translated to an NBT tag", err.Type, err.ValueName)
}

var errStringTooLong = errors.New("string length exceeds maximum length")

// InvalidStringError is returned if a string read is not valid, meaning it does not exist exclusively out of
// utf8 characters, or if it is longer than the length prefix can carry.
type InvalidStringError struct {
Expand Down Expand Up @@ -153,3 +156,15 @@ type MaximumBytesReadError struct {
func (err MaximumBytesReadError) Error() string {
return fmt.Sprintf("nbt: limit of bytes read %v with NetworkLittleEndian format exhausted", maximumNetworkOffset)
}

// InvalidVarintError is returned if a varint(32/64) is encountered that does
// not end after 5 or 10 bytes respectively.
type InvalidVarintError struct {
Off int64
N int
}

// Error ...
func (err InvalidVarintError) Error() string {
return fmt.Sprintf("nbt: varint did not terminate after %v bytes at offset %v", err.N, err.Off)
}

0 comments on commit 381bd5b

Please sign in to comment.