Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions go/lib.go
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -8,34 +14,52 @@ 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)
}

// 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])
}

Expand Down
118 changes: 118 additions & 0 deletions go/lib_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
9 changes: 9 additions & 0 deletions src/idl_gen_go.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down