Skip to content

Commit aa4611c

Browse files
addaleaxcodebytere
authored andcommittedJun 9, 2020
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: #33274 Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
1 parent d2897a2 commit aa4611c

File tree

3 files changed

+27
-11
lines changed

3 files changed

+27
-11
lines changed
 

‎src/node_dir.cc

+7-3
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,8 @@ static MaybeLocal<Array> DirentListToArray(
197197
}
198198

199199
static void AfterDirRead(uv_fs_t* req) {
200-
FSReqBase* req_wrap = FSReqBase::from_req(req);
201-
FSReqAfterScope after(req_wrap, req);
200+
BaseObjectPtr<FSReqBase> req_wrap { FSReqBase::from_req(req) };
201+
FSReqAfterScope after(req_wrap.get(), req);
202202

203203
if (!after.Proceed()) {
204204
return;
@@ -210,12 +210,12 @@ static void AfterDirRead(uv_fs_t* req) {
210210
if (req->result == 0) {
211211
// Done
212212
Local<Value> done = Null(isolate);
213+
after.Clear();
213214
req_wrap->Resolve(done);
214215
return;
215216
}
216217

217218
uv_dir_t* dir = static_cast<uv_dir_t*>(req->ptr);
218-
req->ptr = nullptr;
219219

220220
Local<Value> error;
221221
Local<Array> js_array;
@@ -224,9 +224,13 @@ static void AfterDirRead(uv_fs_t* req) {
224224
req->result,
225225
req_wrap->encoding(),
226226
&error).ToLocal(&js_array)) {
227+
// Clear libuv resources *before* delivering results to JS land because
228+
// that can schedule another operation on the same uv_dir_t. Ditto below.
229+
after.Clear();
227230
return req_wrap->Reject(error);
228231
}
229232

233+
after.Clear();
230234
req_wrap->Resolve(js_array);
231235
}
232236

‎src/node_file.cc

+18-7
Original file line numberDiff line numberDiff line change
@@ -540,8 +540,15 @@ FSReqAfterScope::FSReqAfterScope(FSReqBase* wrap, uv_fs_t* req)
540540
}
541541

542542
FSReqAfterScope::~FSReqAfterScope() {
543+
Clear();
544+
}
545+
546+
void FSReqAfterScope::Clear() {
547+
if (!wrap_) return;
548+
543549
uv_fs_req_cleanup(wrap_->req());
544-
delete wrap_;
550+
wrap_->Detach();
551+
wrap_.reset();
545552
}
546553

547554
// TODO(joyeecheung): create a normal context object, and
@@ -554,12 +561,16 @@ FSReqAfterScope::~FSReqAfterScope() {
554561
// which is also why the errors should have been constructed
555562
// in JS for more flexibility.
556563
void FSReqAfterScope::Reject(uv_fs_t* req) {
557-
wrap_->Reject(UVException(wrap_->env()->isolate(),
558-
req->result,
559-
wrap_->syscall(),
560-
nullptr,
561-
req->path,
562-
wrap_->data()));
564+
BaseObjectPtr<FSReqBase> wrap { wrap_ };
565+
Local<Value> exception =
566+
UVException(wrap_->env()->isolate(),
567+
req->result,
568+
wrap_->syscall(),
569+
nullptr,
570+
req->path,
571+
wrap_->data());
572+
Clear();
573+
wrap->Reject(exception);
563574
}
564575

565576
bool FSReqAfterScope::Proceed() {

‎src/node_file.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ class FSReqAfterScope final {
157157
public:
158158
FSReqAfterScope(FSReqBase* wrap, uv_fs_t* req);
159159
~FSReqAfterScope();
160+
void Clear();
160161

161162
bool Proceed();
162163

@@ -168,7 +169,7 @@ class FSReqAfterScope final {
168169
FSReqAfterScope& operator=(const FSReqAfterScope&&) = delete;
169170

170171
private:
171-
FSReqBase* wrap_ = nullptr;
172+
BaseObjectPtr<FSReqBase> wrap_;
172173
uv_fs_t* req_ = nullptr;
173174
v8::HandleScope handle_scope_;
174175
v8::Context::Scope context_scope_;

0 commit comments

Comments
 (0)
Please sign in to comment.