Skip to content

fix(io): preserve ownership of read_some chunks#514

Merged
Guest0x0 merged 1 commit into
moonbitlang:mainfrom
colmugx:fix/read-buffer
Jul 24, 2026
Merged

fix(io): preserve ownership of read_some chunks#514
Guest0x0 merged 1 commit into
moonbitlang:mainfrom
colmugx:fix/read-buffer

Conversation

@colmugx

@colmugx colmugx commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Reader::read_some could return a Bytes value backed by its reusable internal read buffer. When a returned view covered the entire buffer, BytesView::to_owned() reused the backing Bytes instead of copying it. A later read then overwrote data that had already been returned to the caller.

This is observable outside HTTP as a general Reader ownership bug. On the JavaScript backend it corrupts streamed fetch request bodies: JsReadableStream::new_pipe enqueues a chunk, the next pipe read reuses the same 1024-byte buffer, and fetch later observes the overwritten bytes.

Problem

Before writing to @http.Client, the serialized UTF-8 body was captured. A local Node HTTP server captured the exact bytes received on the wire.

expected bytes: 2654

received bytes: 2654

chunks: 1024 + 1024 + 606

expected SHA-256: 2cafae89bff675c6fb8e6a49e904356c598f52f0743de9506578c5a9d2e23368

received SHA-256: dd4fee7cc2820e53922b5304b83d61984638f97568a0b6f50072776f3411a8d6

first differing offset: 0

server-side JSON.parse: failed

The received stream was not simply truncated. Its layout showed buffer reuse:

received[0..1630] == expected[1024..2654]
received[1630..2654] == expected[1630..2654]

At chunk boundaries this was:

received chunk 1 = expected chunk 2
received chunk 2 = expected final 606 bytes + stale 418-byte tail
received chunk 3 = expected final 606 bytes

@Guest0x0

Copy link
Copy Markdown
Collaborator

lgtm. Initially I was thinking of letting read_some do Bytes::from_array(buf[_:_]) directly instead, since allocation is necessary anyway. But turns out your fix is actually better, because it can save up one copy operation when the slice happens to be the whole buffer.

@Guest0x0
Guest0x0 merged commit 4d7a175 into moonbitlang:main Jul 24, 2026
21 checks passed
@Guest0x0

Copy link
Copy Markdown
Collaborator

Thanks for your contribution!

@colmugx
colmugx deleted the fix/read-buffer branch July 24, 2026 04:50
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.

2 participants