Skip to content

Commit a219a97

Browse files
authored
Fix a data race that could manifest as null pointer dereference in FutureBase::Release() (#747)
1 parent 797c546 commit a219a97

File tree

3 files changed

+54
-13
lines changed

3 files changed

+54
-13
lines changed

app/src/include/firebase/future.h

+8-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <stddef.h>
2121
#include <stdint.h>
2222

23+
#include <mutex>
2324
#include <utility>
2425

2526
#include "firebase/internal/common.h"
@@ -309,6 +310,7 @@ class FutureBase {
309310

310311
/// Returns true if the two Futures reference the same result.
311312
bool operator==(const FutureBase& rhs) const {
313+
std::lock_guard<std::mutex> lock(mutex_);
312314
return api_ == rhs.api_ && handle_ == rhs.handle_;
313315
}
314316

@@ -317,12 +319,17 @@ class FutureBase {
317319

318320
#if defined(INTERNAL_EXPERIMENTAL)
319321
/// Returns the API-specific handle. Should only be called by the API.
320-
const FutureHandle& GetHandle() const { return handle_; }
322+
FutureHandle GetHandle() const {
323+
std::lock_guard<std::mutex> lock(mutex_);
324+
return handle_;
325+
}
321326
#endif // defined(INTERNAL_EXPERIMENTAL)
322327

323328
protected:
324329
/// @cond FIREBASE_APP_INTERNAL
325330

331+
mutable std::mutex mutex_;
332+
326333
/// Backpointer to the issuing API class.
327334
/// Set to nullptr when Future is invalidated.
328335
detail::FutureApiInterface* api_;

app/src/include/firebase/internal/future_impl.h

+43-12
Original file line numberDiff line numberDiff line change
@@ -207,41 +207,63 @@ inline FutureBase::FutureBase(const FutureBase& rhs)
207207
: api_(NULL) // NOLINT
208208
{ // NOLINT
209209
*this = rhs;
210-
detail::RegisterForCleanup(api_, this);
211210
}
212211

213212
inline FutureBase& FutureBase::operator=(const FutureBase& rhs) {
214213
Release();
215-
api_ = rhs.api_;
216-
handle_ = rhs.handle_;
217-
if (api_ != NULL) { // NOLINT
218-
api_->ReferenceFuture(handle_);
214+
215+
detail::FutureApiInterface* new_api;
216+
FutureHandle new_handle;
217+
{
218+
std::lock_guard<std::mutex> lock(rhs.mutex_);
219+
new_api = rhs.api_;
220+
new_handle = rhs.handle_;
219221
}
220-
detail::RegisterForCleanup(api_, this);
222+
223+
{
224+
std::lock_guard<std::mutex> lock(mutex_);
225+
api_ = new_api;
226+
handle_ = new_handle;
227+
228+
if (api_ != NULL) { // NOLINT
229+
api_->ReferenceFuture(handle_);
230+
}
231+
detail::RegisterForCleanup(api_, this);
232+
}
233+
221234
return *this;
222235
}
223236

224237
#if defined(FIREBASE_USE_MOVE_OPERATORS)
225238
inline FutureBase::FutureBase(FutureBase&& rhs) noexcept
226239
: api_(NULL) // NOLINT
227240
{
228-
detail::UnregisterForCleanup(rhs.api_, &rhs);
229241
*this = std::move(rhs);
230-
detail::RegisterForCleanup(api_, this);
231242
}
232243

233244
inline FutureBase& FutureBase::operator=(FutureBase&& rhs) noexcept {
234245
Release();
235-
detail::UnregisterForCleanup(rhs.api_, &rhs);
236-
api_ = rhs.api_;
237-
handle_ = rhs.handle_;
238-
rhs.api_ = NULL; // NOLINT
246+
247+
detail::FutureApiInterface* new_api;
248+
FutureHandle new_handle;
249+
{
250+
std::lock_guard<std::mutex> lock(rhs.mutex_);
251+
detail::UnregisterForCleanup(rhs.api_, &rhs);
252+
new_api = rhs.api_;
253+
new_handle = rhs.handle_;
254+
rhs.api_ = NULL; // NOLINT
255+
}
256+
257+
std::lock_guard<std::mutex> lock(mutex_);
258+
api_ = new_api;
259+
handle_ = new_handle;
239260
detail::RegisterForCleanup(api_, this);
240261
return *this;
241262
}
242263
#endif // defined(FIREBASE_USE_MOVE_OPERATORS)
243264

244265
inline void FutureBase::Release() {
266+
std::lock_guard<std::mutex> lock(mutex_);
245267
if (api_ != NULL) { // NOLINT
246268
detail::UnregisterForCleanup(api_, this);
247269
api_->ReleaseFuture(handle_);
@@ -250,25 +272,30 @@ inline void FutureBase::Release() {
250272
}
251273

252274
inline FutureStatus FutureBase::status() const {
275+
std::lock_guard<std::mutex> lock(mutex_);
253276
return api_ == NULL ? // NOLINT
254277
kFutureStatusInvalid
255278
: api_->GetFutureStatus(handle_);
256279
}
257280

258281
inline int FutureBase::error() const {
282+
std::lock_guard<std::mutex> lock(mutex_);
259283
return api_ == NULL ? -1 : api_->GetFutureError(handle_); // NOLINT
260284
}
261285

262286
inline const char* FutureBase::error_message() const {
287+
std::lock_guard<std::mutex> lock(mutex_);
263288
return api_ == NULL ? NULL : api_->GetFutureErrorMessage(handle_); // NOLINT
264289
}
265290

266291
inline const void* FutureBase::result_void() const {
292+
std::lock_guard<std::mutex> lock(mutex_);
267293
return api_ == NULL ? NULL : api_->GetFutureResult(handle_); // NOLINT
268294
}
269295

270296
inline void FutureBase::OnCompletion(CompletionCallback callback,
271297
void* user_data) const {
298+
std::lock_guard<std::mutex> lock(mutex_);
272299
if (api_ != NULL) { // NOLINT
273300
api_->AddCompletionCallback(handle_, callback, user_data, nullptr,
274301
/*clear_existing_callbacks=*/true);
@@ -278,6 +305,7 @@ inline void FutureBase::OnCompletion(CompletionCallback callback,
278305
#if defined(INTERNAL_EXPERIMENTAL)
279306
inline FutureBase::CompletionCallbackHandle FutureBase::AddOnCompletion(
280307
CompletionCallback callback, void* user_data) const {
308+
std::lock_guard<std::mutex> lock(mutex_);
281309
if (api_ != NULL) { // NOLINT
282310
return api_->AddCompletionCallback(handle_, callback, user_data, nullptr,
283311
/*clear_existing_callbacks=*/false);
@@ -287,6 +315,7 @@ inline FutureBase::CompletionCallbackHandle FutureBase::AddOnCompletion(
287315

288316
inline void FutureBase::RemoveOnCompletion(
289317
CompletionCallbackHandle completion_handle) const {
318+
std::lock_guard<std::mutex> lock(mutex_);
290319
if (api_ != NULL) { // NOLINT
291320
api_->RemoveCompletionCallback(handle_, completion_handle);
292321
}
@@ -296,6 +325,7 @@ inline void FutureBase::RemoveOnCompletion(
296325
#if defined(FIREBASE_USE_STD_FUNCTION)
297326
inline void FutureBase::OnCompletion(
298327
std::function<void(const FutureBase&)> callback) const {
328+
std::lock_guard<std::mutex> lock(mutex_);
299329
if (api_ != NULL) { // NOLINT
300330
api_->AddCompletionCallbackLambda(handle_, callback,
301331
/*clear_existing_callbacks=*/true);
@@ -305,6 +335,7 @@ inline void FutureBase::OnCompletion(
305335
#if defined(INTERNAL_EXPERIMENTAL)
306336
inline FutureBase::CompletionCallbackHandle FutureBase::AddOnCompletion(
307337
std::function<void(const FutureBase&)> callback) const {
338+
std::lock_guard<std::mutex> lock(mutex_);
308339
if (api_ != NULL) { // NOLINT
309340
return api_->AddCompletionCallbackLambda(
310341
handle_, callback,

release_build_files/readme.md

+3
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,9 @@ code.
569569
## Release Notes
570570
### Next Release
571571
- Changes
572+
- General (Android): Fixed a data race that could manifest as null pointer
573+
dereference in `FutureBase::Release()`.
574+
([#747](https://github.com/firebase/firebase-cpp-sdk/pull/747))
572575
- Auth (Desktop): Fixed a crash in `error_code()` when a request
573576
is cancelled or times out.
574577
([#737](https://github.com/firebase/firebase-cpp-sdk/issues/737))

0 commit comments

Comments
 (0)