Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

async_hooks: fix leak in AsyncLocalStorage exit #35779

Open
wants to merge 2 commits into
base: master
from

Conversation

@Qard
Copy link
Member

@Qard Qard commented Oct 23, 2020

If exit is called and then run or enterWith are called within the exit function, the als instance should not be added to the storageList additional times. The correct behaviour is to remove the instance from the storageList before executing the exit handler and then to restore it after.

This PR fixes a memory leak wherein an AsyncLocalStorage instance could be repeatedly inserted to the storageList array which keeps track of active instances to call als._propagate(...) on in the shared async_hooks instance.

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines
Copy link
Member

@vdeturckheim vdeturckheim left a comment

LGTM, thanks a lot @Qard
I feel like _enable() has been added and removed so many times by now ^^

@Qard
Copy link
Member Author

@Qard Qard commented Oct 23, 2020

Yep. The other option I considered was wrapping the exit function in another AsyncResource without the storage slot, which I believe we also had at one point but removed due to people thinking the overhead of another AsyncResource was unnecessary. 😅

If exit is called and then run or enterWith are called within the
exit function, the als instace should not be added to the storageList
additional times. The correct behaviour is to remove the instance
from the storageList before executing the exit handler and then to
restore it after.
@Qard Qard force-pushed the Qard:fix-als-exit branch from 2d7f986 to 43fb5bb Oct 23, 2020
@Qard
Copy link
Member Author

@Qard Qard commented Oct 23, 2020

Whoops...gotta appease that linter. 😅

@github-actions github-actions bot removed the request-ci label Oct 23, 2020
Copy link
Member

@mhdawson mhdawson left a comment

LGTM

@Flarna
Flarna approved these changes Oct 23, 2020
Copy link
Member

@puzpuzpuz puzpuzpuz left a comment

Nice catch!

@codecov-io
Copy link

@codecov-io codecov-io commented Oct 23, 2020

Codecov Report

Merging #35779 into master will decrease coverage by 0.00%.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##           master   #35779      +/-   ##
==========================================
- Coverage   87.92%   87.91%   -0.01%     
==========================================
  Files         477      477              
  Lines      113090   113088       -2     
  Branches    24630    24628       -2     
==========================================
- Hits        99433    99424       -9     
- Misses       7952     7954       +2     
- Partials     5705     5710       +5     
Impacted Files Coverage Δ
lib/async_hooks.js 100.00% <100.00%> (ø)
src/connect_wrap.h 25.00% <0.00%> (-75.00%) ⬇️
src/api/utils.cc 28.57% <0.00%> (-2.86%) ⬇️
src/inspector_profiler.cc 76.17% <0.00%> (-1.09%) ⬇️
src/node_api.cc 69.64% <0.00%> (-0.56%) ⬇️
src/api/environment.cc 75.60% <0.00%> (-0.28%) ⬇️
src/node_worker.cc 77.28% <0.00%> (-0.24%) ⬇️
lib/_http_server.js 97.93% <0.00%> (-0.21%) ⬇️
lib/internal/streams/destroy.js 94.66% <0.00%> (-0.09%) ⬇️
lib/internal/util/inspect.js 95.67% <0.00%> (+0.14%) ⬆️
... and 2 more
@Qard Qard added the request-ci label Oct 23, 2020
@github-actions github-actions bot removed the request-ci label Oct 23, 2020
@Qard
Copy link
Member Author

@Qard Qard commented Oct 23, 2020

Seems to me like that codecov result probably shouldn't be getting reported for an incomplete test run. 🤔

@Qard
Copy link
Member Author

@Qard Qard commented Oct 23, 2020

CI doesn't want to cooperate. I keep getting seemingly unrelated errors. 🤔

@Trott
Trott approved these changes Oct 24, 2020
@Trott

This comment has been hidden.

@Trott

This comment has been hidden.

Co-authored-by: Rich Trott <rtrott@gmail.com>
// lib/async_hooks.js when exit(...) is called, therefore when the nested runs
// are called there should be no copy of the als in the storageList to run the
// _propagate method on.
als._propagate = common.mustNotCall('_propagate() should not be called');

This comment has been minimized.

@Flarna

Flarna Oct 24, 2020
Member

This is quite sensitive, e.g. someone renames _propagate and this test still passes but is worthless.
What about

Suggested change
als._propagate = common.mustNotCall('_propagate() should not be called');
const fName = "_propagate";
assert.ok(typeof(als[fName]) === "function");
als[fName] = common.mustNotCall('_propagate() should not be called');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

8 participants
You can’t perform that action at this time.