Fix integer overflow in napi_create_dataview bounds check#181
Fix integer overflow in napi_create_dataview bounds check#181bkaradzic-microsoft wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This pull request hardens the Chakra Node-API implementation of napi_create_dataview against a size_t addition overflow in its bounds check, preventing creation of a DataView whose stored (reported) offset/length could be out of range for the underlying buffer (potential OOB access in native addons). It also adds a targeted native regression test and wires up a build define so the test only compiles/runs for the Chakra configuration.
Changes:
- Fix overflow-prone range validation in
napi_create_dataviewby checkingbyte_offsetandbyte_lengthagainst the buffer size without adding them. - Add a Chakra + 64-bit guarded gtest regression that reproduces the wraparound scenario and asserts the request is rejected (or at minimum never reports raw huge extents).
- Add a CMake compile definition (
JSRUNTIMEHOST_NAPI_ENGINE_CHAKRA) for UnitTests when building with the Chakra backend.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| Core/Node-API/Source/js_native_api_chakra.cc | Replaces the overflow-prone byte_length + byte_offset bounds check with a subtraction-based check that cannot overflow and keeps stored DataViewInfo extents in range. |
| Tests/UnitTests/Shared/Shared.cpp | Adds a Chakra/_WIN64 regression test that constructs an overflowing (offset, length) pair and verifies it is rejected (and does not leak huge extents via napi_get_dataview_info). |
| Tests/UnitTests/CMakeLists.txt | Defines JSRUNTIMEHOST_NAPI_ENGINE_CHAKRA for UnitTests when NAPI_JAVASCRIPT_ENGINE is Chakra, enabling the guarded regression test. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
e448fbf to
3025fb8
Compare
3025fb8 to
7c4bd47
Compare
napi_create_dataview validated the requested view with an unchecked byte_length + byte_offset > bufferLength comparison. Both are caller-supplied size_t values, so on 64-bit builds their sum can overflow and wrap past the limit, slipping an out-of-range view past the check. - Chakra: the truncated 32-bit values created a small valid view while the original 64-bit extents were stored in DataViewInfo and handed back by napi_get_dataview_info, giving an out-of-bounds access primitive. - V8: the wrapped value passed the check and then tripped a fatal CHECK inside v8::DataView::New, aborting the process. Both backends now validate byte_offset and byte_length against the buffer size individually, without adding them. JavaScriptCore delegates to the JS DataView constructor and is unaffected. The V8JSI Node-API shim does not implement napi_create_dataview (its DataView::New throws "TODO"), so the regression test is compiled out there; it runs on Chakra, V8, and JavaScriptCore on 64-bit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
7c4bd47 to
a553dd1
Compare
|
Removing the Chakra-only guard (per review) surfaced two pre-existing issues in the other backends, now fixed:
Verified locally on all three: V8 passes, Chakra passes, JSI compiles cleanly (test gated out). |
|
|
||
| v8::Local<v8::ArrayBuffer> buffer = value.As<v8::ArrayBuffer>(); | ||
| if (byte_length + byte_offset > buffer->ByteLength()) { | ||
| // byte_offset and byte_length are caller-supplied size_t values; computing |
There was a problem hiding this comment.
Per our convention, changes to the vendored Node.js V8 N-API need the // [BABYLON-NATIVE-ADDITION] marker (e.g. lines 361, 2553, 3372) so the divergence stays visible when syncing from upstream.
| // byte_offset and byte_length are caller-supplied size_t values; computing | |
| // [BABYLON-NATIVE-ADDITION]: overflow-safe bounds check; not in Node.js upstream | |
| // byte_offset and byte_length are caller-supplied size_t values; computing |
Summary
napi_create_dataview(Chakra backend) validated the requested view with an uncheckedbyte_length + byte_offset > bufferLengthcomparison. Both operands are caller-suppliedsize_tvalues, so on 64-bit builds their sum can overflow and wrap past the limit. When that happens:JsCreateDataView(yielding a small, valid view), butbyte_offset/byte_lengthare stored inDataViewInfoand later returned bynapi_get_dataview_infoalongside the small backing buffer…handing a calling native addon an out-of-bounds read/write primitive (CWE-190 → CWE-125/787, also CWE-681).
Fix
Validate
byte_offsetandbyte_lengthagainst the buffer size individually, without adding them:cpp if (byte_offset > bufferLength || byte_length > bufferLength - byte_offset) { /* range error */ }After this check both values are
<= bufferLength(a 32-bit quantity), so the later 32-bit truncation and the values stored inDataViewInfoare guaranteed in range.DataViewconstructor → unaffected.Test
This path is not reachable from JS (
new DataViewuses the engine directly, not this N-API), so coverage is a native gtest. It crafts an offset/length whose low 32 bits are valid for a 16-byte buffer but whose 64-bit sum wraps, and asserts the view is rejected (and never reports the raw 64-bit extents). Guarded to the Chakra engine and 64-bit builds (JSRUNTIMEHOST_NAPI_ENGINE_CHAKRA+_WIN64).Verified the test fails on the pre-fix code and passes after the fix; full suite green (5 native tests + 151 JS tests) on Win32-x64 Chakra Debug.