The Pointer That Ran Past the Buffer — JavaScript Bug Hunt

Modelled on Cloudbleed (Cloudflare, February 2017): an HTML rewriter checked for the end of its buffer with == instead of >=.

  • Language: JavaScript
  • Layer: Backend
  • Difficulty: Medium
  • Concepts: Memory Safety, Off-by-One
  • Modelled on: Cloudflare · Cloudbleed 2017
  • Visible tests: stops at the terminator; a region without a terminator stops at its own end
  • Reward: 50 XP for a complete fix

Briefing

Modelled on Cloudbleed (Cloudflare, February 2017): an HTML rewriter checked for the end of its buffer with == instead of >=. When a page ended in just the wrong way the pointer stepped past the terminator, and the parser kept reading — spraying adjacent memory, including other customers' cookies and private messages, into public responses.

scanner.js walks a buffer looking for a terminator byte and returns everything before it. Its end check has the same flaw.

Fix scan so it can never read past the end of the buffer.

Bug report

BUG-CB17 · Priority: Critical (data leak) · Reported by: security

scan(arena, length, terminator):

  • arena is a shared backing store; this request owns only its first length bytes, and everything after that belongs to other requests
  • returns the owned bytes before the first terminator
  • if the terminator is absent within the owned region, returns all length of them

Observed: the loop bounds itself on the ARENA size rather than on length, so a request whose region has no terminator keeps reading into the next request's bytes and returns them.

Logs

[rewriter] scan overran: length=3 returned=6
[rewriter] response contained bytes from an unrelated request

The code as shipped

src/parser/scanner.js (editable)

// Reads owned bytes up to (but not including) the terminator.
exports.scan = function (arena, length, terminator) {
  var out = [];
  var p = 0;
  // Stop when we reach the end of the backing store.
  while (p < arena.length) {
    if (arena[p] === terminator) return out;
    out.push(arena[p]);
    p++;
  }
  return out;
};

Read-only context: src/parser/README.js.

Open the hunt to edit the files, run the visible tests and submit against the hidden ones. More JavaScript bug hunts.