Skip to content

Bound the UTF-8 decode in napi_create_string_utf8 - #2267

Open
Medios-lgtm wants to merge 1 commit into
meta:static_hfrom
Medios-lgtm:napi-utf8-bounded-decode
Open

Medios-lgtm wants to merge 1 commit into
meta:static_hfrom
Medios-lgtm:napi-utf8-bounded-decode

Conversation

@Medios-lgtm

Copy link
Copy Markdown
Contributor

Summary

napi_create_string_utf8 decodes non-ASCII input with hermes::decodeUTF8, which reads the continuation bytes of a multi-byte sequence without an end check and relies on a terminator to stop. With an explicit length, str does not have to be null-terminated, so input that ends inside a multi-byte sequence (for example a slice of a larger buffer that cuts a character in half) is read up to 3 bytes past str + length, and those bytes are decoded into the string when they happen to be continuation bytes. Under ASan, a 1-byte heap buffer holding 0xE2 passed with length == 1 gives a heap-buffer-overflow READ in _decodeUTF8SlowPath called from napi_create_string_utf8.

The fix decodes a null-terminated copy, so a truncated sequence becomes U+FFFD, which is what the existing CreateStringUtf8_Truncated* tests already expect for terminated input. The other UTF-8 entry points (property keys, node_api_symbol_for, function names) go through the bounded llvh::ConvertUTF8toUTF16 and are not affected.

Test Plan

Added CreateStringUtf8_TruncatedAtLengthNotTerminated, which passes a length that ends inside a 2-byte and a 4-byte sequence. Before the fix it decodes the bytes after length (U+00E9, and a surrogate pair); after the fix each is a single U+FFFD.

cmake --build cmake-build-asan --target NapiTests
cmake-build-asan/unittests/napi/NapiTests
[  PASSED  ] 1057 tests.

Also checked by hand in an ASan build with exact-size heap buffers (new char[1] holding 0xC3, 0xE2 or 0xF0, length 1): heap-buffer-overflow before the fix, clean after. LIT_FILTER="napi|Napi" check-hermes passes.

decodeUTF8 reads the continuation bytes of a sequence without an end
check and relies on a terminator. With an explicit length, the input to
napi_create_string_utf8 does not have to be null-terminated, so input
that ends inside a multi-byte sequence was read past str + length.
Decode a null-terminated copy instead.
@meta-cla meta-cla Bot added the CLA Signed Do not delete this pull request or issue due to inactivity. label Oct 23, 2026

This branch was successfully deployed

1 active deployment
npm-publish — a42a1c7d Deployed Oct 23, 2026 by Medios-lgtm via publish #1631
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed Do not delete this pull request or issue due to inactivity.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant