Commit 6966a907 authored by Sergey Kuznetsov's avatar Sergey Kuznetsov Committed by Commit Bot

Fix use-after-free bug if beginBackgroundTaskWithExpirationHandler returns UIBackgroundTaskInvalid

In this case UIApplication doesn't retain block, and at the end
of constructor ref_count of |this| will be decremented to zero ->
|this| will be deleted. Later deleted object will be accessed in
|core_(new ScopedCriticalAction::Core())|

The solution is to separate object construction and
beginBackgroundTaskWithExpirationHandler method call.

Change-Id: I0aa21cba2396231ea9ccd3ee34617f5d0417dbdd
Reviewed-on: https://chromium-review.googlesource.com/558245Reviewed-by: default avatarSylvain Defresne <sdefresne@chromium.org>
Commit-Queue: Sylvain Defresne <sdefresne@chromium.org>
Cr-Commit-Position: refs/heads/master@{#485582}
parent fa454a9e
...@@ -39,8 +39,12 @@ class ScopedCriticalAction { ...@@ -39,8 +39,12 @@ class ScopedCriticalAction {
public: public:
Core(); Core();
// Informs the OS that the background task has completed. // Informs the OS that the background task has started. This is a
void EndBackgroundTask(); // static method to ensure that the instance has a non-zero refcount.
static void StartBackgroundTask(scoped_refptr<Core> core);
// Informs the OS that the background task has completed. This is a
// static method to ensure that the instance has a non-zero refcount.
static void EndBackgroundTask(scoped_refptr<Core> core);
private: private:
friend base::RefCountedThreadSafe<Core>; friend base::RefCountedThreadSafe<Core>;
......
...@@ -14,46 +14,59 @@ namespace base { ...@@ -14,46 +14,59 @@ namespace base {
namespace ios { namespace ios {
ScopedCriticalAction::ScopedCriticalAction() ScopedCriticalAction::ScopedCriticalAction()
: core_(new ScopedCriticalAction::Core()) { : core_(base::MakeRefCounted<ScopedCriticalAction::Core>()) {
ScopedCriticalAction::Core::StartBackgroundTask(core_);
} }
ScopedCriticalAction::~ScopedCriticalAction() { ScopedCriticalAction::~ScopedCriticalAction() {
core_->EndBackgroundTask(); ScopedCriticalAction::Core::EndBackgroundTask(core_);
}
ScopedCriticalAction::Core::Core()
: background_task_id_(UIBackgroundTaskInvalid) {}
ScopedCriticalAction::Core::~Core() {
DCHECK_EQ(background_task_id_, UIBackgroundTaskInvalid);
} }
// This implementation calls |beginBackgroundTaskWithExpirationHandler:| when // This implementation calls |beginBackgroundTaskWithExpirationHandler:| when
// instantiated and |endBackgroundTask:| when destroyed, creating a scope whose // instantiated and |endBackgroundTask:| when destroyed, creating a scope whose
// execution will continue (temporarily) even after the app is backgrounded. // execution will continue (temporarily) even after the app is backgrounded.
ScopedCriticalAction::Core::Core() { // static
scoped_refptr<ScopedCriticalAction::Core> core = this; void ScopedCriticalAction::Core::StartBackgroundTask(scoped_refptr<Core> core) {
background_task_id_ = [[UIApplication sharedApplication] UIApplication* application = [UIApplication sharedApplication];
beginBackgroundTaskWithExpirationHandler:^{ if (!application) {
DLOG(WARNING) << "Background task with id " << background_task_id_ return;
}
core->background_task_id_ =
[application beginBackgroundTaskWithExpirationHandler:^{
DLOG(WARNING) << "Background task with id " << core->background_task_id_
<< " expired."; << " expired.";
// Note if |endBackgroundTask:| is not called for each task before time // Note if |endBackgroundTask:| is not called for each task before time
// expires, the system kills the application. // expires, the system kills the application.
core->EndBackgroundTask(); EndBackgroundTask(core);
}]; }];
if (background_task_id_ == UIBackgroundTaskInvalid) {
DLOG(WARNING) << if (core->background_task_id_ == UIBackgroundTaskInvalid) {
"beginBackgroundTaskWithExpirationHandler: returned an invalid ID"; DLOG(WARNING)
<< "beginBackgroundTaskWithExpirationHandler: returned an invalid ID";
} else { } else {
VLOG(3) << "Beginning background task with id " << background_task_id_; VLOG(3) << "Beginning background task with id "
<< core->background_task_id_;
} }
} }
ScopedCriticalAction::Core::~Core() { // static
DCHECK_EQ(background_task_id_, UIBackgroundTaskInvalid); void ScopedCriticalAction::Core::EndBackgroundTask(scoped_refptr<Core> core) {
}
void ScopedCriticalAction::Core::EndBackgroundTask() {
UIBackgroundTaskIdentifier task_id; UIBackgroundTaskIdentifier task_id;
{ {
AutoLock lock_scope(background_task_id_lock_); AutoLock lock_scope(core->background_task_id_lock_);
if (background_task_id_ == UIBackgroundTaskInvalid) if (core->background_task_id_ == UIBackgroundTaskInvalid) {
return; return;
task_id = background_task_id_; }
background_task_id_ = UIBackgroundTaskInvalid; task_id = core->background_task_id_;
core->background_task_id_ = UIBackgroundTaskInvalid;
} }
VLOG(3) << "Ending background task with id " << task_id; VLOG(3) << "Ending background task with id " << task_id;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment