or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

cpp-core.mdflexbuffers.mdgo.mdindex.mdjava.mdjavascript.mdpython.mdrust.mdschema-compiler.mdverification.md
tile.json

verification.mddocs/

Buffer Verification

Optional verification system for validating FlatBuffer integrity, detecting corruption, and ensuring safe access to potentially untrusted data. The verification system provides configurable security checks and detailed error reporting.

Capabilities

Verifier Class

The main verification class that validates FlatBuffer structure and content according to configurable security policies.

template<bool TrackVerifierBufferSize = false>
class VerifierTemplate {
public:
    /**
     * Verification options structure
     */
    struct Options {
        /** Maximum nesting depth allowed (default: 64) */
        uoffset_t max_depth = 64;
        
        /** Maximum number of tables allowed (default: 1000000) */
        uoffset_t max_tables = 1000000;
        
        /** Whether to check data alignment (default: true) */
        bool check_alignment = true;
        
        /** Whether to verify nested flatbuffers (default: true) */
        bool check_nested_flatbuffers = true;
        
        /** Maximum buffer size allowed (default: FLATBUFFERS_MAX_BUFFER_SIZE) */
        size_t max_size = FLATBUFFERS_MAX_BUFFER_SIZE;
        
        /** Use assertions for errors instead of returning false (default: false) */
        bool assert = false;
    };
    
    /**
     * Constructor for buffer verification
     * @param buf Pointer to buffer data
     * @param buf_len Buffer length in bytes
     * @param opts Verification options
     */
    VerifierTemplate(const uint8_t *buf, size_t buf_len, const Options &opts = {});
    
    /**
     * Central verification checkpoint - all checks go through this
     * @param ok Boolean result of specific check
     * @return True if check passed, false otherwise
     */
    bool Check(const bool ok) const;
    
    /**
     * Verify memory range is within buffer bounds
     * @param elem Starting position
     * @param elem_len Length of data to verify
     * @return True if range is valid
     */
    bool Verify(const size_t elem, const size_t elem_len) const;
    
    /**
     * Verify data alignment requirements
     * @param elem Data position
     * @param align Required alignment (power of 2)
     * @return True if properly aligned
     */
    bool VerifyAlignment(const size_t elem, const size_t align) const;
    
    /**
     * Verify offset points within buffer
     * @param start Starting position
     * @param offset Offset value to check
     * @return True if offset is valid
     */
    bool VerifyOffset(const size_t start, const uoffset_t offset) const;
    
    /**
     * Verify 64-bit offset points within buffer
     * @param start Starting position
     * @param offset 64-bit offset value to check
     * @return True if offset is valid
     */
    bool VerifyOffset64(const size_t start, const uoffset64_t offset) const;
    
    /**
     * Verify table structure and vtable
     * @param table Pointer to table
     * @return True if table is valid
     */
    template<typename T>
    bool VerifyTable(const T *table);
    
    /**
     * Verify table with custom vtable size
     * @param table Pointer to table
     * @param vtable_size Expected vtable size
     * @return True if table is valid
     */
    bool VerifyTableStart(const uint8_t *table, const voffset_t vtable_size);
    
    /**
     * Verify vector structure and elements
     * @param vec Pointer to vector
     * @return True if vector is valid
     */
    template<typename T>
    bool VerifyVector(const Vector<T> *vec) const;
    
    /**
     * Verify vector with element count
     * @param vec Pointer to vector
     * @param elem_size Size of each element
     * @return True if vector is valid
     */
    bool VerifyVectorOrString(const uint8_t *vec, const size_t elem_size) const;
    
    /**
     * Verify string structure and UTF-8 content
     * @param str Pointer to string
     * @return True if string is valid
     */
    bool VerifyString(const String *str) const;
    
    /**
     * Verify buffer contains valid root table
     * @param identifier Optional file identifier to check
     * @return True if buffer has valid root
     */
    template<typename T>
    bool VerifyBuffer(const char *identifier = nullptr);
    
    /**
     * Verify size-prefixed buffer
     * @param identifier Optional file identifier to check
     * @return True if size-prefixed buffer is valid
     */
    template<typename T>
    bool VerifySizePrefixedBuffer(const char *identifier = nullptr);
    
    /**
     * Get number of tables verified (if tracking enabled)
     * @return Table count
     */
    uoffset_t GetComputedSize() const;
};

// Standard verifier type alias
using Verifier = VerifierTemplate<false>;

Basic Usage Example:

#include "flatbuffers/flatbuffers.h"
#include "monster_generated.h" // Generated from schema

bool VerifyMonsterBuffer(const uint8_t *buf, size_t size) {
    // Create verifier with default options
    flatbuffers::Verifier verifier(buf, size);
    
    // Verify the buffer contains a valid Monster
    return verifier.VerifyBuffer<Monster>(nullptr);
}

// With custom options
bool VerifyMonsterBufferStrict(const uint8_t *buf, size_t size) {
    flatbuffers::Verifier::Options opts;
    opts.max_depth = 32;           // Stricter depth limit
    opts.max_tables = 10000;       // Fewer tables allowed
    opts.check_alignment = true;   // Strict alignment checking
    opts.assert = false;           // Don't use assertions
    
    flatbuffers::Verifier verifier(buf, size, opts);
    return verifier.VerifyBuffer<Monster>("MONS"); // With file identifier
}

Verification Options

Detailed configuration options for customizing verification behavior based on security requirements.

struct VerifierOptions {
    /**
     * Maximum nesting depth for tables and vectors
     * Prevents stack overflow attacks via deeply nested structures
     * Default: 64, Range: 1-1000
     */
    uoffset_t max_depth = 64;
    
    /**
     * Maximum number of tables that can be traversed
     * Prevents DOS attacks via excessive table references  
     * Default: 1000000, Range: 1-UINT32_MAX
     */
    uoffset_t max_tables = 1000000;
    
    /**
     * Whether to verify proper data alignment
     * Ensures data is aligned according to type requirements
     * Default: true
     */
    bool check_alignment = true;
    
    /**
     * Whether to recursively verify nested FlatBuffers
     * Verifies buffers stored as byte vectors
     * Default: true
     */
    bool check_nested_flatbuffers = true;
    
    /**
     * Maximum total buffer size allowed
     * Prevents excessive memory usage
     * Default: FLATBUFFERS_MAX_BUFFER_SIZE (2GB-1)
     */
    size_t max_size = FLATBUFFERS_MAX_BUFFER_SIZE;
    
    /**
     * Use assertions instead of returning false
     * When true, failed checks trigger assertions (debug builds)
     * Default: false (return false on failure)
     */
    bool assert = false;
};

Common Verification Patterns

Typical patterns for integrating verification into applications with various security requirements.

// Network data verification (strict security)
bool VerifyNetworkData(const uint8_t *data, size_t len) {
    flatbuffers::Verifier::Options opts;
    opts.max_depth = 16;        // Shallow nesting only
    opts.max_tables = 1000;     // Limited table count
    opts.check_alignment = true; // Strict alignment
    opts.max_size = 1024 * 1024; // 1MB limit
    
    flatbuffers::Verifier verifier(data, len, opts);
    return verifier.VerifyBuffer<NetworkMessage>("NETM");
}

// File data verification (moderate security)
bool VerifyFileData(const uint8_t *data, size_t len) {
    flatbuffers::Verifier::Options opts;
    opts.max_depth = 64;        // Default depth
    opts.max_tables = 100000;   // Reasonable limit
    opts.check_alignment = true; // Check alignment
    opts.max_size = 100 * 1024 * 1024; // 100MB limit
    
    flatbuffers::Verifier verifier(data, len, opts);
    return verifier.VerifyBuffer<FileData>("FILE");
}

// Trusted data verification (basic checks)
bool VerifyTrustedData(const uint8_t *data, size_t len) {
    flatbuffers::Verifier::Options opts;
    opts.max_depth = 128;       // Allow deeper nesting
    opts.max_tables = 1000000;  // Default limit
    opts.check_alignment = false; // Skip alignment checks
    
    flatbuffers::Verifier verifier(data, len, opts);
    return verifier.VerifyBuffer<TrustedData>();
}

// Size-prefixed buffer verification
bool VerifySizePrefixed(const uint8_t *data, size_t len) {
    flatbuffers::Verifier verifier(data, len);
    return verifier.VerifySizePrefixedBuffer<RootTable>("SPFX");
}

Language-Specific Verification

Verification APIs adapted for different programming languages while maintaining consistent security guarantees.

JavaScript/TypeScript:

import { ByteBuffer } from 'flatbuffers';

interface VerificationOptions {
  maxDepth?: number;
  maxTables?: number; 
  checkAlignment?: boolean;
  maxSize?: number;
}

function verifyBuffer(
  buffer: Uint8Array, 
  options: VerificationOptions = {}
): boolean {
  // Basic JavaScript verification implementation
  if (buffer.length < 4) return false;
  
  const bb = new ByteBuffer(buffer);
  const rootOffset = bb.readInt32(0);
  
  // Additional checks based on options
  return rootOffset > 0 && rootOffset < buffer.length;
}

Python:

import flatbuffers

class VerificationOptions:
    def __init__(self, max_depth=64, max_tables=1000000, 
                 check_alignment=True, max_size=2**31-1):
        self.max_depth = max_depth
        self.max_tables = max_tables
        self.check_alignment = check_alignment
        self.max_size = max_size

def verify_buffer(buffer: bytes, options: VerificationOptions = None) -> bool:
    """
    Verify FlatBuffer integrity
    
    Args:
        buffer: Buffer to verify
        options: Verification options
        
    Returns:
        bool: True if buffer is valid
    """
    if options is None:
        options = VerificationOptions()
        
    # Basic validation
    if len(buffer) < 4 or len(buffer) > options.max_size:
        return False
        
    try:
        # Check root offset
        root_offset = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buffer, 0)
        return 0 < root_offset < len(buffer)
    except (struct.error, IndexError):
        return False

Java:

import com.google.flatbuffers.*;
import java.nio.ByteBuffer;

public class VerificationOptions {
    public int maxDepth = 64;
    public int maxTables = 1000000;
    public boolean checkAlignment = true;
    public long maxSize = Integer.MAX_VALUE;
}

public static boolean verifyBuffer(ByteBuffer buffer, VerificationOptions options) {
    if (options == null) {
        options = new VerificationOptions();
    }
    
    // Basic validation
    if (buffer.remaining() < 4 || buffer.remaining() > options.maxSize) {
        return false;
    }
    
    try {
        buffer.order(ByteOrder.LITTLE_ENDIAN);
        int rootOffset = buffer.getInt(buffer.position());
        return rootOffset > 0 && rootOffset < buffer.remaining();
    } catch (Exception e) {
        return false;
    }
}

Go:

package flatbuffers

type VerificationOptions struct {
    MaxDepth       int
    MaxTables      int
    CheckAlignment bool
    MaxSize        int64
}

func DefaultVerificationOptions() *VerificationOptions {
    return &VerificationOptions{
        MaxDepth:       64,
        MaxTables:      1000000,
        CheckAlignment: true,
        MaxSize:        2147483647, // 2GB-1
    }
}

func VerifyBuffer(buf []byte, opts *VerificationOptions) bool {
    if opts == nil {
        opts = DefaultVerificationOptions()
    }
    
    // Basic validation
    if len(buf) < 4 || int64(len(buf)) > opts.MaxSize {
        return false
    }
    
    // Check root offset
    rootOffset := GetUOffsetT(buf[0:4])
    return rootOffset > 0 && int(rootOffset) < len(buf)
}

Rust:

use flatbuffers::{InvalidFlatbuffer, Verifiable, Verifier, VerifierOptions};

#[derive(Debug, Clone)]
pub struct CustomVerifierOptions {
    pub max_depth: usize,
    pub max_tables: usize,
    pub check_alignment: bool,
    pub max_size: usize,
}

impl Default for CustomVerifierOptions {
    fn default() -> Self {
        Self {
            max_depth: 64,
            max_tables: 1_000_000,
            check_alignment: true,
            max_size: i32::MAX as usize,
        }
    }
}

pub fn verify_buffer<T: Verifiable>(
    buf: &[u8], 
    opts: Option<CustomVerifierOptions>
) -> Result<(), InvalidFlatbuffer> {
    let opts = opts.unwrap_or_default();
    
    // Basic validation
    if buf.len() < 4 || buf.len() > opts.max_size {
        return Err(InvalidFlatbuffer::BufferTooSmall);
    }
    
    // Create verifier with custom options
    let verifier_opts = VerifierOptions {
        max_depth: opts.max_depth,
        max_tables: opts.max_tables,
        check_alignment: opts.check_alignment,
        ..Default::default()
    };
    
    let mut verifier = Verifier::new(&verifier_opts, buf);
    T::run_verifier(&mut verifier, 0)
}

Error Handling and Diagnostics

Comprehensive error reporting for verification failures with actionable diagnostic information.

// Enhanced verification with detailed error reporting
class DetailedVerifier : public flatbuffers::Verifier {
public:
    struct VerificationError {
        enum Type {
            BUFFER_TOO_SMALL,
            BUFFER_TOO_LARGE, 
            INVALID_OFFSET,
            ALIGNMENT_ERROR,
            DEPTH_EXCEEDED,
            TABLE_LIMIT_EXCEEDED,
            INVALID_VTABLE,
            INVALID_STRING,
            INVALID_VECTOR,
            NESTED_BUFFER_ERROR
        };
        
        Type type;
        size_t position;
        std::string description;
        
        VerificationError(Type t, size_t pos, const std::string& desc)
            : type(t), position(pos), description(desc) {}
    };
    
private:
    mutable std::vector<VerificationError> errors_;
    
public:
    DetailedVerifier(const uint8_t *buf, size_t buf_len, const Options &opts = {})
        : flatbuffers::Verifier(buf, buf_len, opts) {}
    
    bool Check(const bool ok) const override {
        if (!ok && errors_.empty()) {
            // Record the first error encountered
            errors_.emplace_back(
                VerificationError::BUFFER_TOO_SMALL, 
                0, 
                "Verification check failed"
            );
        }
        return flatbuffers::Verifier::Check(ok);
    }
    
    const std::vector<VerificationError>& GetErrors() const {
        return errors_;
    }
    
    std::string GetErrorReport() const {
        std::string report = "Verification Errors:\n";
        for (const auto& error : errors_) {
            report += "  - " + error.description + 
                     " at position " + std::to_string(error.position) + "\n";
        }
        return report;
    }
};

// Usage with detailed error reporting
bool VerifyWithDiagnostics(const uint8_t *buf, size_t size) {
    DetailedVerifier verifier(buf, size);
    bool result = verifier.VerifyBuffer<Monster>();
    
    if (!result) {
        std::cout << verifier.GetErrorReport() << std::endl;
        for (const auto& error : verifier.GetErrors()) {
            std::cout << "Error type: " << error.type 
                     << " at position: " << error.position << std::endl;
        }
    }
    
    return result;
}

Complete Verification Example:

#include "flatbuffers/flatbuffers.h"
#include "monster_generated.h"
#include <iostream>
#include <fstream>
#include <vector>

// Comprehensive buffer verification function
bool VerifyMonsterFile(const std::string& filename) {
    // Read file
    std::ifstream file(filename, std::ios::binary);
    if (!file) {
        std::cerr << "Cannot open file: " << filename << std::endl;
        return false;
    }
    
    // Get file size
    file.seekg(0, std::ios::end);
    size_t size = file.tellg();
    file.seekg(0, std::ios::beg);
    
    // Read data
    std::vector<uint8_t> buffer(size);
    file.read(reinterpret_cast<char*>(buffer.data()), size);
    
    // Configure verification options
    flatbuffers::Verifier::Options opts;
    opts.max_depth = 32;           // Moderate depth limit
    opts.max_tables = 10000;       // Reasonable table limit  
    opts.check_alignment = true;   // Verify alignment
    opts.max_size = 10 * 1024 * 1024; // 10MB limit
    
    // Verify buffer
    flatbuffers::Verifier verifier(buffer.data(), size, opts);
    bool isValid = verifier.VerifyBuffer<Monster>("MONS");
    
    if (isValid) {
        std::cout << "Buffer verification passed" << std::endl;
        
        // Safe to access data
        const Monster* monster = GetMonster(buffer.data());
        std::cout << "Monster name: " << monster->name()->c_str() << std::endl;
        std::cout << "Monster HP: " << monster->hp() << std::endl;
    } else {
        std::cerr << "Buffer verification failed!" << std::endl;
    }
    
    return isValid;
}

int main() {
    return VerifyMonsterFile("monster.bin") ? 0 : 1;
}