Skip to content

fix: encodeVarint silently truncates values >= 2^32 #3

Description

@iRonin

Problem

encodeVarint() in worker/cursor.ts uses value >>> 0 to clamp the input to a 32-bit unsigned integer:

function encodeVarint(value: number): Uint8Array {
  let current = value >>> 0;
  // ...
}

If a protobuf tag (fieldNumber << 3) | wireType ever reaches or exceeds 2³², the value silently truncates, corrupting the encoded message.

Currently all field numbers are < 256, so this is not exploitable today. But it is a correctness bug in the encoding layer — the function signature implies it handles arbitrary non-negative integers.

Location

worker/cursor.tsencodeVarint() (near end of file)

Proposed Fix

Add a range check that throws if the value exceeds the safe encoding limit for JavaScript numbers, or switch to bigint-based encoding for full correctness. A pragmatic fix:

function encodeVarint(value: number): Uint8Array {
  if (value < 0 || !Number.isInteger(value)) {
    throw new Error(`encodeVarint: expected non-negative integer, got ${value}`);
  }
  if (value >= 2 ** 32) {
    throw new Error(`encodeVarint: value ${value} exceeds 32-bit limit`);
  }
  // ... rest unchanged
}

This is explicit about the limit rather than silently corrupting data. A future refactor could use bigint for arbitrary-length varints if needed.

Impact

  • No API change
  • No behavioral change for current field numbers (all < 256)
  • Converts silent data corruption into a fail-loud error

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions