Skip to content

fix(v1): preserve caller-owned random buffers - #980

Open
sergioperezcheco wants to merge 4 commits into
uuidjs:mainfrom
sergioperezcheco:fix/preserve-random-buffer
Open

sergioperezcheco wants to merge 4 commits into
uuidjs:mainfrom
sergioperezcheco:fix/preserve-random-buffer

Conversation

@sergioperezcheco

@sergioperezcheco sergioperezcheco commented Oct 8, 2026 •

Copy link
Copy Markdown

v1({ random: Buffer }) and v6({ random: Buffer }) must not mutate caller-owned random bytes when setting the generated node's multicast bit. Capture the six node bytes in scalar locals before writing the output, preserving ordinary-array random/rng inputs and correct bytes when random or explicit-node views overlap the output. This avoids allocating another node array, following LinusU's review; explicit node multicast bits are unchanged.

Regression coverage includes Buffer/Uint8Array and ordinary-array sources, random/rng callbacks, input/output offsets and overlap, deterministic reuse, explicit nodes and separate output buffers. With the same final tests on Node 22/macOS, previous PR production gives 13 failures / 116 passes; the update gives 129 passes. Build, Node examples (ES modules/Jest/TypeScript), full Biome lint, direct changed-file formatting, strict publint and 140 built-export public checks pass. The unchanged format:check script fails on both versions because Biome rejects --check; lint retains an informational schema-version mismatch. Node 20/24, BrowserStack, other operating systems, performance benchmarks and a fresh root frozen-lock install were not run for this revision. The earlier copy-implementation benchmark does not describe this revision.

Prepared and checked with autonomous AI assistance through Hermes Agent; no human code review is claimed.

Copy generated node bytes before applying the multicast bit.

Assisted-by: OpenAI gpt-6.1-sol
Signed-off-by: sergioperezcheco <checo520@outlook.com>
@LinusU

LinusU commented Oct 8, 2026

Copy link
Copy Markdown
Member

Potentially it's faster to just modify the loop a bit to xor the value when assigning the output, that way you don't have to make a copy of the entire buffer.

e.g. from

  // `node`
  for (let n = 0; n < 6; ++n) {
    buf[offset++] = node[n];
  }

to

  // Set multicast bit
  // https://www.rfc-editor.org/rfc/rfc9562.html#section-6.10-3
  buf[offset++] = node[0] | 0x01;

  // `node`
  for (let n = 1; n < 6; ++n) {
    buf[offset++] = node[n];
  }

(this should only be done if node was null and was seeded from random data)

@LinusU

LinusU commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

Maybe something like:

  if (node == null) {
    node = rnds.slice(10, 16);

    // Set multicast bit
    // https://www.rfc-editor.org/rfc/rfc9562.html#section-6.10-3
    buf[offset++] = node[0] | 0x01;
  } else {
    buf[offset++] = node[0];
  }

  // `node`
  for (let n = 1; n < 6; ++n) {
    buf[offset++] = node[n];
  }

Address LinusU's review without copying caller-owned random bytes.

Assisted-by: autonomous AI agent
Signed-off-by: sergioperezcheco <checo520@outlook.com>
@sergioperezcheco

Copy link
Copy Markdown
Author

Changed in d28a601: the generated node now gets its multicast bit only when writing the first output byte, with no node-byte copy or input mutation. Explicit node bytes stay unchanged. The regression, full Node test suite, Node examples, lint, strict publint and an independent built-export replay pass on Node 22; I have not rerun the performance benchmark for this revision.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Ordinary-array inputs now throw, and overlapping random/output views corrupt v1 node bytes.

2 open issues
What changed in this PR

This PR prevents v1/v6 node generation from modifying caller-owned random buffers while preserving multicast-bit behavior.

Changes:

  • Set the multicast bit only when writing the generated node to output.
  • Add regression coverage for random sources, buffer views, deterministic reuse, and explicit nodes.
File Description
src/​v1.ts Moves multicast-bit handling to output writes.
src/​test/​v1.test.ts Tests input preservation across v1/v6 generation paths.

Review effort: Balanced

Comment thread src/v1.ts Outdated
// `node`
for (let n = 0; n < 6; ++n) {
if (node == null) {
node = rnds.subarray(10, 16);
Comment thread src/v1.ts Outdated
Comment on lines 210 to 221
if (node == null) {
node = rnds.subarray(10, 16);

// Set multicast bit without modifying the random input
// https://www.rfc-editor.org/rfc/rfc9562.html#section-6.10-3
buf[offset++] = node[0] | 0x01;
} else {
buf[offset++] = node[0];
}
for (let n = 1; n < 6; ++n) {
buf[offset++] = node[n];
}
Preserve documented ordinary-array random sources using indexed reads.
Capture node bytes before output writes can overwrite aliased inputs,
without allocating another node array or changing explicit multicast bits.

Assisted-by: Hermes Agent (Nous Research), gpt-6.1-sol
Signed-off-by: Checo Tang <checo520@outlook.com>
@sergioperezcheco

Copy link
Copy Markdown
Author

Both findings are reproducible on a53ded9. The fix captures the six node bytes in scalar locals before any output writes and uses indexed reads instead of subarray(), so ordinary-array random/rng inputs work and overlapping output cannot overwrite unread node bytes. This keeps the no-node-array-allocation approach from LinusU's review and preserves explicit node multicast bits. The full Node suite changes from 13 failures to 129 passing tests; rebuilt public-export checks, Node examples, lint and strict publint pass on Node 22/macOS. The existing format:check script rejects its --check option identically before and after the fix; direct changed-file formatting and the full Biome check pass, with the existing schema-version informational warning retained.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants