diff --git a/go/lib.go b/go/lib.go index a4e99de101..1bb1a337da 100644 --- a/go/lib.go +++ b/go/lib.go @@ -1,5 +1,11 @@ package flatbuffers +import "errors" + +// ErrBufferTooShort is returned when the buffer is too short to read the +// root table offset (UOffsetT). +var ErrBufferTooShort = errors.New("flatbuffers: buffer too short") + // FlatBuffer is the interface that represents a flatbuffer. type FlatBuffer interface { Table() Table @@ -8,6 +14,9 @@ type FlatBuffer interface { // GetRootAs is a generic helper to initialize a FlatBuffer with the provided buffer bytes and its data offset. func GetRootAs(buf []byte, offset UOffsetT, fb FlatBuffer) { + if int(offset)+SizeUOffsetT > len(buf) { + return + } n := GetUOffsetT(buf[offset:]) fb.Init(buf, n+offset) } @@ -15,27 +24,42 @@ func GetRootAs(buf []byte, offset UOffsetT, fb FlatBuffer) { // GetSizePrefixedRootAs is a generic helper to initialize a FlatBuffer with the provided size-prefixed buffer // bytes and its data offset func GetSizePrefixedRootAs(buf []byte, offset UOffsetT, fb FlatBuffer) { + if int(offset)+sizePrefixLength+SizeUOffsetT > len(buf) { + return + } n := GetUOffsetT(buf[offset+sizePrefixLength:]) fb.Init(buf, n+offset+sizePrefixLength) } // GetSizePrefix reads the size from a size-prefixed flatbuffer func GetSizePrefix(buf []byte, offset UOffsetT) uint32 { + if int(offset)+SizeUOffsetT > len(buf) { + return 0 + } return GetUint32(buf[offset:]) } // GetIndirectOffset retrives the relative offset in the provided buffer stored at `offset`. func GetIndirectOffset(buf []byte, offset UOffsetT) UOffsetT { + if int(offset)+SizeUOffsetT > len(buf) { + return 0 + } return offset + GetUOffsetT(buf[offset:]) } // GetBufferIdentifier returns the file identifier as string func GetBufferIdentifier(buf []byte) string { + if len(buf) < SizeUOffsetT+fileIdentifierLength { + return "" + } return string(buf[SizeUOffsetT:][:fileIdentifierLength]) } // GetBufferIdentifier returns the file identifier as string for a size-prefixed buffer func GetSizePrefixedBufferIdentifier(buf []byte) string { + if len(buf) < SizeUOffsetT+int(sizePrefixLength)+fileIdentifierLength { + return "" + } return string(buf[SizeUOffsetT+sizePrefixLength:][:fileIdentifierLength]) } diff --git a/go/lib_test.go b/go/lib_test.go new file mode 100644 index 0000000000..b5abdc0a4d --- /dev/null +++ b/go/lib_test.go @@ -0,0 +1,118 @@ +package flatbuffers + +import "testing" + +// TestGetRootAsShortBuffer verifies that GetRootAs does not panic when +// given a buffer shorter than SizeUOffsetT (4 bytes). +func TestGetRootAsShortBuffer(t *testing.T) { + shortBuffers := [][]byte{ + nil, + {}, + {0x01}, + {0x01, 0x02}, + {0x01, 0x02, 0x03}, + } + for _, buf := range shortBuffers { + // Must not panic + tab := &Table{} + GetRootAs(buf, 0, tab) + } +} + +// TestGetSizePrefixedRootAsShortBuffer verifies that GetSizePrefixedRootAs +// does not panic when given a buffer shorter than the required minimum. +func TestGetSizePrefixedRootAsShortBuffer(t *testing.T) { + shortBuffers := [][]byte{ + nil, + {}, + {0x01, 0x02, 0x03}, + {0x01, 0x02, 0x03, 0x04}, // 4 bytes: only size prefix, no root offset + {0x01, 0x02, 0x03, 0x04, 0x05}, // 5 bytes: still too short for prefix + UOffsetT + {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, // 7 bytes: still too short + } + for _, buf := range shortBuffers { + // Must not panic + tab := &Table{} + GetSizePrefixedRootAs(buf, 0, tab) + } +} + +// TestGetSizePrefixShortBuffer verifies GetSizePrefix doesn't panic on +// buffers shorter than 4 bytes. +func TestGetSizePrefixShortBuffer(t *testing.T) { + shortBuffers := [][]byte{ + nil, + {}, + {0x01}, + {0x01, 0x02, 0x03}, + } + for _, buf := range shortBuffers { + result := GetSizePrefix(buf, 0) + if result != 0 { + t.Errorf("GetSizePrefix on short buffer should return 0, got %d", result) + } + } +} + +// TestGetIndirectOffsetShortBuffer verifies GetIndirectOffset doesn't panic +// on buffers shorter than 4 bytes. +func TestGetIndirectOffsetShortBuffer(t *testing.T) { + shortBuffers := [][]byte{ + nil, + {}, + {0x01, 0x02, 0x03}, + } + for _, buf := range shortBuffers { + result := GetIndirectOffset(buf, 0) + if result != 0 { + t.Errorf("GetIndirectOffset on short buffer should return 0, got %d", result) + } + } +} + +// TestGetBufferIdentifierShortBuffer verifies GetBufferIdentifier doesn't +// panic on buffers shorter than SizeUOffsetT + fileIdentifierLength. +func TestGetBufferIdentifierShortBuffer(t *testing.T) { + shortBuffers := [][]byte{ + nil, + {}, + {0x01, 0x02, 0x03}, + {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, // 7 bytes: need at least 8 + } + for _, buf := range shortBuffers { + result := GetBufferIdentifier(buf) + if result != "" { + t.Errorf("GetBufferIdentifier on short buffer should return empty, got %q", result) + } + } +} + +// TestGetRootAsValidBuffer ensures that GetRootAs still works correctly +// for valid buffers. +func TestGetRootAsValidBuffer(t *testing.T) { + // Create a minimal valid buffer: a 4-byte UOffsetT pointing to offset 4 + buf := []byte{0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + tab := &Table{} + GetRootAs(buf, 0, tab) + if tab.Pos != 4 { + t.Errorf("Expected tab.Pos to be 4, got %d", tab.Pos) + } +} + +// TestGetRootAsWithOffset verifies bounds checking works with non-zero offset. +func TestGetRootAsWithOffset(t *testing.T) { + // Buffer with offset=2 needs at least 6 bytes (offset + SizeUOffsetT) + buf := []byte{0x00, 0x00, 0x04, 0x00, 0x00, 0x00} + tab := &Table{} + GetRootAs(buf, 2, tab) + if tab.Pos != 6 { + t.Errorf("Expected tab.Pos to be 6, got %d", tab.Pos) + } + + // Same buffer but with offset=3 should silently fail (only 3 bytes remain) + tab2 := &Table{} + GetRootAs(buf, 3, tab2) + if tab2.Pos != 0 { + t.Errorf("Expected tab.Pos to be 0 for short buffer, got %d", tab2.Pos) + } +} diff --git a/src/idl_gen_go.cpp b/src/idl_gen_go.cpp index a4a734588c..70cecad697 100644 --- a/src/idl_gen_go.cpp +++ b/src/idl_gen_go.cpp @@ -328,8 +328,17 @@ class GoGenerator : public BaseGenerator { code += "*" + struct_type + ""; code += " {\n"; if (i == 0) { + code += + "\tif int(offset)+flatbuffers.SizeUOffsetT > len(buf) {\n" + "\t\treturn nil\n" + "\t}\n"; code += "\tn := flatbuffers.GetUOffsetT(buf[offset:])\n"; } else { + code += + "\tif int(offset)+flatbuffers.SizeUint32+" + "flatbuffers.SizeUOffsetT > len(buf) {\n" + "\t\treturn nil\n" + "\t}\n"; code += "\tn := " "flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])\n";