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

Check the room_id of events when fetching room state/auth #6524

Merged
merged 1 commit into from
Dec 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelog.d/6524.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Improve sanity-checking when receiving events over federation.

74 changes: 51 additions & 23 deletions synapse/handlers/federation.py
Original file line number Diff line number Diff line change
Expand Up @@ -637,42 +637,70 @@ async def _get_events_from_store_or_dest(
room_id
event_ids
If we fail to fetch any of the events, a warning will be logged, and the event
will be omitted from the result. Likewise, any events which turn out not to
be in the given room.
Returns:
map from event_id to event
"""
fetched_events = await self.store.get_events(event_ids, allow_rejected=True)

missing_events = set(event_ids) - fetched_events.keys()

if not missing_events:
return fetched_events
if missing_events:
logger.debug(
"Fetching unknown state/auth events %s for room %s",
missing_events,
room_id,
)

logger.debug(
"Fetching unknown state/auth events %s for room %s",
missing_events,
event_ids,
)
room_version = await self.store.get_room_version(room_id)

room_version = await self.store.get_room_version(room_id)
# XXX 20 requests at once? really?
for batch in batch_iter(missing_events, 20):
deferreds = [
run_in_background(
self.federation_client.get_pdu,
destinations=[destination],
event_id=e_id,
room_version=room_version,
)
for e_id in batch
]

# XXX 20 requests at once? really?
for batch in batch_iter(missing_events, 20):
deferreds = [
run_in_background(
self.federation_client.get_pdu,
destinations=[destination],
event_id=e_id,
room_version=room_version,
res = await make_deferred_yieldable(
defer.DeferredList(deferreds, consumeErrors=True)
)
for e_id in batch
]

res = await make_deferred_yieldable(
defer.DeferredList(deferreds, consumeErrors=True)
for success, result in res:
if success and result:
fetched_events[result.event_id] = result

# check for events which were in the wrong room.
#
# this can happen if a remote server claims that the state or
# auth_events at an event in room A are actually events in room B

bad_events = list(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bad_events = list(
bad_events = (

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, I'm deliberately building a list here rather than using a generator that refers to fetched_events: otherwise we're going to get errors about modifying fetched_events while iterating.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be cleaner to have used a list comprehension then rather than calling list on a generator expression?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you'll have to fight @hawkowl over that; I'm kinda ambivalent on the topic.

The reason to prefer this syntax (aiui) is for symmetry with constructing a tuple, where you have to invoke tuple explicitly to get a tuple rather than a generator.

In the case of creating a list: I'd imagine (I haven't checked) that the bytecode generated for list(x for x in foo) is the same as that for [x for x in foo] ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not - there is no way to guarantee that list refers to the list builtin, so it will always compile to a call to list.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interesting. thanks for the info.

(event_id, event.room_id)
for event_id, event in fetched_events.items()
if event.room_id != room_id
)

for bad_event_id, bad_room_id in bad_events:
# This is a bogus situation, but since we may only discover it a long time
# after it happened, we try our best to carry on, by just omitting the
# bad events from the returned auth/state set.
logger.warning(
"Remote server %s claims event %s in room %s is an auth/state "
"event in room %s",
destination,
bad_event_id,
bad_room_id,
room_id,
)
for success, result in res:
if success and result:
fetched_events[result.event_id] = result
del fetched_events[bad_event_id]

return fetched_events

Expand Down