File 5061-Disable-zlib-by-default-and-limit-size-of-decompress.patch of Package erlang

From 3e189a5a29b70bff72add4cc54b300a7651bfcdc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C5=82=20W=C4=85sowski?= <michal@erlang.org>
Date: Sat, 7 Mar 2026 18:33:39 +0100
Subject: [PATCH 1/6] Disable zlib by default and limit size of decompressed
 data

---
 lib/ssh/src/ssh_connection_handler.erl |  7 +++
 lib/ssh/src/ssh_transport.erl          | 64 ++++++++++++++++++++++----
 2 files changed, 63 insertions(+), 8 deletions(-)

diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 2648c5256b..6e5baa5315 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -1188,6 +1188,13 @@ handle_event(info, {Proto, Sock, NewData}, StateName,
                                  io_lib:format("Bad packet: Size (~p bytes) exceeds max size",
                                                [PacketLen]),
                                  StateName, D0),
+            {stop, Shutdown, D};
+
+    {error, exceeds_max_decompressed_size} ->
+            {Shutdown, D} =
+                ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR,
+                                 "Bad packet: Size after decompression exceeds max size",
+                                 StateName, D0),
             {stop, Shutdown, D}
     catch
 	C:E:ST ->
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 3e96ca9402..08aa126a61 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -193,6 +193,9 @@ default_algorithms1(public_key) ->
                                       'ssh-dss'
                                      ]);
 
+default_algorithms1(compression) ->
+    supported_algorithms(compression, same(['zlib']));
+
 default_algorithms1(Alg) ->
     supported_algorithms(Alg, []).
 
@@ -1423,8 +1426,12 @@ handle_packet_part(DecryptedPfx, EncryptedBuffer, AEAD, TotalNeeded, #ssh{decryp
     case unpack(pkt_type(CryptoAlg), mac_type(MacAlg),
                 DecryptedPfx, EncryptedBuffer, AEAD, TotalNeeded, Ssh0) of
         {ok, Payload, NextPacketBytes, Ssh1} ->
-            {Ssh, DecompressedPayload} = decompress(Ssh1, Payload),
-            {packet_decrypted, DecompressedPayload, NextPacketBytes, Ssh};
+            case decompress(Ssh1, Payload) of
+                {ok, Ssh, DecompressedPayload} ->
+                    {packet_decrypted, DecompressedPayload, NextPacketBytes, Ssh};
+                Other ->
+                    Other
+            end;
         Other ->
             Other
     end.
@@ -1940,15 +1947,56 @@ decompress_final(#ssh{decompress = 'zlib@openssh.com', decompress_ctx = Context,
     {ok, Ssh#ssh{decompress = none, decompress_ctx = undefined}}.
 
 decompress(#ssh{decompress = none} = Ssh, Data) ->
-    {Ssh, Data};
+    {ok, Ssh, Data};
 decompress(#ssh{decompress = zlib, decompress_ctx = Context} = Ssh, Data) ->
-    Decompressed = zlib:inflate(Context, Data),
-    {Ssh, list_to_binary(Decompressed)};
+    case safe_zlib_inflate(Context, Data) of
+        {ok, Decompressed} ->
+            {ok, Ssh, Decompressed};
+        Other ->
+            Other
+    end;
 decompress(#ssh{decompress = 'zlib@openssh.com', authenticated = false} = Ssh, Data) ->
-    {Ssh, Data};
+    {ok, Ssh, Data};
 decompress(#ssh{decompress = 'zlib@openssh.com', decompress_ctx = Context, authenticated = true} = Ssh, Data) ->
-    Decompressed = zlib:inflate(Context, Data),
-    {Ssh, list_to_binary(Decompressed)}.
+    case safe_zlib_inflate(Context, Data) of
+        {ok, Decompressed} ->
+            {ok, Ssh, Decompressed};
+        Other ->
+            Other
+    end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Safe decompression loop
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+safe_zlib_inflate(Context, Data) ->
+    safe_zlib_inflate_loop(Context, {0, []}, zlib:safeInflate(Context, Data)).
+
+safe_zlib_inflate_loop(Context, {AccLen0, AccData}, {Status, Chunk})
+  when Status == continue; Status == finished ->
+    ChunkLen = iolist_size(Chunk),
+    AccLen = AccLen0 + ChunkLen,
+    %% RFC 4253 section 6
+    %% Align with packets that don't use compression, we can process payloads with length
+    %% that required minimum padding.
+    %% From ?SSH_MAX_PACKET_SIZE subtract:
+    %% 1 byte for length of padding_length field
+    %% 4 bytes for minimum allowed length of padding
+    %% We don't subtract:
+    %% 4 bytes for packet_length field - not included in packet_length
+    %% x bytes for mac (size depends on type of used mac) - not included in packet_length
+    case AccLen > (?SSH_MAX_PACKET_SIZE - 5) of
+        true ->
+            {error, exceeds_max_decompressed_size};
+        false when Status == continue ->
+            Next = zlib:safeInflate(Context, []),
+            safe_zlib_inflate_loop(Context, {AccLen, [Chunk | AccData]}, Next);
+        false when Status == finished ->
+            Reversed = lists:reverse([Chunk | AccData]),
+            {ok, iolist_to_binary(Reversed)}
+    end;
+safe_zlib_inflate_loop(_Context, {_AccLen, _AccData}, {need_dictionary, Adler, _Chunk}) ->
+    erlang:error({need_dictionary, Adler}).
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %%
-- 
2.51.0

openSUSE Build Service is sponsored by