From 5d051b2da29cee2673ca039b64da6cf8ba00f335 Mon Sep 17 00:00:00 2001
From: Richard van der Hoff <richard@matrix.org>
Date: Mon, 20 Sep 2021 18:20:17 +0100
Subject: [PATCH] Stop trying to auth/persist events whose auth events we do
 not have.

---
 changelog.d/10907.bugfix             |  1 +
 synapse/handlers/federation_event.py | 24 ++++++++++++++++--------
 2 files changed, 17 insertions(+), 8 deletions(-)
 create mode 100644 changelog.d/10907.bugfix

diff --git a/changelog.d/10907.bugfix b/changelog.d/10907.bugfix
new file mode 100644
index 000000000000..601b341f9fa6
--- /dev/null
+++ b/changelog.d/10907.bugfix
@@ -0,0 +1 @@
+Fix a long-standing bug which could cause events pulled over federation to be incorrectly rejected.
diff --git a/synapse/handlers/federation_event.py b/synapse/handlers/federation_event.py
index 8fd9e51044d6..01fd84112252 100644
--- a/synapse/handlers/federation_event.py
+++ b/synapse/handlers/federation_event.py
@@ -1194,10 +1194,17 @@ async def prep(event: EventBase) -> Optional[Tuple[EventBase, EventContext]]:
                 auth = {}
                 for auth_event_id in event.auth_event_ids():
                     ae = persisted_events.get(auth_event_id)
-                    if ae:
-                        auth[(ae.type, ae.state_key)] = ae
-                    else:
-                        logger.info("Missing auth event %s", auth_event_id)
+                    if not ae:
+                        logger.warning(
+                            "Event %s relies on auth_event %s, which could not be found.",
+                            event,
+                            auth_event_id,
+                        )
+                        # the fact we can't find the auth event doesn't mean it doesn't
+                        # exist, which means it is premature to reject `event`. Instead we
+                        # just ignore it for now.
+                        return None
+                    auth[(ae.type, ae.state_key)] = ae
 
                 context = EventContext.for_outlier()
                 context = await self._check_event_auth(
@@ -1208,8 +1215,10 @@ async def prep(event: EventBase) -> Optional[Tuple[EventBase, EventContext]]:
                 )
             return event, context
 
-        events_to_persist = await yieldable_gather_results(prep, fetched_events)
-        await self.persist_events_and_notify(room_id, events_to_persist)
+        events_to_persist = (
+            x for x in await yieldable_gather_results(prep, fetched_events) if x
+        )
+        await self.persist_events_and_notify(room_id, tuple(events_to_persist))
 
     async def _check_event_auth(
         self,
@@ -1235,8 +1244,7 @@ async def _check_event_auth(
 
             claimed_auth_event_map:
                 A map of (type, state_key) => event for the event's claimed auth_events.
-                Possibly incomplete, and possibly including events that are not yet
-                persisted, or authed, or in the right room.
+                Possibly including events that were rejected, or are in the wrong room.
 
                 Only populated when populating outliers.