Skip to content

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

@iRonin

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions