From 0661fd6f7c66ae888b6fc253af6dd0514798eff5 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Fri, 26 Sep 2014 13:29:44 +0200 Subject: lib/nfs/FileReader: postpone the nfs_close_async() call If an async opertion is in progress, nfs_close_async() will make libnfs crash because the RPC callback will dereference an object that was freed by nfs_close_async(). --- src/lib/nfs/Connection.cxx | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) (limited to 'src/lib/nfs/Connection.cxx') diff --git a/src/lib/nfs/Connection.cxx b/src/lib/nfs/Connection.cxx index 853cacc9a..e5dc87727 100644 --- a/src/lib/nfs/Connection.cxx +++ b/src/lib/nfs/Connection.cxx @@ -80,10 +80,23 @@ NfsConnection::CancellableCallback::Read(nfs_context *ctx, struct nfsfh *fh, return true; } +inline void +NfsConnection::CancellableCallback::CancelAndScheduleClose(struct nfsfh *fh) +{ + assert(!open); + assert(close_fh == nullptr); + assert(fh != nullptr); + + close_fh = fh; + Cancel(); +} + inline void NfsConnection::CancellableCallback::Callback(int err, void *data) { if (!IsCancelled()) { + assert(close_fh == nullptr); + NfsCallback &cb = Get(); connection.callbacks.Remove(*this); @@ -98,9 +111,12 @@ NfsConnection::CancellableCallback::Callback(int err, void *data) /* a nfs_open_async() call was cancelled - to avoid a memory leak, close the newly allocated file handle immediately */ + assert(close_fh == nullptr); + struct nfsfh *fh = (struct nfsfh *)data; connection.Close(fh); - } + } else if (close_fh != nullptr) + connection.DeferClose(close_fh); connection.callbacks.Remove(*this); } @@ -135,6 +151,7 @@ NfsConnection::~NfsConnection() assert(new_leases.empty()); assert(active_leases.empty()); assert(callbacks.IsEmpty()); + assert(deferred_close.empty()); if (context != nullptr) DestroyContext(); @@ -224,6 +241,13 @@ NfsConnection::Close(struct nfsfh *fh) ScheduleSocket(); } +void +NfsConnection::CancelAndClose(struct nfsfh *fh, NfsCallback &callback) +{ + CancellableCallback &cancel = callbacks.Get(callback); + cancel.CancelAndScheduleClose(fh); +} + void NfsConnection::DestroyContext() { @@ -237,6 +261,15 @@ NfsConnection::DestroyContext() context = nullptr; } +inline void +NfsConnection::DeferClose(struct nfsfh *fh) +{ + assert(in_event); + assert(in_service); + + deferred_close.push_front(fh); +} + void NfsConnection::ScheduleSocket() { @@ -257,6 +290,8 @@ NfsConnection::ScheduleSocket() bool NfsConnection::OnSocketReady(unsigned flags) { + assert(deferred_close.empty()); + bool closed = false; const bool was_mounted = mount_finished; @@ -278,6 +313,12 @@ NfsConnection::OnSocketReady(unsigned flags) assert(in_service); in_service = false; + while (!deferred_close.empty()) { + nfs_close_async(context, deferred_close.front(), + DummyCallback, nullptr); + deferred_close.pop_front(); + } + if (!was_mounted && mount_finished) { if (postponed_mount_error.IsDefined()) { DestroyContext(); -- cgit v1.2.3