Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Commit

Permalink
Fix recursion error when fetching auth chain over federation (#7817)
Browse files Browse the repository at this point in the history
When fetching the state of a room over federation we receive the event
IDs of the state and auth chain. We then fetch those events that we
don't already have.

However, we used a function that recursively fetched any missing auth
events for the fetched events, which can lead to a lot of recursion if
the server is missing most of the auth chain. This work is entirely
pointless because would have queued up the missing events in the auth
chain to be fetched already.

Let's just diable the recursion, since it only gets called from one
place anyway.
  • Loading branch information
erikjohnston committed Jul 10, 2020
1 parent 4372678 commit e29c443
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 16 deletions.
1 change: 1 addition & 0 deletions changelog.d/7817.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix bug where Synapse fails to process an incoming event over federation if the server is missing too much of the event's auth chain.
10 changes: 6 additions & 4 deletions synapse/event_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,16 @@ def check(

room_id = event.room_id

# I'm not really expecting to get auth events in the wrong room, but let's
# sanity-check it
# We need to ensure that the auth events are actually for the same room, to
# stop people from using powers they've been granted in other rooms for
# example.
for auth_event in auth_events.values():
if auth_event.room_id != room_id:
raise Exception(
raise AuthError(
403,
"During auth for event %s in room %s, found event %s in the state "
"which is in room %s"
% (event.event_id, room_id, auth_event.event_id, auth_event.room_id)
% (event.event_id, room_id, auth_event.event_id, auth_event.room_id),
)

if do_sig_check:
Expand Down
49 changes: 37 additions & 12 deletions synapse/handlers/federation.py
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,11 @@ async def _get_events_from_store_or_dest(
will be omitted from the result. Likewise, any events which turn out not to
be in the given room.
This function *does not* automatically get missing auth events of the
newly fetched events. Callers must include the full auth chain of
of the missing events in the `event_ids` argument, to ensure that any
missing auth events are correctly fetched.
Returns:
map from event_id to event
"""
Expand Down Expand Up @@ -1131,12 +1136,16 @@ async def _get_events_and_persist(
):
"""Fetch the given events from a server, and persist them as outliers.
This function *does not* recursively get missing auth events of the
newly fetched events. Callers must include in the `events` argument
any missing events from the auth chain.
Logs a warning if we can't find the given event.
"""

room_version = await self.store.get_room_version(room_id)

event_infos = []
event_map = {} # type: Dict[str, EventBase]

async def get_event(event_id: str):
with nested_logging_context(event_id):
Expand All @@ -1150,17 +1159,7 @@ async def get_event(event_id: str):
)
return

# recursively fetch the auth events for this event
auth_events = await self._get_events_from_store_or_dest(
destination, room_id, event.auth_event_ids()
)
auth = {}
for auth_event_id in event.auth_event_ids():
ae = auth_events.get(auth_event_id)
if ae:
auth[(ae.type, ae.state_key)] = ae

event_infos.append(_NewEventInfo(event, None, auth))
event_map[event.event_id] = event

except Exception as e:
logger.warning(
Expand All @@ -1172,6 +1171,32 @@ async def get_event(event_id: str):

await concurrently_execute(get_event, events, 5)

# Make a map of auth events for each event. We do this after fetching
# all the events as some of the events' auth events will be in the list
# of requested events.

auth_events = [
aid
for event in event_map.values()
for aid in event.auth_event_ids()
if aid not in event_map
]
persisted_events = await self.store.get_events(
auth_events, allow_rejected=True,
)

event_infos = []
for event in event_map.values():
auth = {}
for auth_event_id in event.auth_event_ids():
ae = persisted_events.get(auth_event_id) or event_map.get(auth_event_id)
if ae:
auth[(ae.type, ae.state_key)] = ae
else:
logger.info("Missing auth event %s", auth_event_id)

event_infos.append(_NewEventInfo(event, None, auth))

await self._handle_new_events(
destination, event_infos,
)
Expand Down

0 comments on commit e29c443

Please sign in to comment.