CVE-2018-10657: Improper Input Validation (CWE-20)
value: Matrix Synapse before 0.28.1 is prone to a denial of service flaw where malicious events injected with depth = 2^63 - 1 render rooms unusable, related to federation/federation_base.py and handlers/message.py, as exploited in the wild in April 2018.
CWE description: The product receives input or data, but it does
        not validate or incorrectly validates that the input has the
        properties that are required to process the data safely and
        correctly.
CVSS v3 base score: 7.5
Published: 2018-05-02T16:29Z
Repository: https://github.com/matrix-org/synapse
Language: Python
Files changed: synapse/api/constants.py, synapse/federation/federation_base.py, synapse/handlers/message.py
Commit date: 2018-05-01 17:54:19 +0100
Commit message: Apply some limits to depth to counter abuse

* When creating a new event, cap its depth to 2^63 - 1
* When receiving events, reject any without a sensible depth

As per https://docs.google.com/document/d/1I3fi2S-XnpO45qrpCsowZv8P8dHcNZ4fsBsbOW7KABI

Diff: diff --git a/synapse/api/constants.py b/synapse/api/constants.py
index 489efb7f..5baba439 100644
--- a/synapse/api/constants.py
+++ b/synapse/api/constants.py
@@ -14,10 +14,13 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
 """Contains constants from the specification."""
 
+# the "depth" field on events is limited to 2**63 - 1
+MAX_DEPTH = 2**63 - 1
+
 
 class Membership(object):
 
     """Represents the membership states of a user in a room."""
     INVITE = u"invite"
diff --git a/synapse/federation/federation_base.py b/synapse/federation/federation_base.py
index 79eaa310..4cc98a3f 100644
--- a/synapse/federation/federation_base.py
+++ b/synapse/federation/federation_base.py
@@ -12,11 +12,14 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
 import logging
 
-from synapse.api.errors import SynapseError
+import six
+
+from synapse.api.constants import MAX_DEPTH
+from synapse.api.errors import SynapseError, Codes
 from synapse.crypto.event_signing import check_event_content_hash
 from synapse.events import FrozenEvent
 from synapse.events.utils import prune_event
 from synapse.http.servlet import assert_params_in_request
 from synapse.util import unwrapFirstError, logcontext
@@ -188,15 +191,27 @@ def event_from_pdu_json(pdu_json, outlier=False):
 
     Returns:
         FrozenEvent
 
     Raises:
-        SynapseError: if the pdu is missing required fields
+        SynapseError: if the pdu is missing required fields or is otherwise
+            not a valid matrix event
     """
     # we could probably enforce a bunch of other fields here (room_id, sender,
     # origin, etc etc)
-    assert_params_in_request(pdu_json, ('event_id', 'type'))
+    assert_params_in_request(pdu_json, ('event_id', 'type', 'depth'))
+
+    depth = pdu_json['depth']
+    if not isinstance(depth, six.integer_types):
+        raise SynapseError(400, "Depth %r not an intger" % (depth, ),
+                           Codes.BAD_JSON)
+
+    if depth < 0:
+        raise SynapseError(400, "Depth too small", Codes.BAD_JSON)
+    elif depth > MAX_DEPTH:
+        raise SynapseError(400, "Depth too large", Codes.BAD_JSON)
+
     event = FrozenEvent(
         pdu_json
     )
 
     event.internal_metadata.outlier = outlier
diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py
index 21628a85..53beb2b9 100644
--- a/synapse/handlers/message.py
+++ b/synapse/handlers/message.py
@@ -14,11 +14,11 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 from twisted.internet import defer, reactor
 from twisted.python.failure import Failure
 
-from synapse.api.constants import EventTypes, Membership
+from synapse.api.constants import EventTypes, Membership, MAX_DEPTH
 from synapse.api.errors import AuthError, Codes, SynapseError
 from synapse.crypto.event_signing import add_hashes_and_signatures
 from synapse.events.utils import serialize_event
 from synapse.events.validator import EventValidator
 from synapse.types import (
@@ -622,10 +622,14 @@ class EventCreationHandler(object):
             prev_events_and_hashes = \
                 yield self.store.get_prev_events_for_room(builder.room_id)
 
         if prev_events_and_hashes:
             depth = max([d for _, _, d in prev_events_and_hashes]) + 1
+            # we cap depth of generated events, to ensure that they are not
+            # rejected by other servers (and so that they can be persisted in
+            # the db)
+            depth = min(depth, MAX_DEPTH)
         else:
             depth = 1
 
         prev_events = [
             (event_id, prev_hashes)

Vulnerable code: from synapse.api.errors import SynapseError
        SynapseError: if the pdu is missing required fields
    assert_params_in_request(pdu_json, ('event_id', 'type'))
from synapse.api.constants import EventTypes, Membership
Fixed code: # the "depth" field on events is limited to 2**63 - 1
MAX_DEPTH = 2**63 - 1

import six

from synapse.api.constants import MAX_DEPTH
from synapse.api.errors import SynapseError, Codes
        SynapseError: if the pdu is missing required fields or is otherwise
            not a valid matrix event
    assert_params_in_request(pdu_json, ('event_id', 'type', 'depth'))

    depth = pdu_json['depth']
    if not isinstance(depth, six.integer_types):
        raise SynapseError(400, "Depth %r not an intger" % (depth, ),
                           Codes.BAD_JSON)

    if depth < 0:
        raise SynapseError(400, "Depth too small", Codes.BAD_JSON)
    elif depth > MAX_DEPTH:
        raise SynapseError(400, "Depth too large", Codes.BAD_JSON)

from synapse.api.constants import EventTypes, Membership, MAX_DEPTH
            # we cap depth of generated events, to ensure that they are not
            # rejected by other servers (and so that they can be persisted in
            # the db)
            depth = min(depth, MAX_DEPTH)