fix: clamp negative truncate length to zero (uninitialized-memory leak)#1261
Merged
streamich merged 1 commit intoJun 20, 2026
Conversation
`Node.truncate(len)` only special-cased `len === 0`; a negative length
fell through to `len <= this.size` and set `this.size` to the negative
value. `getBuffer()` then calls `buf.subarray(0, this.size)`, which
interprets the negative end relative to the buffer length and returns
bytes past the used region — leaking uninitialized memory from the
`bufferAllocUnsafe` backing buffer.
For example, after `writeFileSync('/a', 'hello')`, calling
`truncateSync('/a', -1)` produced a 63-byte file containing "hello"
followed by uninitialized heap bytes. Node.js `fs.truncate` /
`fs.ftruncate` clamp negative lengths to an empty file, so do the same.
Adds regression tests at the `Node` and `Volume` (public `truncateSync`)
levels.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Node.truncate(len)(infs-core) only special-caseslen === 0. A negative length falls through to theif (len <= this.size)branch and setsthis.sizeto the negative value:getBuffer()then doesthis.buf.subarray(0, this.size). Withthis.size === -1,subarraytreats the end index relative to the buffer's length and returns bytes past the used region — i.e. uninitialized memory from thebufferAllocUnsafebacking buffer.Reproduction
Node.js for comparison:
Node's
fs.truncate/fs.ftruncateclamp a negative length to an empty file (the C++ layer floors negative offsets at 0). memfs instead produced a larger file leaking adjacent heap contents.Fix
Treat a negative
lenthe same as0inNode.truncate— change thelen === 0guard tolen <= 0. One-line behavioral change;len === 0is unaffected.Tests
Regression tests added at two levels, both verified to fail before the fix and pass after:
fs-coreunit:Node#truncate(-1)→ size0, empty buffer.fs-nodepublic API:vol.truncateSync('/neg', -1)→readFileSync(...).length === 0(guards against the leak).Full
fs-core+fs-nodesuites pass (817 tests),typecheckandprettier:checkclean.Context
While investigating #942 (close-after-unlink — already resolved by the
Superblockrefactor), differential testing against Node's realfssurfaced this separate negative-length truncate parity gap, which has the added concern of exposing uninitialized memory.