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

Commit 4f4f27e

Browse files
authored
Mitigate a race where /make_join could 403 for restricted rooms (#15080)
Previously, when creating a join event in /make_join, we would decide whether to include additional fields to satisfy restricted room checks based on the current state of the room. Then, when building the event, we would capture the forward extremities of the room to use as prev events. This is subject to race conditions. For example, when leaving and rejoining a room, the following sequence of events leads to a misleading 403 response: 1. /make_join reads the current state of the room and sees that the user is still in the room. It decides to omit the field required for restricted room joins. 2. The leave event is persisted and the room's forward extremities are updated. 3. /make_join builds the event, using the post-leave forward extremities. The event then fails the restricted room checks. To mitigate the race, we move the read of the forward extremities closer to the read of the current state. Ideally, we would compute the state based off the chosen prev events, but that can involve state resolution, which is expensive. Signed-off-by: Sean Quah <[email protected]>
1 parent ad1f3fa commit 4f4f27e

File tree

2 files changed

+16
-1
lines changed

2 files changed

+16
-1
lines changed

changelog.d/15080.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Reduce the likelihood of a rare race condition where rejoining a restricted room over federation would fail.

synapse/handlers/federation.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -952,7 +952,20 @@ async def on_make_join_request(
952952
#
953953
# Note that this requires the /send_join request to come back to the
954954
# same server.
955+
prev_event_ids = None
955956
if room_version.msc3083_join_rules:
957+
# Note that the room's state can change out from under us and render our
958+
# nice join rules-conformant event non-conformant by the time we build the
959+
# event. When this happens, our validation at the end fails and we respond
960+
# to the requesting server with a 403, which is misleading — it indicates
961+
# that the user is not allowed to join the room and the joining server
962+
# should not bother retrying via this homeserver or any others, when
963+
# in fact we've just messed up with building the event.
964+
#
965+
# To reduce the likelihood of this race, we capture the forward extremities
966+
# of the room (prev_event_ids) just before fetching the current state, and
967+
# hope that the state we fetch corresponds to the prev events we chose.
968+
prev_event_ids = await self.store.get_prev_events_for_room(room_id)
956969
state_ids = await self._state_storage_controller.get_current_state_ids(
957970
room_id
958971
)
@@ -994,7 +1007,8 @@ async def on_make_join_request(
9941007
event,
9951008
unpersisted_context,
9961009
) = await self.event_creation_handler.create_new_client_event(
997-
builder=builder
1010+
builder=builder,
1011+
prev_event_ids=prev_event_ids,
9981012
)
9991013
except SynapseError as e:
10001014
logger.warning("Failed to create join to %s because %s", room_id, e)

0 commit comments

Comments
 (0)