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

Commit 9ceae17

Browse files
Mathieu VeltenH-Shay
Mathieu Velten
authored andcommitted
Faster joins: filter out non local events when a room doesn't have its full state (#14404)
Signed-off-by: Mathieu Velten <[email protected]>
1 parent 9ac4501 commit 9ceae17

File tree

5 files changed

+43
-13
lines changed

5 files changed

+43
-13
lines changed

changelog.d/14404.misc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Faster joins: filter out non local events when a room doesn't have its full state.

synapse/federation/sender/per_destination_queue.py

+1
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,7 @@ async def _catch_up_transmission_loop(self) -> None:
505505
new_pdus = await filter_events_for_server(
506506
self._storage_controllers,
507507
self._destination,
508+
self._server_name,
508509
new_pdus,
509510
redact=False,
510511
)

synapse/handlers/federation.py

+10-5
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,7 @@ async def _maybe_backfill_inner(
379379
filtered_extremities = await filter_events_for_server(
380380
self._storage_controllers,
381381
self.server_name,
382+
self.server_name,
382383
events_to_check,
383384
redact=False,
384385
check_history_visibility_only=True,
@@ -1238,7 +1239,9 @@ async def get_state_ids_for_pdu(self, room_id: str, event_id: str) -> List[str]:
12381239
async def on_backfill_request(
12391240
self, origin: str, room_id: str, pdu_list: List[str], limit: int
12401241
) -> List[EventBase]:
1241-
await self._event_auth_handler.assert_host_in_room(room_id, origin)
1242+
# We allow partially joined rooms since in this case we are filtering out
1243+
# non-local events in `filter_events_for_server`.
1244+
await self._event_auth_handler.assert_host_in_room(room_id, origin, True)
12421245

12431246
# Synapse asks for 100 events per backfill request. Do not allow more.
12441247
limit = min(limit, 100)
@@ -1259,7 +1262,7 @@ async def on_backfill_request(
12591262
)
12601263

12611264
events = await filter_events_for_server(
1262-
self._storage_controllers, origin, events
1265+
self._storage_controllers, origin, self.server_name, events
12631266
)
12641267

12651268
return events
@@ -1290,7 +1293,7 @@ async def get_persisted_pdu(
12901293
await self._event_auth_handler.assert_host_in_room(event.room_id, origin)
12911294

12921295
events = await filter_events_for_server(
1293-
self._storage_controllers, origin, [event]
1296+
self._storage_controllers, origin, self.server_name, [event]
12941297
)
12951298
event = events[0]
12961299
return event
@@ -1303,7 +1306,9 @@ async def on_get_missing_events(
13031306
latest_events: List[str],
13041307
limit: int,
13051308
) -> List[EventBase]:
1306-
await self._event_auth_handler.assert_host_in_room(room_id, origin)
1309+
# We allow partially joined rooms since in this case we are filtering out
1310+
# non-local events in `filter_events_for_server`.
1311+
await self._event_auth_handler.assert_host_in_room(room_id, origin, True)
13071312

13081313
# Only allow up to 20 events to be retrieved per request.
13091314
limit = min(limit, 20)
@@ -1316,7 +1321,7 @@ async def on_get_missing_events(
13161321
)
13171322

13181323
missing_events = await filter_events_for_server(
1319-
self._storage_controllers, origin, missing_events
1324+
self._storage_controllers, origin, self.server_name, missing_events
13201325
)
13211326

13221327
return missing_events

synapse/visibility.py

+26-3
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,8 @@ def get_effective_room_visibility_from_state(state: StateMap[EventBase]) -> str:
563563

564564
async def filter_events_for_server(
565565
storage: StorageControllers,
566-
server_name: str,
566+
target_server_name: str,
567+
local_server_name: str,
567568
events: List[EventBase],
568569
redact: bool = True,
569570
check_history_visibility_only: bool = False,
@@ -603,7 +604,7 @@ def check_event_is_visible(
603604
# if the server is either in the room or has been invited
604605
# into the room.
605606
for ev in memberships.values():
606-
assert get_domain_from_id(ev.state_key) == server_name
607+
assert get_domain_from_id(ev.state_key) == target_server_name
607608

608609
memtype = ev.membership
609610
if memtype == Membership.JOIN:
@@ -622,6 +623,24 @@ def check_event_is_visible(
622623
# to no users having been erased.
623624
erased_senders = {}
624625

626+
# Filter out non-local events when we are in the middle of a partial join, since our servers
627+
# list can be out of date and we could leak events to servers not in the room anymore.
628+
# This can also be true for local events but we consider it to be an acceptable risk.
629+
630+
# We do this check as a first step and before retrieving membership events because
631+
# otherwise a room could be fully joined after we retrieve those, which would then bypass
632+
# this check but would base the filtering on an outdated view of the membership events.
633+
634+
partial_state_invisible_events = set()
635+
if not check_history_visibility_only:
636+
for e in events:
637+
sender_domain = get_domain_from_id(e.sender)
638+
if (
639+
sender_domain != local_server_name
640+
and await storage.main.is_partial_state_room(e.room_id)
641+
):
642+
partial_state_invisible_events.add(e)
643+
625644
# Let's check to see if all the events have a history visibility
626645
# of "shared" or "world_readable". If that's the case then we don't
627646
# need to check membership (as we know the server is in the room).
@@ -636,7 +655,7 @@ def check_event_is_visible(
636655
if event_to_history_vis[e.event_id]
637656
not in (HistoryVisibility.SHARED, HistoryVisibility.WORLD_READABLE)
638657
],
639-
server_name,
658+
target_server_name,
640659
)
641660

642661
to_return = []
@@ -645,6 +664,10 @@ def check_event_is_visible(
645664
visible = check_event_is_visible(
646665
event_to_history_vis[e.event_id], event_to_memberships.get(e.event_id, {})
647666
)
667+
668+
if e in partial_state_invisible_events:
669+
visible = False
670+
648671
if visible and not erased:
649672
to_return.append(e)
650673
elif redact:

tests/test_visibility.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def test_filtering(self) -> None:
6161

6262
filtered = self.get_success(
6363
filter_events_for_server(
64-
self._storage_controllers, "test_server", events_to_filter
64+
self._storage_controllers, "test_server", "hs", events_to_filter
6565
)
6666
)
6767

@@ -83,7 +83,7 @@ def test_filter_outlier(self) -> None:
8383
self.assertEqual(
8484
self.get_success(
8585
filter_events_for_server(
86-
self._storage_controllers, "remote_hs", [outlier]
86+
self._storage_controllers, "remote_hs", "hs", [outlier]
8787
)
8888
),
8989
[outlier],
@@ -94,7 +94,7 @@ def test_filter_outlier(self) -> None:
9494

9595
filtered = self.get_success(
9696
filter_events_for_server(
97-
self._storage_controllers, "remote_hs", [outlier, evt]
97+
self._storage_controllers, "remote_hs", "local_hs", [outlier, evt]
9898
)
9999
)
100100
self.assertEqual(len(filtered), 2, f"expected 2 results, got: {filtered}")
@@ -106,7 +106,7 @@ def test_filter_outlier(self) -> None:
106106
# be redacted)
107107
filtered = self.get_success(
108108
filter_events_for_server(
109-
self._storage_controllers, "other_server", [outlier, evt]
109+
self._storage_controllers, "other_server", "local_hs", [outlier, evt]
110110
)
111111
)
112112
self.assertEqual(filtered[0], outlier)
@@ -141,7 +141,7 @@ def test_erased_user(self) -> None:
141141
# ... and the filtering happens.
142142
filtered = self.get_success(
143143
filter_events_for_server(
144-
self._storage_controllers, "test_server", events_to_filter
144+
self._storage_controllers, "test_server", "local_hs", events_to_filter
145145
)
146146
)
147147

0 commit comments

Comments
 (0)