Skip to content

[release/3.0] fix(odsp-driver): remove invalid byteOffset === 0 assumption - #28199

Merged
Lindsey Nguyen (lindsnguyen) merged 2 commits into
microsoft:release/client/3.0from
lindsnguyen:3.0/odsp-compact-offset
Sep 11, 2026
Merged

Lindsey Nguyen (lindsnguyen) merged 2 commits into
microsoft:release/client/3.0from
lindsnguyen:3.0/odsp-compact-offset

Conversation

@lindsnguyen

Copy link
Copy Markdown
Contributor

Backport for the ODSP compact snapshot parser fix to support non-zero-offset buffers exposed by Node 24.18+.
PR for same change into main: #28194 (root cause and validation)

…ption (microsoft#28194)

Fixes compact snapshot parsing when the input is a valid `Uint8Array`
view with a non-zero `byteOffset`.

The ODSP compact snapshot parser asserted that its input must begin at
offset zero:

```ts
assert(input.byteOffset === 0, 0x3e8 /* code below assumes no offset */);
```

The affected snapshots were valid, but their binary data was represented
by a view into a larger backing buffer. The assertion closed the Fluid
container during loading and caused eight of the 29 Office eDiscovery
fixtures to fail with assertion `0x3e8`.

The assertion has existed since 2022, but the relevant inputs previously
happened to have a zero `byteOffset`, leaving the invalid assumption
hidden.

The failure started after [Node.js 24.18.0 increased the default
`Buffer.poolSize` from 8 KiB to 64
KiB](nodejs/node#63597). Node uses this pool for
`Buffer.allocUnsafe()` allocations smaller than half the pool size. This
increased the pooling threshold from approximately 4 KiB to 32 KiB.

The Office eDiscovery export path reads binary snapshots using:

```ts
fs.readFileSync(filePath)
```

Node's `fs.readFileSync()` allocates the returned `Buffer` using
`Buffer.allocUnsafe(fileSize)`. A Node `Buffer` is also a `Uint8Array`
view.

Before Node 24.18:

- The default buffer pool was 8 KiB.
- Allocations of approximately 4 KiB or larger bypassed the pool.
- Relevant snapshot reads therefore received dedicated backing buffers,
normally with `byteOffset === 0`.

Beginning with Node 24.18:

- The default buffer pool is 64 KiB.
- Allocations smaller than approximately 32 KiB can come from the shared
pool.
- Those buffers can be slices of the shared backing allocation and
therefore have a valid non-zero `byteOffset`.

Node 24.20 and 24.21 inherited this behavior. This explains why the
failure appeared when the Office External Partner pipeline moved from
Node 24.11 to newer Node 24 releases.

The Office External Partner integration pipeline selected Node using
this floating range:

```yaml
- task: UseNode@1
  inputs:
    version: ">=24.11.0 <25.0.0"
```

The range selects the newest available matching Node 24 release, not the
lowest version in the range. Normal Office CI did not initially
reproduce the issue because its managed build image still provided Node
24.11.0.

Controlled runs confirmed the Node version as the differentiating
variable:

- Node 24.21.0 with Fluid build 422789: eight eDiscovery failures.
- Node 24.11.0 with the same Fluid build and Office test code: all 29
eDiscovery tests passed.

Node did not introduce malformed snapshot data or change the
`Uint8Array` contract. The larger buffer pool caused more file reads to
use valid offset views, exposing Fluid's existing assumption about the
backing-buffer layout.

Pinning Node 24.11 would avoid the expanded pooling threshold and can
serve as a temporary mitigation. However, it would leave Fluid dependent
on an allocation detail that Node does not guarantee. Other consumers
can also provide valid non-zero-offset `Uint8Array` views independently
of Node's file-reading behavior.

The assertion was originally added by Vlad Sudzilouski in microsoft#12058 as part
of a string-parsing performance optimization. The PR discusses
null-character handling and differences between browser and Node string
decoding, but it does not document why a zero byte offset was required.

The parser does not require a zero-offset backing buffer:

- `Uint8Array` indexing is relative to the view.
- `Uint8Array.subarray()` is relative to the view.
- `ReadBuffer` positions and reads are relative to the supplied
`Uint8Array`.
- Existing `BlobShallowCopy` handling explicitly accounts for
`data.byteOffset`.

For example, if a view begins at offset 100 in its backing buffer,
`input[0]` still reads the first byte of that view, not byte zero of the
backing buffer. The string positions recorded by the parser are also
relative to that same view.

A non-zero offset is therefore a supported memory layout rather than
evidence of a malformed or corrupted snapshot. The assertion rejected
valid input without providing additional snapshot-integrity protection.

This change removes that assertion. It does not remove validation of
snapshot structure, string lengths, tree markers, skipped ranges, or
unexpected end-of-file conditions.

Copying the input into a new zero-offset buffer would also avoid the
assertion, but it would introduce an unnecessary full-snapshot
allocation and preserve the incorrect parser restriction.

- Remove the `byteOffset === 0` assertion from compact snapshot string
loading.
- Add a regression test that:
  - Serializes a compact snapshot.
  - Embeds it inside a larger backing buffer.
  - Passes a non-zero-offset `subarray()` to `TreeBuilder.load()`.
  - Confirms that the test input actually has a non-zero `byteOffset`.
- Verifies that the resulting strings, blobs, and tree match the
original snapshot.

The issue was isolated using controlled Office External Partner pipeline
runs:

| Office build | Node | Fluid build | eDiscovery result |
| --- | --- | --- | --- |
|
[55007732](https://office.visualstudio.com/OC/_build/results?buildId=55007732&view=results)
| 24.21.0 | 422789 | 21 passed, 8 failed with `0x3e8` |
|
[55015480](https://office.visualstudio.com/OC/_build/results?buildId=55015480&view=results)
| 24.11.0 | 422789 | 29/29 passed |
|
[55069760](https://office.visualstudio.com/OC/_build/results?buildId=55069760&view=results)
| 24.21.0 | 422948 with this fix | 29/29 passed; pipeline succeeded |

The first two runs used the same Fluid build and Office test code, with
only the Node version changed. This confirmed that the larger buffer
pool in newer Node versions exposes the existing parser assumption.

The final run restored the triggering Node 24.21.0 environment and used
Fluid build 422948 containing this change. All 29 eDiscovery tests
passed, including the eight fixtures that previously failed. The overall
integration pipeline also succeeded.

The ODSP driver build completed successfully, including compilation,
linting, API checks, and test compilation. The Tree Representation test
suite passed in both ESM and CommonJS configurations.

A Fluid Real Service End to End Tests run was also queued using client
build 422948 to exercise the change against an actual ODSP tenant:

- [Fluid Real Service E2E build
423091](https://dev.azure.com/fluidframework/internal/_build/results?buildId=423091&view=results)

Please focus on whether any compact snapshot parsing operation depends
on the backing buffer starting at offset zero.

The relevant string positions are relative to the supplied `Uint8Array`
view, and the added regression test exercises the parser using a
non-zero-offset view and compares the fully parsed result with the
original tree.

The original assertion author was Vlad Sudzilouski (`vladsud`) in
zero-offset invariant.
@github-actions github-actions Bot added area: tools area: driver Driver related issues area: repo Repo related work area: website area: odsp-driver base: release PRs targeted against a release branch labels Sep 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Warning

WARNING: This PR is targeting a release branch!

All changes must first be merged into main and then backported to the target release branch.
Please include a link to the main PR in the description of this PR.

Changes to release branches require approval from the Patch Triage group before merging.
You should have already discussed this change with them so they know to expect it.

For more details, see our internal documentation for the patch policy and processes for
patch releases.

@github-actions

github-actions Bot commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Hi! Thank you for opening this PR. Want me to review it?

Based on the diff (27 lines, 3 files), I've queued these reviewers:

  • Correctness — logic errors, race conditions, lifecycle issues
  • Security — vulnerabilities, secret exposure, injection
  • API Compatibility — breaking changes, release tags, type design
  • Performance — algorithmic regressions, memory leaks
  • Testing — coverage gaps, hollow tests

How this works

  • Adjust the reviewer set by ticking/unticking boxes above. Reviewer toggles alone don't trigger anything.

  • Tick Start review below to dispatch the review fleet.

  • After review finishes, tick Start review again to request another run — it auto-resets after each dispatch.

  • This comment updates as new commits land; your reviewer selections are preserved.

  • Start review

@jason-ha

Copy link
Copy Markdown
Contributor

Should this have a small changeset to note the fix?

@lindsnguyen

Lindsey Nguyen (lindsnguyen) commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor Author

Should this have a small changeset to note the fix?

yeah i didn't think of that in the main pr, will make it there and add that commit to this backport pr.

…soft#28203)

## Description

Adds the missing changeset for the compact snapshot parsing fix in [PR
microsoft#28194](microsoft#28194).

Although the implementation change is internal, it fixes a
consumer-visible compatibility issue where valid compact snapshots could
fail to load with pooled buffers returned by Node.js 24.18 and later.
@github-actions

Copy link
Copy Markdown
Contributor

Bundle size comparison

Base commit: c2fe8c5879996195c6b9edbb6fe44884a638d8f2
Head commit: ddde2d2c26ba14a45c66b8cab2ddf1b666eac7c6

Pending — Build - client packages is running. Results will appear here when the build completes.

@shlevari
shlevari self-requested a review September 11, 2026 00:27
@lindsnguyen
Lindsey Nguyen (lindsnguyen) merged commit 58071c4 into microsoft:release/client/3.0 Sep 11, 2026
34 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: driver Driver related issues area: odsp-driver area: repo Repo related work area: tools area: website base: release PRs targeted against a release branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants