Skip to content

Commit 2830013

Browse files
committed
Merge branch 'master' into develop
2 parents 31110f3 + ecc09b1 commit 2830013

File tree

10 files changed

+77
-28
lines changed

10 files changed

+77
-28
lines changed

CHANGES.md

+7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# Synapse 1.127.1 (2025-03-26)
2+
3+
## Security
4+
- Fix [CVE-2025-30355](https://www.cve.org/CVERecord?id=CVE-2025-30355) / [GHSA-v56r-hwv5-mxg6](https://github.com/element-hq/synapse/security/advisories/GHSA-v56r-hwv5-mxg6). **High severity vulnerability affecting federation. The vulnerability has been exploited in the wild.**
5+
6+
7+
18
# Synapse 1.127.0 (2025-03-25)
29

310
No significant changes since 1.127.0rc1.

debian/changelog

+6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ matrix-synapse-py3 (1.128.0~rc1+nmu1) UNRELEASED; urgency=medium
44

55
-- Synapse Packaging team <packages@matrix.org> Wed, 19 Mar 2025 17:38:49 +0000
66

7+
matrix-synapse-py3 (1.127.1) stable; urgency=medium
8+
9+
* New Synapse release 1.127.1.
10+
11+
-- Synapse Packaging team <packages@matrix.org> Wed, 26 Mar 2025 21:07:31 +0000
12+
713
matrix-synapse-py3 (1.127.0) stable; urgency=medium
814

915
* New Synapse release 1.127.0.

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ module-name = "synapse.synapse_rust"
9797

9898
[tool.poetry]
9999
name = "matrix-synapse"
100-
version = "1.127.0"
100+
version = "1.127.1"
101101
description = "Homeserver for the Matrix decentralised comms protocol"
102102
authors = ["Matrix.org Team and Contributors <packages@matrix.org>"]
103103
license = "AGPL-3.0-or-later"

synapse/api/constants.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,13 @@
2929
# the max size of a (canonical-json-encoded) event
3030
MAX_PDU_SIZE = 65536
3131

32-
# the "depth" field on events is limited to 2**63 - 1
33-
MAX_DEPTH = 2**63 - 1
32+
# Max/min size of ints in canonical JSON
33+
CANONICALJSON_MAX_INT = (2**53) - 1
34+
CANONICALJSON_MIN_INT = -CANONICALJSON_MAX_INT
35+
36+
# the "depth" field on events is limited to the same as what
37+
# canonicaljson accepts
38+
MAX_DEPTH = CANONICALJSON_MAX_INT
3439

3540
# the maximum length for a room alias is 255 characters
3641
MAX_ALIAS_LENGTH = 255

synapse/events/utils.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
from canonicaljson import encode_canonical_json
4141

4242
from synapse.api.constants import (
43+
CANONICALJSON_MAX_INT,
44+
CANONICALJSON_MIN_INT,
4345
MAX_PDU_SIZE,
4446
EventContentFields,
4547
EventTypes,
@@ -61,9 +63,6 @@
6163
# Find escaped characters, e.g. those with a \ in front of them.
6264
ESCAPE_SEQUENCE_PATTERN = re.compile(r"\\(.)")
6365

64-
CANONICALJSON_MAX_INT = (2**53) - 1
65-
CANONICALJSON_MIN_INT = -CANONICALJSON_MAX_INT
66-
6766

6867
# Module API callback that allows adding fields to the unsigned section of
6968
# events that are sent to clients.

synapse/events/validator.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,7 @@ def validate_new(self, event: EventBase, config: HomeServerConfig) -> None:
8686

8787
# Depending on the room version, ensure the data is spec compliant JSON.
8888
if event.room_version.strict_canonicaljson:
89-
# Note that only the client controlled portion of the event is
90-
# checked, since we trust the portions of the event we created.
91-
validate_canonicaljson(event.content)
89+
validate_canonicaljson(event.get_pdu_json())
9290

9391
if event.type == EventTypes.Aliases:
9492
if "aliases" in event.content:

synapse/federation/federation_base.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
#
2121
#
2222
import logging
23-
from typing import TYPE_CHECKING, Awaitable, Callable, Optional
23+
from typing import TYPE_CHECKING, Awaitable, Callable, List, Optional, Sequence
2424

2525
from synapse.api.constants import MAX_DEPTH, EventContentFields, EventTypes, Membership
2626
from synapse.api.errors import Codes, SynapseError
@@ -29,6 +29,7 @@
2929
from synapse.crypto.keyring import Keyring
3030
from synapse.events import EventBase, make_event_from_dict
3131
from synapse.events.utils import prune_event, validate_canonicaljson
32+
from synapse.federation.units import filter_pdus_for_valid_depth
3233
from synapse.http.servlet import assert_params_in_dict
3334
from synapse.logging.opentracing import log_kv, trace
3435
from synapse.types import JsonDict, get_domain_from_id
@@ -267,6 +268,15 @@ def _is_invite_via_3pid(event: EventBase) -> bool:
267268
)
268269

269270

271+
def parse_events_from_pdu_json(
272+
pdus_json: Sequence[JsonDict], room_version: RoomVersion
273+
) -> List[EventBase]:
274+
return [
275+
event_from_pdu_json(pdu_json, room_version)
276+
for pdu_json in filter_pdus_for_valid_depth(pdus_json)
277+
]
278+
279+
270280
def event_from_pdu_json(pdu_json: JsonDict, room_version: RoomVersion) -> EventBase:
271281
"""Construct an EventBase from an event json received over federation
272282

synapse/federation/federation_client.py

+5-8
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
FederationBase,
6969
InvalidEventSignatureError,
7070
event_from_pdu_json,
71+
parse_events_from_pdu_json,
7172
)
7273
from synapse.federation.transport.client import SendJoinResponse
7374
from synapse.http.client import is_unknown_endpoint
@@ -349,7 +350,7 @@ async def backfill(
349350

350351
room_version = await self.store.get_room_version(room_id)
351352

352-
pdus = [event_from_pdu_json(p, room_version) for p in transaction_data_pdus]
353+
pdus = parse_events_from_pdu_json(transaction_data_pdus, room_version)
353354

354355
# Check signatures and hash of pdus, removing any from the list that fail checks
355356
pdus[:] = await self._check_sigs_and_hash_for_pulled_events_and_fetch(
@@ -393,9 +394,7 @@ async def get_pdu_from_destination_raw(
393394
transaction_data,
394395
)
395396

396-
pdu_list: List[EventBase] = [
397-
event_from_pdu_json(p, room_version) for p in transaction_data["pdus"]
398-
]
397+
pdu_list = parse_events_from_pdu_json(transaction_data["pdus"], room_version)
399398

400399
if pdu_list and pdu_list[0]:
401400
pdu = pdu_list[0]
@@ -809,7 +808,7 @@ async def get_event_auth(
809808

810809
room_version = await self.store.get_room_version(room_id)
811810

812-
auth_chain = [event_from_pdu_json(p, room_version) for p in res["auth_chain"]]
811+
auth_chain = parse_events_from_pdu_json(res["auth_chain"], room_version)
813812

814813
signed_auth = await self._check_sigs_and_hash_for_pulled_events_and_fetch(
815814
destination, auth_chain, room_version=room_version
@@ -1529,9 +1528,7 @@ async def get_missing_events(
15291528

15301529
room_version = await self.store.get_room_version(room_id)
15311530

1532-
events = [
1533-
event_from_pdu_json(e, room_version) for e in content.get("events", [])
1534-
]
1531+
events = parse_events_from_pdu_json(content.get("events", []), room_version)
15351532

15361533
signed_events = await self._check_sigs_and_hash_for_pulled_events_and_fetch(
15371534
destination, events, room_version=room_version

synapse/federation/federation_server.py

+13-8
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
event_from_pdu_json,
6767
)
6868
from synapse.federation.persistence import TransactionActions
69-
from synapse.federation.units import Edu, Transaction
69+
from synapse.federation.units import Edu, Transaction, serialize_and_filter_pdus
7070
from synapse.handlers.worker_lock import NEW_EVENT_DURING_PURGE_LOCK_NAME
7171
from synapse.http.servlet import assert_params_in_dict
7272
from synapse.logging.context import (
@@ -469,7 +469,12 @@ async def _handle_pdus_in_txn(
469469
logger.info("Ignoring PDU: %s", e)
470470
continue
471471

472-
event = event_from_pdu_json(p, room_version)
472+
try:
473+
event = event_from_pdu_json(p, room_version)
474+
except SynapseError as e:
475+
logger.info("Ignoring PDU for failing to deserialize: %s", e)
476+
continue
477+
473478
pdus_by_room.setdefault(room_id, []).append(event)
474479

475480
if event.origin_server_ts > newest_pdu_ts:
@@ -636,8 +641,8 @@ async def _on_context_state_request_compute(
636641
)
637642

638643
return {
639-
"pdus": [pdu.get_pdu_json() for pdu in pdus],
640-
"auth_chain": [pdu.get_pdu_json() for pdu in auth_chain],
644+
"pdus": serialize_and_filter_pdus(pdus),
645+
"auth_chain": serialize_and_filter_pdus(auth_chain),
641646
}
642647

643648
async def on_pdu_request(
@@ -761,8 +766,8 @@ async def on_send_join_request(
761766
event_json = event.get_pdu_json(time_now)
762767
resp = {
763768
"event": event_json,
764-
"state": [p.get_pdu_json(time_now) for p in state_events],
765-
"auth_chain": [p.get_pdu_json(time_now) for p in auth_chain_events],
769+
"state": serialize_and_filter_pdus(state_events, time_now),
770+
"auth_chain": serialize_and_filter_pdus(auth_chain_events, time_now),
766771
"members_omitted": caller_supports_partial_state,
767772
}
768773

@@ -1005,7 +1010,7 @@ async def on_event_auth(
10051010

10061011
time_now = self._clock.time_msec()
10071012
auth_pdus = await self.handler.on_event_auth(event_id)
1008-
res = {"auth_chain": [a.get_pdu_json(time_now) for a in auth_pdus]}
1013+
res = {"auth_chain": serialize_and_filter_pdus(auth_pdus, time_now)}
10091014
return 200, res
10101015

10111016
async def on_query_client_keys(
@@ -1090,7 +1095,7 @@ async def on_get_missing_events(
10901095

10911096
time_now = self._clock.time_msec()
10921097

1093-
return {"events": [ev.get_pdu_json(time_now) for ev in missing_events]}
1098+
return {"events": serialize_and_filter_pdus(missing_events, time_now)}
10941099

10951100
async def on_openid_userinfo(self, token: str) -> Optional[str]:
10961101
ts_now_ms = self._clock.time_msec()

synapse/federation/units.py

+24-2
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@
2424
"""
2525

2626
import logging
27-
from typing import List, Optional
27+
from typing import List, Optional, Sequence
2828

2929
import attr
3030

31+
from synapse.api.constants import CANONICALJSON_MAX_INT, CANONICALJSON_MIN_INT
32+
from synapse.events import EventBase
3133
from synapse.types import JsonDict
3234

3335
logger = logging.getLogger(__name__)
@@ -104,8 +106,28 @@ def get_dict(self) -> JsonDict:
104106
result = {
105107
"origin": self.origin,
106108
"origin_server_ts": self.origin_server_ts,
107-
"pdus": self.pdus,
109+
"pdus": filter_pdus_for_valid_depth(self.pdus),
108110
}
109111
if self.edus:
110112
result["edus"] = self.edus
111113
return result
114+
115+
116+
def filter_pdus_for_valid_depth(pdus: Sequence[JsonDict]) -> List[JsonDict]:
117+
filtered_pdus = []
118+
for pdu in pdus:
119+
# Drop PDUs that have a depth that is outside of the range allowed
120+
# by canonical json.
121+
if (
122+
"depth" in pdu
123+
and CANONICALJSON_MIN_INT <= pdu["depth"] <= CANONICALJSON_MAX_INT
124+
):
125+
filtered_pdus.append(pdu)
126+
127+
return filtered_pdus
128+
129+
130+
def serialize_and_filter_pdus(
131+
pdus: Sequence[EventBase], time_now: Optional[int] = None
132+
) -> List[JsonDict]:
133+
return filter_pdus_for_valid_depth([pdu.get_pdu_json(time_now) for pdu in pdus])

0 commit comments

Comments
 (0)