Skip to content

Commit

Permalink
tls: ensure no synchronous callbacks
Browse files Browse the repository at this point in the history
Make sure that no WriteItem's callback will be invoked synchronously.
Doing so may lead to the use of uninitialized `req` object, or even
worse use-after-free in the caller code.

Fix: #1512
  • Loading branch information
indutny committed Apr 29, 2015
1 parent 5992187 commit df28a73
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 3 deletions.
30 changes: 28 additions & 2 deletions src/tls_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,38 @@ bool TLSWrap::InvokeQueued(int status) {
WriteItemList queue;
pending_write_items_.MoveBack(&queue);
while (WriteItem* wi = queue.PopFront()) {
wi->w_->Done(status);
if (wi->async_) {
wi->w_->Done(status);
} else {
CheckWriteItem* check = new CheckWriteItem(wi->w_, status);
int err = uv_check_init(env()->event_loop(), &check->check_);
check->check_.data = check;
if (err == 0)
err = uv_check_start(&check->check_, CheckWriteItem::CheckCb);

// No luck today, do it on next InvokeQueued
if (err) {
delete check;
pending_write_items_.PushBack(wi);
continue;
}
}
delete wi;
}

return true;
}


void TLSWrap::CheckWriteItem::CheckCb(uv_check_t* check) {
CheckWriteItem* c = reinterpret_cast<CheckWriteItem*>(check->data);

c->w_->Done(c->status_);
uv_close(reinterpret_cast<uv_handle_t*>(check), nullptr);
delete c;
}


void TLSWrap::NewSessionDoneCb() {
Cycle();
}
Expand Down Expand Up @@ -556,7 +580,9 @@ int TLSWrap::DoWrite(WriteWrap* w,
}

// Queue callback to execute it on next tick
write_item_queue_.PushBack(new WriteItem(w));
WriteItem* item = new WriteItem(w);
WriteItem::SyncScope item_async(item);
write_item_queue_.PushBack(item);
w->Dispatched();

// Write queued data
Expand Down
32 changes: 31 additions & 1 deletion src/tls_wrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,46 @@ class TLSWrap : public crypto::SSLWrap<TLSWrap>,
// Write callback queue's item
class WriteItem {
public:
explicit WriteItem(WriteWrap* w) : w_(w) {
class SyncScope {
public:
explicit SyncScope(WriteItem* item) : item_(item) {
item_->async_ = false;
}
~SyncScope() {
item_->async_ = true;
}

private:
WriteItem* item_;
};

explicit WriteItem(WriteWrap* w) : w_(w), async_(false) {
}
~WriteItem() {
w_ = nullptr;
}

WriteWrap* w_;
bool async_;
ListNode<WriteItem> member_;
};

class CheckWriteItem {
public:
CheckWriteItem(WriteWrap* w, int status) : w_(w), status_(status) {
}

~CheckWriteItem() {
w_ = nullptr;
}

static void CheckCb(uv_check_t* check);

WriteWrap* w_;
int status_;
uv_check_t check_;
};

TLSWrap(Environment* env,
Kind kind,
StreamBase* stream,
Expand Down

0 comments on commit df28a73

Please sign in to comment.