Skip to content

Commit f68fc4c

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 ef1eb4c commit f68fc4c

2 files changed

Lines changed: 15 additions & 8 deletions

File tree

services/inputflinger/dispatcher/InputDispatcher.cpp

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

61016101
bool restartEvent;
61026102
if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) {
6103-
KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry->eventEntry));
6104-
restartEvent =
6105-
afterKeyEventLockedInterruptable(connection, dispatchEntry, keyEntry, handled);
6103+
restartEvent = afterKeyEventLockedInterruptable(connection, dispatchEntry, handled);
61066104
} else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) {
61076105
MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry->eventEntry));
61086106
restartEvent = afterMotionEventLockedInterruptable(connection, dispatchEntry, motionEntry,
@@ -6320,8 +6318,17 @@ void InputDispatcher::processConnectionResponsiveLocked(const Connection& connec
63206318
}
63216319

63226320
bool InputDispatcher::afterKeyEventLockedInterruptable(
6323-
const std::shared_ptr<Connection>& connection, DispatchEntry* dispatchEntry,
6324-
KeyEntry& keyEntry, bool handled) {
6321+
const std::shared_ptr<Connection>& connection, DispatchEntry* dispatchEntry, bool handled) {
6322+
// The dispatchEntry is currently valid, but it might point to a deleted object after we release
6323+
// the lock. For simplicity, make copies of the data of interest here and assume that
6324+
// 'dispatchEntry' is not valid after this section.
6325+
// Hold a strong reference to the EventEntry to ensure it's valid for the duration of this
6326+
// function, even if the DispatchEntry gets destroyed and releases its share of the ownership.
6327+
std::shared_ptr<EventEntry> eventEntry = dispatchEntry->eventEntry;
6328+
const bool hasForegroundTarget = dispatchEntry->hasForegroundTarget();
6329+
KeyEntry& keyEntry = static_cast<KeyEntry&>(*(eventEntry));
6330+
// To prevent misuse, ensure dispatchEntry is no longer valid.
6331+
dispatchEntry = nullptr;
63256332
if (keyEntry.flags & AKEY_EVENT_FLAG_FALLBACK) {
63266333
if (!handled) {
63276334
// Report the key as unhandled, since the fallback was not handled.
@@ -6338,7 +6345,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptable(
63386345
connection->inputState.removeFallbackKey(originalKeyCode);
63396346
}
63406347

6341-
if (handled || !dispatchEntry->hasForegroundTarget()) {
6348+
if (handled || !hasForegroundTarget) {
63426349
// If the application handles the original key for which we previously
63436350
// generated a fallback or if the window is not a foreground window,
63446351
// then cancel the associated fallback key, if any.

services/inputflinger/dispatcher/InputDispatcher.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -684,8 +684,8 @@ class InputDispatcher : public android::InputDispatcherInterface {
684684
void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
685685
REQUIRES(mLock);
686686
bool afterKeyEventLockedInterruptable(const std::shared_ptr<Connection>& connection,
687-
DispatchEntry* dispatchEntry, KeyEntry& keyEntry,
688-
bool handled) REQUIRES(mLock);
687+
DispatchEntry* dispatchEntry, bool handled)
688+
REQUIRES(mLock);
689689
bool afterMotionEventLockedInterruptable(const std::shared_ptr<Connection>& connection,
690690
DispatchEntry* dispatchEntry, MotionEntry& motionEntry,
691691
bool handled) REQUIRES(mLock);

0 commit comments

Comments
 (0)