diff options
author | Anna Henningsen <anna@addaleax.net> | 2020-05-07 02:30:36 +0300 |
---|---|---|
committer | Anna Henningsen <anna@addaleax.net> | 2020-05-15 20:36:48 +0300 |
commit | e9f293750760d59243020d0376edf242c9a26b67 (patch) | |
tree | bfa905e137940a3d530b1008b5b0327f0bd22f7a /src/node_dir.cc | |
parent | 441e703b2851be4c0f924907ff3c20cc4bab8588 (diff) |
fs: clean up Dir.read() uv_fs_t data before calling into JS
A call into JS can schedule another operation on the same `uv_dir_t`.
In particular, when the handle is closed from the callback for a
directory read operation, there previously was a race condition window:
1. A `dir.read()` operation is submitted to libuv
2. The read operation is finished by libuv, calling `AfterDirRead()`
3. We call into JS
4. JS calls dir.close()
5. libuv posts the close request to a thread in the pool
6. The close request runs, destroying the directory handle
7. `AfterDirRead()` is being exited.
Exiting the `FSReqAfterScope` in step 7 attempts to destroy the original
uv_fs_t` from step 1, which now points to an `uv_dir_t` that has
already been destroyed in step 5.
By forcing the `FSReqAfterScope` to clean up before we call into JS,
we can be sure that no other operations on the same `uv_dir_t` are
submitted concurrently.
This addresses issues observed when running with ASAN/valgrind.
PR-URL: https://github.com/nodejs/node/pull/33274
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Diffstat (limited to 'src/node_dir.cc')
-rw-r--r-- | src/node_dir.cc | 10 |
1 files changed, 7 insertions, 3 deletions
diff --git a/src/node_dir.cc b/src/node_dir.cc index c4aaf4bcd3e..b0904c3d39d 100644 --- a/src/node_dir.cc +++ b/src/node_dir.cc @@ -197,8 +197,8 @@ static MaybeLocal<Array> DirentListToArray( } static void AfterDirRead(uv_fs_t* req) { - FSReqBase* req_wrap = FSReqBase::from_req(req); - FSReqAfterScope after(req_wrap, req); + BaseObjectPtr<FSReqBase> req_wrap { FSReqBase::from_req(req) }; + FSReqAfterScope after(req_wrap.get(), req); if (!after.Proceed()) { return; @@ -210,12 +210,12 @@ static void AfterDirRead(uv_fs_t* req) { if (req->result == 0) { // Done Local<Value> done = Null(isolate); + after.Clear(); req_wrap->Resolve(done); return; } uv_dir_t* dir = static_cast<uv_dir_t*>(req->ptr); - req->ptr = nullptr; Local<Value> error; Local<Array> js_array; @@ -224,9 +224,13 @@ static void AfterDirRead(uv_fs_t* req) { req->result, req_wrap->encoding(), &error).ToLocal(&js_array)) { + // Clear libuv resources *before* delivering results to JS land because + // that can schedule another operation on the same uv_dir_t. Ditto below. + after.Clear(); return req_wrap->Reject(error); } + after.Clear(); req_wrap->Resolve(js_array); } |