Commit 51452545 authored by John Kleinschmidt's avatar John Kleinschmidt Committed by Commit Bot

fix heap-use-after-free when aborting an animation

Under certain circumstances, such as aborting an animation and then
reassigning a unique_ptr to a new CallbackLayerAnimationObserver (eg
InkDropRipple::SnapToState(InkDropState ink_drop_state), the logic to
detect deletion in CallbackLayerAnimationObserver ends up causing a
heap-use-after-free because
CallbackLayerAnimationObserver::CheckAllSequencesCompleted() overwrites
the destroyed_ pointer that CallbackLayerAnimationObserver::SetActive() set.

In order to fix this issue, use of the destroyed_ pointer was replaced
with local WeakPtrs in order to detect deletion.

Change-Id: I051657fae929eb4d7200f965561e9d9fb21b44f5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1714626Reviewed-by: default avatarkylechar <kylechar@chromium.org>
Commit-Queue: Jeremy Apthorp <jeremya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#683029}
parent 625d0a90
...@@ -444,6 +444,7 @@ Joe Knoll <joe.knoll@workday.com> ...@@ -444,6 +444,7 @@ Joe Knoll <joe.knoll@workday.com>
Joe Thomas <mhx348@motorola.com> Joe Thomas <mhx348@motorola.com>
Joel Stanley <joel@jms.id.au> Joel Stanley <joel@jms.id.au>
Johannes Rudolph <johannes.rudolph@googlemail.com> Johannes Rudolph <johannes.rudolph@googlemail.com>
John Kleinschmidt <kleinschmidtorama@gmail.com>
John Yani <vanuan@gmail.com> John Yani <vanuan@gmail.com>
John Yoo <nearbyh13@gmail.com> John Yoo <nearbyh13@gmail.com>
Johnson Lin <johnson.lin@intel.com> Johnson Lin <johnson.lin@intel.com>
......
...@@ -38,22 +38,18 @@ CallbackLayerAnimationObserver::CallbackLayerAnimationObserver( ...@@ -38,22 +38,18 @@ CallbackLayerAnimationObserver::CallbackLayerAnimationObserver(
&CallbackLayerAnimationObserver::DummyAnimationStartedCallback)), &CallbackLayerAnimationObserver::DummyAnimationStartedCallback)),
animation_ended_callback_(animation_ended_callback) {} animation_ended_callback_(animation_ended_callback) {}
CallbackLayerAnimationObserver::~CallbackLayerAnimationObserver() { CallbackLayerAnimationObserver::~CallbackLayerAnimationObserver() {}
if (destroyed_)
*destroyed_ = true;
}
void CallbackLayerAnimationObserver::SetActive() { void CallbackLayerAnimationObserver::SetActive() {
active_ = true; active_ = true;
bool destroyed = false; base::WeakPtr<CallbackLayerAnimationObserver> weak_this =
destroyed_ = &destroyed; weak_factory_.GetWeakPtr();
CheckAllSequencesStarted(); CheckAllSequencesStarted();
if (destroyed) if (!weak_this)
return; return;
destroyed_ = nullptr;
CheckAllSequencesCompleted(); CheckAllSequencesCompleted();
} }
...@@ -110,19 +106,17 @@ void CallbackLayerAnimationObserver::CheckAllSequencesStarted() { ...@@ -110,19 +106,17 @@ void CallbackLayerAnimationObserver::CheckAllSequencesStarted() {
void CallbackLayerAnimationObserver::CheckAllSequencesCompleted() { void CallbackLayerAnimationObserver::CheckAllSequencesCompleted() {
if (active_ && GetNumSequencesCompleted() == attached_sequence_count_) { if (active_ && GetNumSequencesCompleted() == attached_sequence_count_) {
active_ = false; active_ = false;
bool destroyed = false; base::WeakPtr<CallbackLayerAnimationObserver> weak_this =
destroyed_ = &destroyed; weak_factory_.GetWeakPtr();
bool should_delete = animation_ended_callback_.Run(*this); bool should_delete = animation_ended_callback_.Run(*this);
if (destroyed) { if (!weak_this) {
if (should_delete) if (should_delete)
LOG(WARNING) << "CallbackLayerAnimationObserver was explicitly " LOG(WARNING) << "CallbackLayerAnimationObserver was explicitly "
"destroyed AND was requested to be destroyed via the " "destroyed AND was requested to be destroyed via the "
"AnimationEndedCallback's return value."; "AnimationEndedCallback's return value.";
return; return;
} }
destroyed_ = nullptr;
if (should_delete) if (should_delete)
delete this; delete this;
......
...@@ -167,9 +167,8 @@ class COMPOSITOR_EXPORT CallbackLayerAnimationObserver ...@@ -167,9 +167,8 @@ class COMPOSITOR_EXPORT CallbackLayerAnimationObserver
// The callback to invoke once all the animation sequences have finished. // The callback to invoke once all the animation sequences have finished.
AnimationEndedCallback animation_ended_callback_; AnimationEndedCallback animation_ended_callback_;
// Set to true in the destructor (if non-NULL). Used to detect deletion while // Used to detect deletion while calling out.
// calling out. base::WeakPtrFactory<CallbackLayerAnimationObserver> weak_factory_{this};
bool* destroyed_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(CallbackLayerAnimationObserver); DISALLOW_COPY_AND_ASSIGN(CallbackLayerAnimationObserver);
}; };
......
...@@ -256,6 +256,47 @@ CallbackLayerAnimationObserverTest::CreateLayerAnimationSequence() { ...@@ -256,6 +256,47 @@ CallbackLayerAnimationObserverTest::CreateLayerAnimationSequence() {
return sequences_.back().get(); return sequences_.back().get();
} }
class CallbackLayerAnimationObserverTestOverwrite
: public CallbackLayerAnimationObserverTest {
public:
CallbackLayerAnimationObserverTestOverwrite();
protected:
void AnimationStarted(const CallbackLayerAnimationObserver& observer);
std::unique_ptr<CallbackLayerAnimationObserver> CreateAnimationObserver();
private:
DISALLOW_COPY_AND_ASSIGN(CallbackLayerAnimationObserverTestOverwrite);
};
CallbackLayerAnimationObserverTestOverwrite::
CallbackLayerAnimationObserverTestOverwrite() {
observer_ = CreateAnimationObserver();
observer_test_api_ =
std::make_unique<LayerAnimationObserverTestApi>(observer_.get());
}
void CallbackLayerAnimationObserverTestOverwrite::AnimationStarted(
const CallbackLayerAnimationObserver& observer) {
observer_->OnLayerAnimationAborted(sequences_.front().get());
observer_test_api_.reset();
// Replace the current observer with a new observer so that the destructor
// gets called on the current observer.
observer_ = CreateAnimationObserver();
}
std::unique_ptr<CallbackLayerAnimationObserver>
CallbackLayerAnimationObserverTestOverwrite::CreateAnimationObserver() {
return std::make_unique<CallbackLayerAnimationObserver>(
base::BindRepeating(
&CallbackLayerAnimationObserverTestOverwrite::AnimationStarted,
base::Unretained(this)),
base::BindRepeating([](const CallbackLayerAnimationObserver& observer) {
return false;
}));
}
TEST(CallbackLayerAnimationObserverDestructionTest, VerifyFalseAutoDelete) { TEST(CallbackLayerAnimationObserverDestructionTest, VerifyFalseAutoDelete) {
TestCallbacks callbacks; TestCallbacks callbacks;
callbacks.set_should_delete_observer_on_animations_ended(false); callbacks.set_should_delete_observer_on_animations_ended(false);
...@@ -327,6 +368,23 @@ TEST(CallbackLayerAnimationObserverDestructionTest, AnimationEndedReturnsTrue) { ...@@ -327,6 +368,23 @@ TEST(CallbackLayerAnimationObserverDestructionTest, AnimationEndedReturnsTrue) {
EXPECT_TRUE(is_destroyed); EXPECT_TRUE(is_destroyed);
} }
// Verifies that there are not heap-use-after-free errors when an observer has
// its animation aborted and it gets destroyed due to a
// unique_ptr<CallbackLayerAnimationObserver> being assigned a new value.
TEST_F(CallbackLayerAnimationObserverTestOverwrite,
VerifyOverwriteOnAnimationStart) {
LayerAnimationSequence* sequence_1 = CreateLayerAnimationSequence();
LayerAnimationSequence* sequence_2 = CreateLayerAnimationSequence();
observer_test_api_->AttachedToSequence(sequence_1);
observer_test_api_->AttachedToSequence(sequence_2);
observer_->OnLayerAnimationStarted(sequence_1);
observer_->OnLayerAnimationStarted(sequence_2);
observer_->OnLayerAnimationEnded(sequence_1);
observer_->SetActive();
EXPECT_FALSE(observer_->active());
}
TEST_F(CallbackLayerAnimationObserverTest, VerifyInitialState) { TEST_F(CallbackLayerAnimationObserverTest, VerifyInitialState) {
EXPECT_FALSE(observer_->active()); EXPECT_FALSE(observer_->active());
EXPECT_EQ(0, observer_->aborted_count()); EXPECT_EQ(0, observer_->aborted_count());
......
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