Skip to content

Memory leak in node::fs::ReadFileUtf8(const FunctionCallbackInfo<Value>& args) when args[0] is a string. #57800

Closed
@Justin-Nietzel

Description

@Justin-Nietzel

Version

v22.14.0

Platform

Microsoft Windows NT 10.0.19045.0 x64

Subsystem

node::fs

What steps will reproduce the bug?

Open a file with ReadFileUtf8() using a path (not a file handle).

How often does it reproduce? Is there a required condition?

Every time, including loading JavaScript files for execution.

What is the expected behavior? Why is that the expected behavior?

Calling readFileSync() with an encoding of UTF-8 should not permanently increase the amount of rss memory being used by node. It's (hopefully) the expected behavior because memory leaks are bad.

What do you see instead?

Calling will cause Node's memory usage to go up indefinitely.

This was discovered when a process using node:worker_threads would continuously increase it's memory consumption until it was forcibly closed. JavaScript debug tools showed that the managed heap's memory usage was constant, but the rss memory usage kept rising. Debugging the node runtime showed that loading modules was calling the underlying ReadFileUtf8 method. The process that was showing this memory leak was not caching workers and was re-loading all code files each time a worker thread was created.

Minimum code example:

import { readFileSync } from 'node:fs';

const options = { encoding: 'utf-8' };
for (let i = 1; true; i++) {
  readFileSync('./empty.txt', options);
  if (i % 1000 === 0) {
    i = 0;
    gc();
  }
}

Memory usage in task manager:
Image

Output from process.memoryUsage():

2025-04-08 20:23:38:       rss -   357.03 MB
2025-04-08 20:23:38: heapTotal -     7.03 MB
2025-04-08 20:23:38:  heapUsed -     4.28 MB
2025-04-08 20:23:38:  external -     1.73 MB

Additional information

This is my best guess at the bug. I'm like 85% sure of what's going, but this is the first time I'm looking at the NodeJS codebase.

Allocation:

  • void ReadFileUtf8(const FunctionCallbackInfo<Value>& args) calls uv_fs_open which calls fs__capture_path.
  • fs__capture_path allocates a UTF16 buffer and assigns it to uv_fs_s.file.pathw.
  • uv_fs_s.file is a union type with the definition: union { WCHAR* pathw; int fd; }

Corruption

  • uv_file file; is allocated and assigned the return value from uv_fs_open.
  • ReadFileUtf8 calls uv_fs_read passing in the file handle file.
  • uv_fs_read overwrites the least significant half of uv_fs_s.file.pathw with this call req->file.fd = fd;.
  • The pointer to the buffer formerly assigned to uv_fs_s.file.pathw is lost.

Metadata

Metadata

Assignees

No one assigned

    Labels

    fsIssues and PRs related to the fs subsystem / file system.libuvIssues and PRs related to the libuv dependency or the uv binding.windowsIssues and PRs related to the Windows platform.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions