`PooledBuffer.FromStringUtf8`/`FromSpanUtf8` never write the NUL terminator → garbage in JS error messages and linked module names on .NET
Đánh giá
- Độ khó
- 2/5
- Thời gian dự kiến
- 1-3 giờ
- Mức phù hợp với người mới
- 30/100
Hướng nghiên cứu
Start with src/NodeApi/Runtime/PooledBuffer.cs and inspect FromStringUtf8 and FromSpanUtf8; the issue includes a deterministic reproduction that dirties the shared array pool. Check the linked pull request #510 for work already underway. Done means UTF-8 buffers terminate correctly, empty strings are handled, and the reported error message and module name cases no longer include garbage.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
PooledBuffer.FromStringUtf8 and PooledBuffer.FromSpanUtf8 (src/NodeApi/Runtime/PooledBuffer.cs) reserve byteLength + 1 bytes but never write the terminating \0:
PooledBuffer buffer = new(byteLength, byteLength + 1); // reserves the terminator byte...
Encoding.UTF8.GetBytes(value, 0, value!.Length, buffer.Buffer, 0); // ...but never writes it
On .NET the array comes from ArrayPool<byte>.Shared.Rent, which is not zeroed. (On .NET Framework / netstandard it is a fresh new byte[], so the bug only shows on .NET.) Several call sites pass the pointer to Node-API functions that take a NUL-terminated const char* without a length:
| Managed API | Native function | Argument |
|---|---|---|
JSError.ThrowError / ThrowTypeError / ThrowRangeError / ThrowSyntaxError (string overloads) |
napi_throw_error, napi_throw_type_error, napi_throw_range_error, node_api_throw_syntax_error |
code and msg |
NodeEmbeddingRuntimeSettings.Modules |
node_embedding_runtime_config_add_module |
module_name |
exceptions in the embedding callback adapters (TaskRunCallbackAdapter, …) |
node_embedding_last_error_message_set |
message |
Once the pool is warm, native code reads stale bytes after the string, or past the end of the array if it contains no zero byte (out-of-bounds read).
Call sites that pass an explicit length (CreateFunction, DefineClass, GetSymbolFor, FatalError) are not affected.
What we saw
- In production (0.9.21, net10.0): a module registered via
NodeEmbeddingRuntimeSettings.Modulesasgaiawas linked under a corrupted name, andprocess._linkedBinding('gaia')failed withNo such binding was linked: gaia. It happened intermittently, only after the app had been running for a while. JSError.ThrowError("x")from a .NET callback → in JS,e.messageis"x"followed by 15 garbage bytes (x���������������).
Repro
Make it deterministic by dirtying the shared pool on the JS thread first:
static void FillArrayPoolWithGarbage()
{
List<byte[]> rented = new();
for (int size = 1; size <= 1024; size *= 2)
for (int i = 0; i < 8; i++)
{
byte[] array = ArrayPool<byte>.Shared.Rent(size);
array.AsSpan().Fill(0xAA);
rented.Add(array);
}
foreach (byte[] array in rented) ArrayPool<byte>.Shared.Return(array);
}
nodejs.Run(() =>
{
JSValue.Global.SetProperty("throwError", JSValue.CreateFunction("throwError", (args) =>
{
FillArrayPoolWithGarbage();
JSError.ThrowError((string)args[0]);
return JSValue.Undefined;
}));
string message = (string)JSValue.RunScript(
"(() => { try { throwError('x'); } catch (e) { return e.message; } })()");
// Expected "x"; actual "x" + garbage.
});
Related
- An empty string returns
PooledBuffer.Empty, which pins to a NULL pointer. SoJSError.ThrowError("")passesmsg = NULL,napi_throw_errorreturnsnapi_invalid_arg, and no error is thrown at all. An empty module name would likewise passmodule_name = NULL. NodejsRuntime.EmbeddingRuntimeConfigAddModulenever disposes itsPooledBuffer, so the rented array is not returned to the pool.- Same class of bug as #481 (
Utf8StringArray, fix in PR #482).
Suggested fix
Write the terminator explicitly in both helpers (buffer.Buffer[byteLength] = 0). Encode "" as a 1-byte buffer holding only the terminator, and keep null → null pointer (used for the optional code). Add using in EmbeddingRuntimeConfigAddModule; Node.js copies the name into a std::string, so disposing right after the call is safe. I have a PR ready.
Versions: reproduced on main (54d2b74) and in 0.9.21; the code is unchanged in 0.9.28. Windows x64, net10.0, Microsoft.JavaScript.LibNode 20.1800.203.
- Ngôn ngữ chính
- C#
- Star
- 789
- Fork
- 81
- Merge trung bình
- 4 ngày 22 giờ
- Pull request đã merge (30 ngày)
- 4
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Không có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của microsoft/node-api-dotnet
-
Độ khó 1/5 1-3 giờ Mức phù hợp với người mới 88/100
microsoft/node-api-dotnet#484 · 1 bình luận ·
-
`JSError.ThrowTypeError` / `ThrowRangeError` / `ThrowSyntaxError` throw a plain `Error`Có thể đã có người làm @r-Larch đã nhận 7 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 58/100
microsoft/node-api-dotnet#509 ·
-
JSError.ThrowError(Exception) aborts the process when a Worker terminates during a .NET callCó thể đã có người làm @GalaxiasKyklos đã nhận 15 ngày trước. Đang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 18/100
microsoft/node-api-dotnet#505 ·
-
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 70/100
microsoft/node-api-dotnet#504 ·
-
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 82/100
microsoft/node-api-dotnet#502 ·
Tất cả issue của microsoft/node-api-dotnet
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
Maintainer thường phản hồi trong vòng 1 ngày
-
[Bug] Dashboard renders attachment links that resolve to 404 when the FILES binding is absentĐang mởbug 優先: 低
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 92/100
-
.NET area: storage bug good first issue
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
Larson-Logan/medal-sync#61 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 88/100
Maintainer thường phản hồi trong vòng 1 ngày