Skip to main content
Sigvex

Deserialization Endianness

Detects potential endianness mismatches in data deserialization.

Deserialization Endianness

Overview

Remediation Guide: How to Fix Deserialization Endianness

The deserialization endianness detector identifies potential byte-order mismatches when deserializing multi-byte values from account data. Solana programs typically use little-endian byte order (via Borsh serialization), but programs that process data from external sources (oracles, bridges, network protocols) or perform manual byte manipulation may introduce endianness bugs.

Sigvex detects this by identifying multi-byte loads from account data followed by bit-shift operations at byte boundaries (shifts by 8, 16, or 24) without an explicit byte-swap operation, suggesting manual endianness handling that may not match the serialization format.

Why This Is an Issue

  • Data corruption: interpreting a big-endian value as little-endian (or vice versa) produces a completely incorrect number, potentially orders of magnitude off.
  • Financial impact: incorrect byte order on balance or price fields can lead to transfers of wrong amounts.
  • Cross-chain interop: bridges and oracles may use big-endian (network byte order), conflicting with Solana’s native little-endian.

CWE mapping: CWE-198 (Use of Incorrect Byte Ordering).

How to Resolve

Native Solana

// Use explicit endianness conversion
let data = account.data.borrow();

// For Solana-native data (little-endian):
let value = u32::from_le_bytes(data[offset..offset + 4].try_into()?);

// For external data (big-endian/network byte order):
let value = u32::from_be_bytes(data[offset..offset + 4].try_into()?);

// Or use Borsh for consistent serialization:
let state = MyStruct::try_from_slice(&data)?;

Anchor

// Anchor uses Borsh (little-endian) consistently
#[account]
pub struct PriceData {
    pub price: u64,    // Always little-endian via Borsh
    pub decimals: u8,
}

Examples

Vulnerable

let data = account.data.borrow();
let raw = u32::from_le_bytes(data[0..4].try_into()?);
// Manual byte extraction — may be wrong endianness
let byte0 = (raw >> 24) & 0xFF; // Big-endian extraction on little-endian data!

Fixed

let data = account.data.borrow();
// If data source is big-endian:
let value = u32::from_be_bytes(data[0..4].try_into()?);
// If data source is little-endian (Borsh):
let value = u32::from_le_bytes(data[0..4].try_into()?);

JSON Finding

{
  "detector": "deserialization-endianness",
  "severity": "Medium",
  "confidence": 0.60,
  "title": "Potential Endianness Mismatch in Deserialization",
  "description": "Multi-byte value used in byte manipulation without explicit endianness conversion.",
  "cwe": [198]
}

Detection Methodology

The detector tracks multi-byte loads from account data and byte-swap operations. When a loaded value is used in shift/mask operations at byte-aligned amounts (8, 16, 24) without a prior byte-swap or explicit from_le/from_be conversion, a finding is generated. Single-byte loads are excluded since they have no endianness concerns.

Limitations

  • The detector relies on shift amounts to identify suspicious patterns and may miss endianness issues in arithmetic expressions.
  • Programs that correctly use little-endian throughout may still be flagged if they perform byte extraction for non-endianness reasons.
  • Borsh deserialization in Anchor programs eliminates most endianness concerns, reducing confidence significantly.

References