Skip to content

Commit 7d7ac48

Browse files
committed
Ensure objects remain valid after calling policy
The function afterKeyEventLockedInterruptable releases the lock and calls into policy. During this time, the call to "removeInputChannel" might come in. This call would cause the waitQueue to be drained. Therefore, the dispatchEntry that's stored in this queue would be deleted. Before this CL, we obtained a reference to the EventEntry object before calling policy. If there aren't any more strong pointers remaining to the EventEntry, the object would become deleted, and the reference would end up pointing to freed memory. Previous flow of events: - KeyEntry is allocated during setFocusedWindow call, as part of "synthesizeCancelationEvents". - App calls "finish" on an event, and dispatcher notifies policy about the unhandled key event. But dispatcher must release lock before calling policy. - After dispatcher has released the lock, but before it called policy, there is a binder call to "removeInputChannel" that comes in. That causes the waitQueue to be drained, and deletes the DispatchEntry. If the dispatch entry is the last remaining reference to the KeyEntry, then the KeyEntry gets deleted, as well. - The dispatcher calls policy, and uses the reference to the KeyEntry that it was provided. But that reference points to freed memory. This causes a crash. To deal with this, make a few changes in this CL: - Since the "doDispatchCycleFinishedCommand" is stored in queue, it should have a strong pointer to the connection object, and not just a reference. That means the Connection object will be valid when the command actually runs (otherwise, someone might delete it) - Inside afterKeyEventLockedInterruptable, assume that the dispatchEntry will be deleted after the lock is released. Make copies of the data that we need after the lock is regained: 1) Add refcount for EventEntry 2) Store the "hasForegroundTarget" into a separate variable (technically, it's not necessary, but it allows us to remove all usages of "dispatchEntry" in the rest of the function. As an alternative, we could re-look up the DispatchEntry in the waitQueue after we regain the lock, but that seems more complex in terms of implementation / readability. Bug: 343129193 Test: atest --host inputflinger_tests Merged-In: Ibea7117e4c85cd1e98bbd01872ce249cbb2d54bd Change-Id: Ibea7117e4c85cd1e98bbd01872ce249cbb2d54bd
1 parent db00dfa commit 7d7ac48

2 files changed

Lines changed: 14 additions & 6 deletions

File tree

services/inputflinger/dispatcher/InputDispatcher.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5730,9 +5730,7 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime,
57305730

57315731
bool restartEvent;
57325732
if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) {
5733-
KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry->eventEntry));
5734-
restartEvent =
5735-
afterKeyEventLockedInterruptable(connection, dispatchEntry, keyEntry, handled);
5733+
restartEvent = afterKeyEventLockedInterruptable(connection, dispatchEntry, handled);
57365734
} else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) {
57375735
MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry->eventEntry));
57385736
restartEvent = afterMotionEventLockedInterruptable(connection, dispatchEntry, motionEntry,
@@ -5960,7 +5958,17 @@ void InputDispatcher::processConnectionResponsiveLocked(const Connection& connec
59605958

59615959
bool InputDispatcher::afterKeyEventLockedInterruptable(const sp<Connection>& connection,
59625960
DispatchEntry* dispatchEntry,
5963-
KeyEntry& keyEntry, bool handled) {
5961+
bool handled) {
5962+
// The dispatchEntry is currently valid, but it might point to a deleted object after we release
5963+
// the lock. For simplicity, make copies of the data of interest here and assume that
5964+
// 'dispatchEntry' is not valid after this section.
5965+
// Hold a strong reference to the EventEntry to ensure it's valid for the duration of this
5966+
// function, even if the DispatchEntry gets destroyed and releases its share of the ownership.
5967+
std::shared_ptr<EventEntry> eventEntry = dispatchEntry->eventEntry;
5968+
const bool hasForegroundTarget = dispatchEntry->hasForegroundTarget();
5969+
KeyEntry& keyEntry = static_cast<KeyEntry&>(*(eventEntry));
5970+
// To prevent misuse, ensure dispatchEntry is no longer valid.
5971+
dispatchEntry = nullptr;
59645972
if (keyEntry.flags & AKEY_EVENT_FLAG_FALLBACK) {
59655973
if (!handled) {
59665974
// Report the key as unhandled, since the fallback was not handled.
@@ -5977,7 +5985,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptable(const sp<Connection>& con
59775985
connection->inputState.removeFallbackKey(originalKeyCode);
59785986
}
59795987

5980-
if (handled || !dispatchEntry->hasForegroundTarget()) {
5988+
if (handled || !hasForegroundTarget) {
59815989
// If the application handles the original key for which we previously
59825990
// generated a fallback or if the window is not a foreground window,
59835991
// then cancel the associated fallback key, if any.

services/inputflinger/dispatcher/InputDispatcher.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -662,7 +662,7 @@ class InputDispatcher : public android::InputDispatcherInterface {
662662
void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
663663
REQUIRES(mLock);
664664
bool afterKeyEventLockedInterruptable(const sp<Connection>& connection,
665-
DispatchEntry* dispatchEntry, KeyEntry& keyEntry,
665+
DispatchEntry* dispatchEntry,
666666
bool handled) REQUIRES(mLock);
667667
bool afterMotionEventLockedInterruptable(const sp<Connection>& connection,
668668
DispatchEntry* dispatchEntry, MotionEntry& motionEntry,

0 commit comments

Comments
 (0)