Skip to content

Conversation

Rabie45
Copy link

@Rabie45 Rabie45 commented Oct 15, 2025

Fixes an out-of-bounds memory access vulnerability caused by unvalidated copy ranges in Buffer::SlowCopy() and Buffer::FastCopy().

The patch adds proper instance checks and range validation before calling CopyImpl, preventing potential segfaults or memory corruption.

Refs: #59985

Fixes an out-of-bounds memory access vulnerability caused by
unvalidated copy ranges in Buffer::SlowCopy() and Buffer::FastCopy().

The patch adds proper instance checks and range validation before
calling CopyImpl, preventing potential segfaults or memory corruption.

Refs: nodejs#59985

Signed-off-by: Rabie45 <[email protected]>
@nodejs-github-bot nodejs-github-bot added buffer Issues and PRs related to the buffer subsystem. c++ Issues and PRs that require attention from people who are familiar with C++. needs-ci PRs that need a full CI run. labels Oct 15, 2025
}
// Validate First before call CopyImpl blindly
size_t src_len = Buffer::Length(source_obj.As<v8::Object>());
size_t dst_len = Buffer::Length(target_obj.As<v8::Object>());
Copy link
Member

Choose a reason for hiding this comment

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

Just fyi, Buffer::HasInstance(obj) is an alias for obj->IsArrayBufferView() and Buffer::Length(obj) is an alias for obj.As<ArrayBufferView>()->ByteLength(). I'd suggest just using those here directly

Copy link
Author

Choose a reason for hiding this comment

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

Thanks for the note you’re absolutely right. I’ve updated the code to use IsArrayBufferView() and ByteLength() directly instead of Buffer::HasInstance() and Buffer::Length() for consistency with the V8 API.


if (source_start > src_len || target_start > dst_len ||
to_copy > src_len - source_start || to_copy > dst_len - target_start) {
// options.fallback = true; // Let JS slow path handle it (safe fallback)
Copy link
Member

Choose a reason for hiding this comment

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

This seems to be an unresolved comment here? We don't do fallbacks anymore, but we still need to handle the situation in this case.

Maybe we could return -1 and check for that on the JS side?

Copy link
Author

Choose a reason for hiding this comment

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

You’re right, returning a sentinel like -1 would be clearer to signal an invalid range. However, since the function currently returns a uint32_t, using -1 would actually wrap around to 4294967295, which could cause unexpected behavior.

If we want to use -1 safely, we should change the return type to int32_t so the error code is properly represented.

Copy link
Contributor

@Renegade334 Renegade334 left a comment

Choose a reason for hiding this comment

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

The same validation behaviour can be performed in JS, it just requires the use of the immutable TypedArrayPrototypeGetByteLength rather than direct .byteLength property access, which is the reason for the unsafe behaviour reported in that issue.

All calls to the bound C++ Copy callback occur via the _copyActual validation wrapper in JS; it doesn't really make much sense to duplicate the same validation steps in C++, instead of just fixing the existing ones.


if (source_start > src_len || target_start > dst_len ||
to_copy > src_len - source_start || to_copy > dst_len - target_start) {
return -1;
Copy link
Member

Choose a reason for hiding this comment

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

This still needs to be actually handled on the JS side, right?

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

Labels

buffer Issues and PRs related to the buffer subsystem. c++ Issues and PRs that require attention from people who are familiar with C++. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants