Title: fs: heap buffer overflow in readFileSync(fd, "utf8") when a pipe returns short reads · Issue #66341 · nodejs/node · GitHub
Open Graph Title: fs: heap buffer overflow in readFileSync(fd, "utf8") when a pipe returns short reads · Issue #66341 · nodejs/node
X Title: fs: heap buffer overflow in readFileSync(fd, "utf8") when a pipe returns short reads · Issue #66341 · nodejs/node
Description: Version v26.10.0 (also present in v26.8.0 through v26.10.0 and v24.21.0; not present in v26.7.0 or v22.23.3) Platform Linux archpc 7.2.4-arch1-2 #1 SMP PREEMPT_DYNAMIC Tue, 08 Sep 2026 10:22:31 +0000 x86_64 GNU/Linux Arch Linux, nodejs 2...
Open Graph Description: Version v26.10.0 (also present in v26.8.0 through v26.10.0 and v24.21.0; not present in v26.7.0 or v22.23.3) Platform Linux archpc 7.2.4-arch1-2 #1 SMP PREEMPT_DYNAMIC Tue, 08 Sep 2026 10:22:31 +00...
X Description: Version v26.10.0 (also present in v26.8.0 through v26.10.0 and v24.21.0; not present in v26.7.0 or v22.23.3) Platform Linux archpc 7.2.4-arch1-2 #1 SMP PREEMPT_DYNAMIC Tue, 08 Sep 2026 10:22:31 +00...
Opengraph URL: https://github.com/nodejs/node/issues/66341
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"fs: heap buffer overflow in readFileSync(fd, \"utf8\") when a pipe returns short reads","articleBody":"### Version\n\nv26.10.0 (also present in v26.8.0 through v26.10.0 and v24.21.0; not present in v26.7.0 or v22.23.3)\n\n### Platform\n\n```text\nLinux archpc 7.2.4-arch1-2 #1 SMP PREEMPT_DYNAMIC Tue, 08 Sep 2026 10:22:31 +0000 x86_64 GNU/Linux\nArch Linux, nodejs 26.10.0-1, glibc 2.44\n```\n\n### Subsystem\n\nfs\n\n### What steps will reproduce the bug?\n\n```bash\n#!/usr/bin/env bash\n# Producer: 24 short writes (~96 KiB, each read back as a short read), then one large write.\nnode -e '\nconst chunk = \"x\".repeat(4000);\nlet i = 0;\nconst t = setInterval(() => {\n process.stdout.write(chunk);\n if (++i === 24) { clearInterval(t); process.stdout.write(\"y\".repeat(400000)); }\n}, 5);\n' | node -e 'console.log(require(\"fs\").readFileSync(0, \"utf8\").length)'\necho \"exit=${PIPESTATUS[1]}\"\n```\n\n### How often does it reproduce? Is there a required condition?\n\n10 of 10 runs with the script above. The input must be a pipe (or FIFO, or `/dev/stdin` backed by one) that delivers more than 64 KiB in reads shorter than 8 KiB before the first full 8 KiB read. Regular files are not affected. The encoding does not matter: the input above is pure ASCII.\n\nReal-world trigger: `some-test-runner | node -e 'require(\"fs\").readFileSync(0, \"utf8\")...'`, where the producer writes in small chunks. It crashed at 7 of 60 runs with a ~420 KB input written in 4093-byte chunks.\n\n### What is the expected behavior? Why is that the expected behavior?\n\nPrints `496000` and exits 0, as v26.7.0 does and as `readFileSync(0).toString(\"utf8\")` does on v26.10.0.\n\n### What do you see instead?\n\n```text\nmalloc(): invalid size (unsorted)\nexit=134\n```\n\nSIGABRT from glibc. Symbolized stack (from a real crash, via debuginfod.archlinux.org):\n\n```text\n#6 malloc_printerr (str=\"malloc(): invalid size (unsorted)\") at malloc.c:5093\n#7 _int_malloc (av=\u003cmain_arena>, bytes=427486) at malloc.c:3683\n#10 __GI___libc_realloc (oldmem=0x0, bytes=427486) at malloc.c:3198\n#11 node::UncheckedRealloc\u003cchar16_t> (pointer=0x0, n=\u003coptimized out>) at ../../src/util-inl.h:267\n#13 node::MaybeStackBuffer\u003cunsigned short, 256ul>::AllocateSufficientStorage (storage=213743) at ../../src/util-inl.h:544\n#14 node::StringBytes::Encode (...) at ../../src/string_bytes.cc:659\n#15 node::fs::ReadFileUtf8 (args=...) at ../../src/node_file.cc:3559\n```\n\nThe abort happens later than the corruption. The corruption itself is a heap buffer overflow in `ReadFileUtf8`.\n\n### Additional information\n\nCause, in `ReadFileUtf8` ([src/node_file.cc#L3511-L3523 at v26.10.0](https://github.com/nodejs/node/blob/59da6b44a953cf9b45f2591ae80f569ce1ec9da5/src/node_file.cc#L3511-L3523)):\n\n- While `big == nullptr`, every short read (`r \u003c 8192`) is appended to `result` and the loop `continue`s. On a pipe, `result` can grow past 64 KiB this way.\n- On the first full 8 KiB read, the code allocates `big_cap = kMinChunk` (64 KiB) and runs `memcpy(big, result.data(), result.size())` with no check that `result.size() \u003c= big_cap`. That write overflows the heap buffer.\n- After that, `big_len > big_cap`, so `big_len == big_cap` never matches, and `big_cap - big_len` in the next `uv_buf_init` underflows.\n\nA possible fix: size the first allocation from what is already buffered, for example `big_cap = std::max(kMinChunk, result.size() + sizeof(buffer))`, and make the growth check `big_len >= big_cap`.\n\nWorkaround: `fs.readFileSync(0).toString(\"utf8\")` avoids the fast path.\n","author":{"url":"https://github.com/bartech-lab","@type":"Person","name":"bartech-lab"},"datePublished":"2026-09-27T08:15:04Z","url":"https://github.com/nodejs/node/issues/66341"}
| react-profiling | 0 |
| react-import-map | react,react-dom,react-dom/client,react-dom/profiling,react-is,react-reconciler,react/compiler-runtime,react/jsx-dev-runtime,react/jsx-runtime,scheduler@d5750093facf |
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:018d92db-4a7d-eb0f-1900-9cfdc86004e4 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | D028:16D14D:4B5E71:66D5C9:6AED56E8 |
| html-safe-nonce | c5124551914060d0614593e8600050db91d26d98a4bb7d1372b152a44b6ae1f2 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJEMDI4OjE2RDE0RDo0QjVFNzE6NjZENUM5OjZBRUQ1NkU4IiwidmlzaXRvcl9pZCI6IjY1ODEyOTA1ODI1OTA0NDUyODgiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | 642ca27f324ca547c352935a948464aba0c4f23cad406f83fa9afaf639c3489f |
| hovercard-subject-tag | issue:5601053677 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/nodejs/node/66341/issue_layout |
| twitter:image | https://opengraph.githubassets.com/6e702f49df8e2a6ec14f73a608244eba14d4946bd829e1121b6b2d257ad56caf/nodejs/node/issues/66341 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/6e702f49df8e2a6ec14f73a608244eba14d4946bd829e1121b6b2d257ad56caf/nodejs/node/issues/66341 |
| og:image:alt | Version v26.10.0 (also present in v26.8.0 through v26.10.0 and v24.21.0; not present in v26.7.0 or v22.23.3) Platform Linux archpc 7.2.4-arch1-2 #1 SMP PREEMPT_DYNAMIC Tue, 08 Sep 2026 10:22:31 +00... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | bartech-lab |
| hostname | github.com |
| expected-hostname | github.com |
| None | 56fb42677ae87ed3e64410323d67980aa65d0485568a7858f874e7edfc6d1c11 |
| turbo-cache-control | no-preview |
| go-import | github.com/nodejs/node git https://github.com/nodejs/node.git |
| octolytics-dimension-user_id | 9950313 |
| octolytics-dimension-user_login | nodejs |
| octolytics-dimension-repository_id | 27193779 |
| octolytics-dimension-repository_nwo | nodejs/node |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 27193779 |
| octolytics-dimension-repository_network_root_nwo | nodejs/node |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 9c9a37e2423ed382cf39f70b5aa4b4858b40a3b0 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
| react-app-name | issues |
Links:
Viewport: width=device-width