File feature-fix-update-ssh-stack.patch of Package erlang26.42057

diff -ruN a/lib/ssh/doc/html/.gitignore b/lib/ssh/doc/html/.gitignore
--- a/lib/ssh/doc/html/.gitignore	1970-01-01 09:30:00.000000000 +0930
+++ b/lib/ssh/doc/html/.gitignore	2025-12-22 23:16:56.257838884 +1030
@@ -0,0 +1 @@
+*
diff -ruN a/lib/ssh/doc/specs/.gitignore b/lib/ssh/doc/specs/.gitignore
--- a/lib/ssh/doc/specs/.gitignore	1970-01-01 09:30:00.000000000 +0930
+++ b/lib/ssh/doc/specs/.gitignore	2025-12-22 23:16:56.257838884 +1030
@@ -0,0 +1 @@
+specs_*.xml
diff -ruN a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
--- a/lib/ssh/doc/src/notes.xml	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/doc/src/notes.xml	2025-12-22 23:16:56.258838895 +1030
@@ -4,7 +4,7 @@
 <chapter>
   <header>
     <copyright>
-      <year>2004</year><year>2023</year>
+      <year>2004</year><year>2024</year>
       <holder>Ericsson AB. All Rights Reserved.</holder>
     </copyright>
     <legalnotice>
@@ -30,6 +30,382 @@
     <file>notes.xml</file>
   </header>
 
+<section><title>Ssh 5.1.4.13</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+	    <p>With this change user space buffers are used to limit
+	    ssh hello message size instead of kernel buffers</p>
+          <p>
+	    Own Id: OTP-19839 Aux Id: ERIERL-1273, PR-10350 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.12</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+	    <p>Option max_handles can be configured for sshd running
+	    SFTP. The positive integer value limits amount of file
+	    handles opened for a connection (by default 4096 is
+	    used).</p>
+          <p>
+	    *** POTENTIAL INCOMPATIBILITY ***</p>
+          <p>
+	    Own Id: OTP-19701 Aux Id: CVE-2025-48041, PR-10157 </p>
+        </item>
+        <item>
+	    <p>Avoid decoding KEX messages providing too many
+	    algorithms. This change does not introduce new limitation
+	    but assures it is enforced earlier in processing chain.
+	    Adjustments in error logging during handshake.</p>
+          <p>
+	    *** POTENTIAL INCOMPATIBILITY ***</p>
+          <p>
+	    Own Id: OTP-19741 Aux Id: CVE-2025-48040, PR-10162 </p>
+        </item>
+        <item>
+	    <p>A new 'max_path' option is now available in the sshd
+	    configuration, allowing administrators to set the maximum
+	    allowable path length. By default, this value is set to
+	    4096 characters.</p>
+          <p>
+	    *** POTENTIAL INCOMPATIBILITY ***</p>
+          <p>
+	    Own Id: OTP-19742 Aux Id: CVE-2025-48039, PR-10155 </p>
+        </item>
+        <item>
+	    <p>Reject file handles exceeding size specified in RFCs
+	    (256 bytes).</p>
+          <p>
+	    *** POTENTIAL INCOMPATIBILITY ***</p>
+          <p>
+	    Own Id: OTP-19748 Aux Id: CVE-2025-48038, PR-10156 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.11</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+	    <p>Fix file handle id generation.</p>
+          <p>
+	    Own Id: OTP-19691 Aux Id: PR-10003 </p>
+        </item>
+        <item>
+	    <p>Fixes a badmatch error, when SFTP operation cannot be
+	    processed due to channel closed in parallel.</p>
+          <p>
+	    Own Id: OTP-19707 Aux Id: GH-9655, PR-10035, PR-10036 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.10</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+	    <p>Various channel closing robustness improvements. Avoid
+	    crashes when channel handling process closes channel and
+	    immediately exits. Avoid breaking the protocol by sending
+	    duplicated channel-close messages. Cleanup channels which
+	    timeout during closing procedure.</p>
+          <p>
+	    Own Id: OTP-19634 Aux Id: GH-9102, PR-9103 </p>
+        </item>
+        <item>
+	    <p>Improved interoperability with clients acting as
+	    Paramiko.</p>
+          <p>
+	    Own Id: OTP-19637 Aux Id: GH-6463, PR-9838 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.9</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+	    <p>Fix KEX strict implementation according to
+	    draft-miller-sshm-strict-kex-01 document.</p>
+          <p>
+	    Own Id: OTP-19625 Aux Id: CVE-2025-46712 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.8</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+	    <p>Reception of wrong Unicode does not cause unnecessary
+	    processing. US-ASCII fields are not decoded as
+	    Unicode.</p>
+          <p>
+	    Own Id: OTP-19582 Aux Id: PR-9679 </p>
+        </item>
+        <item>
+	    <p>SSH daemon disconnects upon receiving connection
+	    protocol message for unauthenticated used.</p>
+	    <p>Thanks to Fabian Bäumer, Marcel Maehren, Marcus
+	    Brinkmann, Nurullah Erinola, Jörg Schwenk (Ruhr
+	    University Bochum).</p>
+          <p>
+	    Own Id: OTP-19595 Aux Id: CVE-2025-32433 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.7</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+	    <p>Reception of malicious KEX init message does not
+	    result with ssh daemon excessive memory usage.</p>
+          <p>
+	    Own Id: OTP-19543 Aux Id: CVE-2025-30211 </p>
+        </item>
+        <item>
+	    <p>Call to ssh:daemon_replace_options does not crash when
+	    argument is not a valid daemon ref.</p>
+          <p>
+	    Own Id: OTP-19559 Aux Id: GH-9554, PR-9545 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.6</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+	    <p>SFTP packets exceeding max packet size are not
+	    processed and dropped.</p>
+          <p>
+	    Own Id: OTP-19466 Aux Id: ERIERL-1173, CVE-2025-26618 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.5</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+	    <p>With this change, type specs for
+	    ssh:connection_info/1,2 functions are fixed so they
+	    include {error, term()} return value.</p>
+          <p>
+	    Own Id: OTP-19388 Aux Id: ERIERL-1165, PR-9161 </p>
+        </item>
+        <item>
+	    <p>With this change, ssh client accepts a banner sent
+	    during processing keyboard interactive user
+	    authentication.</p>
+          <p>
+	    Own Id: OTP-19392 Aux Id: PR-9139, GH-9065 </p>
+        </item>
+        <item>
+	    <p> With this change, large sftp transfers does not hang.
+	    Redundant window adjustment are not requested. </p>
+          <p>
+	    Own Id: OTP-19435 Aux Id: PR-9309 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.4</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+	    <p>With this change, ssh connection does not crash upon
+	    receiving exit-signal message for an already terminated
+	    channel.</p>
+          <p>
+	    Own Id: OTP-19326 Aux Id: PR-8995, GH-8929 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.3</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, a race condition is removed from ssh
+	    client connection setup procedure.</p>
+          <p>
+	    Own Id: OTP-19124 Aux Id: GH-7550, PR-8766 </p>
+        </item>
+        <item>
+          <p>
+	    With this change, ssh:connect is not affected by presence
+	    of EXIT message in queue.</p>
+          <p>
+	    Own Id: OTP-19246 Aux Id: GH-8223, PR-8854 </p>
+        </item>
+        <item>
+          <p>
+	    With this change, ssh appends {active, false} option
+	    after socket options received from user - so that false
+	    value is always used.</p>
+          <p>
+	    Own Id: OTP-19247 Aux Id: PR-8226 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.2</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, ssh daemon started with TCP port number
+	    argument will re-try to obtain listen socket before
+	    returning error to user.</p>
+          <p>
+	    Own Id: OTP-19170 Aux Id: GH-7746 </p>
+        </item>
+        <item>
+          <p>
+	    With this change, robustness is improved by monitoring
+	    connection handler process before casting socket control
+	    notification.</p>
+          <p>
+	    Own Id: OTP-19173 Aux Id: PR-8310 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.1</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, ssh client will automatically adjust
+	    transfer window size for commands executed remotely over
+	    SSH.</p>
+          <p>
+	    Own Id: OTP-19057 Aux Id: PR-8345, GH-7483 </p>
+        </item>
+        <item>
+          <p>
+	    With this change, race condition between connection
+	    closing and automatic window adjustment is fixed.</p>
+          <p>
+	    Own Id: OTP-19109 Aux Id: PR-8345 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, owner and group file attributes
+	    decoding is fixed and results with value of integer type.</p>
+          <p>
+	    Own Id: OTP-19013 Aux Id: GH-7897, PR-8220 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.3</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, acceptor_sup is not started for ssh
+	    client as it is not needed in that role.</p>
+          <p>
+	    Own Id: OTP-18974 </p>
+        </item>
+        <item>
+          <p>
+	    With this change, more secure algorithms are preferred by
+	    ssh and documentation is updated to reflect that.</p>
+          <p>
+	    Own Id: OTP-18986 </p>
+        </item>
+        <item>
+          <p>
+	    With this change, KEX strict terminal message is emitted
+	    with debug verbosity.</p>
+          <p>
+	    Own Id: OTP-19002 Aux Id: ERIERL-1041 </p>
+        </item>
+        <item>
+          <p>
+	    Fix reading of password for ssh client when in
+	    <c>user_interactive</c> mode.</p>
+          <p>
+	    Own Id: OTP-19007 Aux Id: ERIERL-1049 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.2</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, Curve25519 and Curve448 KEX methods
+	    become most preferred (related to RFC8731).</p>
+          <p>
+	    Own Id: OTP-18964 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
 <section><title>Ssh 5.1.1</title>
 
     <section><title>Fixed Bugs and Malfunctions</title>
@@ -164,6 +540,59 @@
 
 </section>
 
+<section><title>Ssh 4.15.3.2</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, Curve25519 and Curve448 KEX methods
+	    become most preferred (related to RFC8731).</p>
+          <p>
+	    Own Id: OTP-18964 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.15.3.1</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, connection handler does not execute
+	    socket operations until it becomes socket owner.
+	    Previously errors could occur if connection handler tried
+	    to work with socket whose owner exited.</p>
+          <p>
+	    Own Id: OTP-18869 Aux Id: PR-7849,GH-7571 </p>
+        </item>
+        <item>
+          <p>
+	    With this change (being response to CVE-2023-48795), ssh
+	    can negotiate "strict KEX" OpenSSH extension with peers
+	    supporting it; also 'chacha20-poly1305@openssh.com'
+	    algorithm becomes a less preferred cipher.</p>
+          <p>
+	    If strict KEX availability cannot be ensured on both
+	    connection sides, affected encryption modes(CHACHA and
+	    CBC) can be disabled with standard ssh configuration.
+	    This will provide protection against vulnerability, but
+	    at a cost of affecting interoperability. See <seeguide
+	    marker="configure_algos">Configuring algorithms in
+	    SSH</seeguide>.</p>
+          <p>
+	    *** POTENTIAL INCOMPATIBILITY ***</p>
+          <p>
+	    Own Id: OTP-18897</p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
 <section><title>Ssh 4.15.3</title>
 
     <section><title>Fixed Bugs and Malfunctions</title>
@@ -357,6 +786,59 @@
         </item>
       </list>
     </section>
+
+</section>
+
+<section><title>Ssh 4.13.2.5</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, Curve25519 and Curve448 KEX methods
+	    become most preferred (related to RFC8731).</p>
+          <p>
+	    Own Id: OTP-18964 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.13.2.4</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, connection handler does not execute
+	    socket operations until it becomes socket owner.
+	    Previously errors could occur if connection handler tried
+	    to work with socket whose owner exited.</p>
+          <p>
+	    Own Id: OTP-18869 Aux Id: PR-7849,GH-7571 </p>
+        </item>
+        <item>
+          <p>
+	    With this change (being response to CVE-2023-48795), ssh
+	    can negotiate "strict KEX" OpenSSH extension with peers
+	    supporting it; also 'chacha20-poly1305@openssh.com'
+	    algorithm becomes a less preferred cipher.</p>
+          <p>
+	    If strict KEX availability cannot be ensured on both
+	    connection sides, affected encryption modes(CHACHA and
+	    CBC) can be disabled with standard ssh configuration.
+	    This will provide protection against vulnerability, but
+	    at a cost of affecting interoperability. See <seeguide
+	    marker="configure_algos">Configuring algorithms in
+	    SSH</seeguide>.</p>
+          <p>
+	    *** POTENTIAL INCOMPATIBILITY ***</p>
+          <p>
+	    Own Id: OTP-18897</p>
+        </item>
+      </list>
+    </section>
 
 </section>
 
diff -ruN a/lib/ssh/doc/src/SSH_app.xml b/lib/ssh/doc/src/SSH_app.xml
--- a/lib/ssh/doc/src/SSH_app.xml	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/doc/src/SSH_app.xml	2025-12-22 23:16:56.257838884 +1030
@@ -4,7 +4,7 @@
 <appref>
   <header>
     <copyright>
-      <year>2012</year><year>2021</year>
+      <year>2012</year><year>2024</year>
       <holder>Ericsson AB. All Rights Reserved.</holder>
     </copyright>
     <legalnotice>
@@ -172,16 +172,16 @@
       <tag>Key exchange algorithms</tag>
       <item>
 	<list type="bulleted">
-	  <item>ecdh-sha2-nistp384</item>
+	  <item>curve25519-sha256</item>
+	  <item>curve25519-sha256@libssh.org</item>
+	  <item>curve448-sha512</item>
 	  <item>ecdh-sha2-nistp521</item>
+	  <item>ecdh-sha2-nistp384</item>
 	  <item>ecdh-sha2-nistp256</item>
 	  <item>diffie-hellman-group-exchange-sha256</item>
 	  <item>diffie-hellman-group16-sha512</item>
 	  <item>diffie-hellman-group18-sha512</item>
 	  <item>diffie-hellman-group14-sha256</item>
-	  <item>curve25519-sha256</item>
-	  <item>curve25519-sha256@libssh.org</item>
-	  <item>curve448-sha512</item>
 	</list>
 	<p>The following unsecure <c>SHA1</c> algorithms are now disabled by default:</p>
 	<list>
@@ -199,13 +199,13 @@
       <tag>Public key algorithms</tag>
       <item>
 	<list type="bulleted">
-	  <item>ecdsa-sha2-nistp384</item>
-	  <item>ecdsa-sha2-nistp521</item>
-	  <item>ecdsa-sha2-nistp256</item>
-	  <item>ssh-ed25519</item>
+          <item>ssh-ed25519</item>
 	  <item>ssh-ed448</item>
+          <item>ecdsa-sha2-nistp521</item>
+          <item>ecdsa-sha2-nistp384</item>
+	  <item>ecdsa-sha2-nistp256</item>
+          <item>rsa-sha2-512</item>
 	  <item>rsa-sha2-256</item>
-	  <item>rsa-sha2-512</item>
 	</list>
 	<p>The following unsecure <c>SHA1</c> algorithms are supported but disabled by default:</p>
 	<list>
@@ -227,11 +227,11 @@
       <tag>MAC algorithms</tag>
       <item>
 	<list type="bulleted">
-	  <item>hmac-sha2-256-etm@openssh.com</item>
 	  <item>hmac-sha2-512-etm@openssh.com</item>
-	  <item>hmac-sha1-etm@openssh.com</item>
-	  <item>hmac-sha2-256</item>
+	  <item>hmac-sha2-256-etm@openssh.com</item>
 	  <item>hmac-sha2-512</item>
+	  <item>hmac-sha2-256</item>
+          <item>hmac-sha1-etm@openssh.com</item>
 	  <item>hmac-sha1</item>
 	</list>
 	<p>The following unsecure <c>SHA1</c> algorithm is disabled by default:</p>
diff -ruN a/lib/ssh/doc/src/ssh_sftpd.xml b/lib/ssh/doc/src/ssh_sftpd.xml
--- a/lib/ssh/doc/src/ssh_sftpd.xml	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/doc/src/ssh_sftpd.xml	2025-12-22 23:16:56.259838907 +1030
@@ -4,7 +4,7 @@
 <erlref>
   <header>
     <copyright>
-      <year>2005</year><year>2020</year>
+      <year>2005</year><year>2025</year>
       <holder>Ericsson AB. All Rights Reserved.</holder>
     </copyright>
     <legalnotice>
@@ -65,6 +65,18 @@
 	    If supplied, the number of filenames returned to the SFTP client per <c>READDIR</c>
 	    request is limited to at most the given value.</p>
 	  </item>
+	  <tag><c>max_handles</c></tag>
+	  <item>
+            <p>The default value is <c>1000</c>. Positive integer value represents the maximum number of file handles allowed for a connection.</p>            
+	  </item>
+          <tag><c>max_path</c></tag>
+	  <item>
+	    <p>The default value is <c>4096</c>. Positive integer
+	    value represents the maximum path length which cannot be
+	    exceeded in data provided by the SFTP client. (Note:
+	    limitations might be also enforced by underlying operating
+	    system)</p>
+	  </item>
 	  <tag><c>root</c></tag>
 	  <item>
 	    <p>Sets the SFTP root directory. Then the user cannot see any files
diff -ruN a/lib/ssh/src/.gitignore b/lib/ssh/src/.gitignore
--- a/lib/ssh/src/.gitignore	1970-01-01 09:30:00.000000000 +0930
+++ b/lib/ssh/src/.gitignore	2025-12-17 17:25:02.074275138 +1030
@@ -0,0 +1 @@
+deps
diff -ruN a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile
--- a/lib/ssh/src/Makefile	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/src/Makefile	2025-12-22 23:16:56.259838907 +1030
@@ -1,7 +1,7 @@
 #
 # %CopyrightBegin%
 #
-# Copyright Ericsson AB 2004-2022. All Rights Reserved.
+# Copyright Ericsson AB 2004-2024. All Rights Reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -80,7 +80,7 @@
 	ssh_sftpd \
 	ssh_sftpd_file\
 	ssh_shell \
-	ssh_subsystem_sup \
+	ssh_connection_sup \
 	ssh_system_sup \
 	ssh_tcpip_forward_srv \
 	ssh_tcpip_forward_client \
diff -ruN a/lib/ssh/src/ssh_acceptor.erl b/lib/ssh/src/ssh_acceptor.erl
--- a/lib/ssh/src/ssh_acceptor.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/src/ssh_acceptor.erl	2025-12-22 23:16:56.261838930 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2023. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -47,7 +47,7 @@
 %%%----------------------------------------------------------------
 listen(Port, Options) ->
     {_, Callback, _} = ?GET_OPT(transport, Options),
-    SockOpts = [{active, false}, {reuseaddr,true} | ?GET_OPT(socket_options, Options)],
+    SockOpts = ?GET_OPT(socket_options, Options) ++ [{active, false}, {reuseaddr,true}],
     case Callback:listen(Port, SockOpts) of
 	{error, nxdomain} ->
 	    Callback:listen(Port, lists:delete(inet6, SockOpts));
@@ -83,7 +83,6 @@
                     proc_lib:init_ack(Parent, {ok, self()}),
                     request_ownership(LSock, SockOwner),
                     acceptor_loop(Port, Address, Opts, LSock, AcceptTimeout, SystemSup);
-
                 {error,_Error} ->
                     %% Not open, a restart
                     %% Allow gen_tcp:listen to fail 4 times if eaddrinuse (It is a bug fix):
@@ -96,10 +95,9 @@
                             proc_lib:init_fail(Parent, {error,Error}, {exit, normal})
                     end
             end;
-
         undefined ->
             %% No listening socket (nor fd option) was provided; open a listening socket:
-            case listen(Port, Opts) of
+            case try_listen(Port, Opts, 4) of
                 {ok,LSock} ->
                     proc_lib:init_ack(Parent, {ok, self()}),
                     acceptor_loop(Port, Address, Opts, LSock, AcceptTimeout, SystemSup);
@@ -108,7 +106,6 @@
             end
     end.
 
-
 try_listen(Port, Opts, NtriesLeft) ->
     try_listen(Port, Opts, 1, NtriesLeft).
 
@@ -121,7 +118,6 @@
             Other
     end.
 
-
 request_ownership(LSock, SockOwner) ->
     SockOwner ! {request_control,LSock,self()},
     receive
@@ -137,7 +133,8 @@
                 MaxSessions = ?GET_OPT(max_sessions, Opts),
                 NumSessions = number_of_connections(SystemSup),
                 ParallelLogin = ?GET_OPT(parallel_login, Opts),
-                case handle_connection(Address, Port, PeerName, Opts, Socket, MaxSessions, NumSessions, ParallelLogin) of
+                case handle_connection(Address, Port, PeerName, Opts, Socket,
+                                       MaxSessions, NumSessions, ParallelLogin) of
                     {error,Error} ->
                         catch close(Socket, Opts),
                         handle_error(Error, Address, Port, PeerName);
@@ -154,18 +151,19 @@
     ?MODULE:acceptor_loop(Port, Address, Opts, ListenSocket, AcceptTimeout, SystemSup).
 
 %%%----------------------------------------------------------------
-handle_connection(_Address, _Port, _Peer, _Options, _Socket, MaxSessions, NumSessions, _ParallelLogin)
+handle_connection(_Address, _Port, _Peer, _Options, _Socket,
+                  MaxSessions, NumSessions, _ParallelLogin)
   when NumSessions >= MaxSessions->
     {error,{max_sessions,MaxSessions}};
-
-handle_connection(_Address, _Port, {error,Error}, _Options, _Socket, _MaxSessions, _NumSessions, _ParallelLogin) ->
+handle_connection(_Address, _Port, {error,Error}, _Options, _Socket,
+                  _MaxSessions, _NumSessions, _ParallelLogin) ->
     {error,Error};
-
-handle_connection(Address, Port, _Peer, Options, Socket, _MaxSessions, _NumSessions, ParallelLogin)
+handle_connection(Address, Port, _Peer, Options, Socket,
+                  _MaxSessions, _NumSessions, ParallelLogin)
   when ParallelLogin == false ->
     handle_connection(Address, Port, Options, Socket);
-
-handle_connection(Address, Port, _Peer, Options, Socket, _MaxSessions, _NumSessions, ParallelLogin)
+handle_connection(Address, Port, _Peer, Options, Socket,
+                  _MaxSessions, _NumSessions, ParallelLogin)
   when ParallelLogin == true ->
     Ref = make_ref(),
     Pid = spawn_link(
@@ -182,12 +180,10 @@
     Pid ! {start,Ref},
     ok.
 
-
-
 handle_connection(Address, Port, Options0, Socket) ->
     Options = ?PUT_INTERNAL_OPT([{user_pid, self()}
                                 ], Options0),
-    ssh_system_sup:start_subsystem(server,
+    ssh_system_sup:start_connection(server,
                                    #address{address = Address,
                                             port = Port,
                                             profile = ?GET_OPT(profile,Options)
@@ -206,44 +202,68 @@
 handle_error(Reason, ToAddress, ToPort, FromAddress, FromPort) ->
     case Reason of
         {max_sessions, MaxSessions} ->
-            error_logger:info_report(
-              lists:concat(["Ssh login attempt to ",ssh_lib:format_address_port(ToAddress,ToPort),
-                            " from ",ssh_lib:format_address_port(FromAddress,FromPort),
-                            " denied due to option max_sessions limits to ",
-                            MaxSessions, " sessions."
-                           ])
-             );
-
+            MsgFun =
+                fun(debug) ->
+                        lists:concat(["Ssh login attempt to ",
+                                      ssh_lib:format_address_port(ToAddress,ToPort),
+                                      " from ",
+                                      ssh_lib:format_address_port(FromAddress,FromPort),
+                                      " denied due to option max_sessions limits to ",
+                                      MaxSessions, " sessions."]);
+                   (_) ->
+                        ["Ssh login attempt denied max_session limits"]
+                end,
+            error_logger:info_report(?SELECT_MSG(MsgFun));
         Limit when Limit==enfile ; Limit==emfile ->
             %% Out of sockets...
-            error_logger:info_report([atom_to_list(Limit),": out of accept sockets on ",
-                                      ssh_lib:format_address_port(ToAddress, ToPort),
-                                      " - retrying"]),
+            MsgFun =
+                fun(debug) ->
+                        [atom_to_list(Limit),": out of accept sockets on ",
+                         ssh_lib:format_address_port(ToAddress, ToPort),
+                         " - retrying"];
+                   (_) ->
+                        ["Out of accept sockets on - retrying"]
+                end,
+            error_logger:info_report(?SELECT_MSG(MsgFun)),
             timer:sleep(?SLEEP_TIME);
-
         closed ->
-            error_logger:info_report(["The ssh accept socket on ",ssh_lib:format_address_port(ToAddress,ToPort),
-                                      "was closed by a third party."]
-                                    );
-
+            MsgFun =
+                fun(debug) ->
+                        ["The ssh accept socket on ", ssh_lib:format_address_port(ToAddress,ToPort),
+                         "was closed by a third party."];
+                   (_) ->
+                        ["The ssh accept socket on was closed by a third party"]
+                end,
+            error_logger:info_report(?SELECT_MSG(MsgFun));
         timeout ->
             ok;
-
         Error when is_list(Error) ->
             ok;
         Error when FromAddress=/=undefined,
                    FromPort=/=undefined ->
-            error_logger:info_report(["Accept failed on ",ssh_lib:format_address_port(ToAddress,ToPort),
-                                      " for connect from ",ssh_lib:format_address_port(FromAddress,FromPort),
-                                      io_lib:format(": ~p", [Error])]);
+            MsgFun =
+                fun(debug) ->
+                        ["Accept failed on ",ssh_lib:format_address_port(ToAddress,ToPort),
+                         " for connect from ",ssh_lib:format_address_port(FromAddress,FromPort),
+                         io_lib:format(": ~p", [Error])];
+                   (_) ->
+                        [io_lib:format("Accept failed on for connection: ~p", [Error])]
+                end,
+            error_logger:info_report(?SELECT_MSG(MsgFun));
         Error ->
-            error_logger:info_report(["Accept failed on ",ssh_lib:format_address_port(ToAddress,ToPort),
-                                      io_lib:format(": ~p", [Error])])
+            MsgFun =
+                fun(debug) ->
+                        ["Accept failed on ",ssh_lib:format_address_port(ToAddress,ToPort),
+                         io_lib:format(": ~p", [Error])];
+                   (_) ->
+                        [io_lib:format("Accept failed on for connection: ~p", [Error])]
+                end,
+            error_logger:info_report(?SELECT_MSG(MsgFun))
     end.
 
 %%%----------------------------------------------------------------
 number_of_connections(SysSupPid) ->
-    lists:foldl(fun({_Ref,_Pid,supervisor,[ssh_subsystem_sup]}, N) -> N+1;
+    lists:foldl(fun({_Ref,_Pid,supervisor,[ssh_connection_sup]}, N) -> N+1;
                    (_, N) -> N
                 end, 0, supervisor:which_children(SysSupPid)).
 
diff -ruN a/lib/ssh/src/ssh_app.erl b/lib/ssh/src/ssh_app.erl
--- a/lib/ssh/src/ssh_app.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/src/ssh_app.erl	2025-12-22 23:16:56.261838930 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2004-2021. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2024. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -23,11 +23,11 @@
 %%%=========================================================================
 %%% Purpose : Application master and top supervisors for SSH.
 %%%
-%%%  -----> ssh_sup -----+-----> sshc_sup --+--> "system sup" (etc)
+%%%  -----> ssh_sup -----+-----> sshc_sup --+--> "connection sup" (etc)
 %%%                      |                  |
-%%%                      |                  +--> "system sup" (etc)
+%%%                      |                  +--> "connection sup" (etc)
 %%%                      |                  :
-%%%                      |                  +--> "system sup" (etc)
+%%%                      |                  +--> "connection sup" (etc)
 %%%                      |
 %%%                      +-----> sshc_sup --+--> "system sup" (etc)
 %%%                                         |
diff -ruN a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src
--- a/lib/ssh/src/ssh.app.src	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/src/ssh.app.src	2025-12-22 23:16:56.259838907 +1030
@@ -36,7 +36,7 @@
 	     ssh_sftpd,
 	     ssh_sftpd_file,
 	     ssh_sftpd_file_api,
-	     ssh_subsystem_sup,
+	     ssh_connection_sup,
              ssh_tcpip_forward_client,
              ssh_tcpip_forward_srv,
              ssh_tcpip_forward_acceptor_sup,
@@ -51,7 +51,7 @@
                            ssh_acceptor,
                            ssh_channel_sup,
                            ssh_connection_handler,
-                           ssh_subsystem_sup,
+                           ssh_connection_sup,
                            ssh_system_sup
                           ]},
          {default_filter, rm} %% rm | filter
diff -ruN a/lib/ssh/src/ssh_connect.hrl b/lib/ssh/src/ssh_connect.hrl
--- a/lib/ssh/src/ssh_connect.hrl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/src/ssh_connect.hrl	2025-12-17 17:25:02.075556404 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2005-2021. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2024. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -269,5 +269,5 @@
           suggest_window_size,
           suggest_packet_size,
 	  exec,
-	  sub_system_supervisor
+	  connection_supervisor
 	 }).
diff -ruN a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl
--- a/lib/ssh/src/ssh_connection.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/src/ssh_connection.erl	2025-12-22 23:16:56.262838942 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2023. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -26,6 +26,8 @@
 
 -module(ssh_connection).
 
+-include_lib("kernel/include/logger.hrl").
+
 -include("ssh.hrl").
 -include("ssh_connect.hrl").
 -include("ssh_transport.hrl").
@@ -468,23 +470,50 @@
 %%% Replies {Reply, UpdatedConnection}
 %%%
 
+handle_msg(#ssh_msg_disconnect{code = Code, description = Description}, Connection, _, _SSH) ->
+    {disconnect, {Code, Description}, handle_stop(Connection)};
+
+handle_msg(Msg, Connection, server, Ssh = #ssh{authenticated = false}) ->
+    %% See RFC4252 6.
+    %% Message numbers of 80 and higher are reserved for protocols running
+    %% after this authentication protocol, so receiving one of them before
+    %% authentication is complete is an error, to which the server MUST
+    %% respond by disconnecting, preferably with a proper disconnect message
+    %% sent to ease troubleshooting.
+    MsgFun = fun(M) ->
+                     io_lib:format("Connection terminated. Unexpected message for unauthenticated user."
+                                   " Message:  ~w", [M],
+                                   [{chars_limit, ssh_lib:max_log_len(Ssh)}])
+             end,
+    ?LOG_DEBUG(MsgFun, [Msg]),
+    {disconnect, {?SSH_DISCONNECT_PROTOCOL_ERROR, "Connection refused"}, handle_stop(Connection)};
+
 handle_msg(#ssh_msg_channel_open_confirmation{recipient_channel = ChannelId, 
 					      sender_channel = RemoteId,
 					      initial_window_size = WindowSz,
 					      maximum_packet_size = PacketSz}, 
 	   #connection{channel_cache = Cache} = Connection0, _, _SSH) ->
     
-    #channel{remote_id = undefined} = Channel =
+    #channel{remote_id = undefined, user = U} = Channel =
 	ssh_client_channel:cache_lookup(Cache, ChannelId), 
     
-    ssh_client_channel:cache_update(Cache, Channel#channel{
-				     remote_id = RemoteId,
-				     recv_packet_size = max(32768, % rfc4254/5.2
-							    min(PacketSz, Channel#channel.recv_packet_size)
-							   ),
-				     send_window_size = WindowSz,
-				     send_packet_size = PacketSz}),
-    reply_msg(Channel, Connection0, {open, ChannelId});
+    if U /= undefined ->
+            ssh_client_channel:cache_update(Cache, Channel#channel{
+                                             remote_id = RemoteId,
+                                             recv_packet_size = max(32768, % rfc4254/5.2
+                                                                    min(PacketSz, Channel#channel.recv_packet_size)
+                                                                   ),
+                                             send_window_size = WindowSz,
+                                             send_packet_size = PacketSz}),
+            reply_msg(Channel, Connection0, {open, ChannelId});
+        true ->
+            %% There is no user process so nobody cares about the channel
+            %% close it and remove from the cache, reply from the peer will be
+            %% ignored
+            CloseMsg = channel_close_msg(RemoteId),
+            ssh_client_channel:cache_delete(Cache, ChannelId),
+            {[{connection_reply, CloseMsg}], Connection0}
+    end;
  
 handle_msg(#ssh_msg_channel_open_failure{recipient_channel = ChannelId,
 					 reason = Reason,
@@ -533,6 +562,10 @@
 		{Replies, Connection};
 
 	    undefined ->
+                %% This may happen among other reasons
+                %% - we sent 'channel-close' %% and the peer failed to respond in time
+                %% - we tried to open a channel but the handler died prematurely
+                %%    and the channel entry was removed from the cache
 		{[], Connection0}
 	end;
 
@@ -548,21 +581,23 @@
     channel_data_reply_msg(ChannelId, Connection, DataType, Data);
 
 handle_msg(#ssh_msg_channel_window_adjust{recipient_channel = ChannelId,
-					  bytes_to_add = Add}, 
+					  bytes_to_add = Add},
 	   #connection{channel_cache = Cache} = Connection, _, _SSH) ->
-    #channel{send_window_size = Size, remote_id = RemoteId} = 
-	Channel0 = ssh_client_channel:cache_lookup(Cache, ChannelId), 
-    
-    {SendList, Channel} =  %% TODO: Datatype 0 ?
-	update_send_window(Channel0#channel{send_window_size = Size + Add},
-			   0, undefined, Connection),
-    
-    Replies = lists:map(fun({Type, Data}) -> 
-				{connection_reply, channel_data_msg(RemoteId, Type, Data)}
-			end, SendList),
-    FlowCtrlMsgs = flow_control(Channel, Cache),
-    {Replies ++ FlowCtrlMsgs, Connection};
-
+    case ssh_client_channel:cache_lookup(Cache, ChannelId) of
+        Channel0 = #channel{send_window_size = Size,
+                            remote_id = RemoteId} ->
+            {SendList, Channel} =  %% TODO: Datatype 0 ?
+                update_send_window(Channel0#channel{send_window_size = Size + Add},
+                                   0, undefined, Connection),
+            Replies = lists:map(fun({Type, Data}) ->
+                                        {connection_reply,
+                                         channel_data_msg(RemoteId, Type, Data)}
+                                end, SendList),
+            FlowCtrlMsgs = flow_control(Channel, Cache),
+            {Replies ++ FlowCtrlMsgs, Connection};
+        undefined ->
+            {[], Connection}
+    end;
 handle_msg(#ssh_msg_channel_open{channel_type = "session" = Type,
 				 sender_channel = RemoteId,
 				 initial_window_size = WindowSz,
@@ -606,7 +641,7 @@
                        suggest_window_size = WinSz,
                        suggest_packet_size = PktSz,
                        options = Options,
-                       sub_system_supervisor = SubSysSup
+                       connection_supervisor = ConnectionSup
                       } = C,
 	   client, _SSH) ->
     {ReplyMsg, NextChId} =
@@ -614,7 +649,7 @@
             {ok, {ConnectToHost,ConnectToPort}} ->
                 case gen_tcp:connect(ConnectToHost, ConnectToPort, [{active,false}, binary]) of
                     {ok,Sock} ->
-                        {ok,Pid} = ssh_subsystem_sup:start_channel(client, SubSysSup, self(),
+                        {ok,Pid} = ssh_connection_sup:start_channel(client, ConnectionSup, self(),
                                                                    ssh_tcpip_forward_client, ChId,
                                                                    [Sock], undefined, Options),
                         ssh_client_channel:cache_update(Cache,
@@ -664,7 +699,7 @@
                        suggest_window_size = WinSz,
                        suggest_packet_size = PktSz,
                        options = Options,
-                       sub_system_supervisor = SubSysSup
+                       connection_supervisor = ConnectionSup
                       } = C,
 	   server, _SSH) ->
     {ReplyMsg, NextChId} =
@@ -680,7 +715,7 @@
                 case gen_tcp:connect(binary_to_list(HostToConnect), PortToConnect,
                                      [{active,false}, binary]) of
                     {ok,Sock} ->
-                        {ok,Pid} = ssh_subsystem_sup:start_channel(server, SubSysSup, self(),
+                        {ok,Pid} = ssh_connection_sup:start_channel(server, ConnectionSup, self(),
                                                                    ssh_tcpip_forward_srv, ChId,
                                                                    [Sock], undefined, Options),
                         ssh_client_channel:cache_update(Cache,
@@ -739,21 +774,35 @@
 handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
 				    request_type = "exit-signal",
 				    want_reply = false,
-				    data = Data},  
+				    data = Data},
            #connection{channel_cache = Cache} = Connection0, _, _SSH) ->
     <<?DEC_BIN(SigName, _SigLen),
-      ?BOOLEAN(_Core), 
+      ?BOOLEAN(_Core),
       ?DEC_BIN(Err, _ErrLen),
       ?DEC_BIN(Lang, _LangLen)>> = Data,
-    Channel = ssh_client_channel:cache_lookup(Cache, ChannelId),
-    RemoteId =  Channel#channel.remote_id,
-    {Reply, Connection} =  reply_msg(Channel, Connection0, 
-				     {exit_signal, ChannelId,
-				      binary_to_list(SigName),
-				      binary_to_list(Err),
-				      binary_to_list(Lang)}),
-    CloseMsg = channel_close_msg(RemoteId),
-    {[{connection_reply, CloseMsg}|Reply], Connection};
+    case ssh_client_channel:cache_lookup(Cache, ChannelId) of
+        #channel{remote_id = RemoteId, sent_close = SentClose} = Channel ->
+            {Reply, Connection} =  reply_msg(Channel, Connection0,
+                                             {exit_signal, ChannelId,
+                                              binary_to_list(SigName),
+                                              binary_to_list(Err),
+                                              binary_to_list(Lang)}),
+            %% Send 'channel-close' only if it has not been sent yet
+            %% by e.g. our side also closing the channel or going down
+            %% and(!) update the cache
+            %% so that the 'channel-close' is not sent twice
+            if not SentClose ->
+                    CloseMsg = channel_close_msg(RemoteId),
+                    ssh_client_channel:cache_update(Cache,
+                                            Channel#channel{sent_close = true}),
+                    {[{connection_reply, CloseMsg}|Reply], Connection};
+                true ->
+                    {Reply, Connection}
+            end;
+        _ ->
+            %% Channel already closed by peer
+            {[], Connection0}
+    end;
 
 handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
 				    request_type = "xon-xoff",
@@ -912,8 +961,8 @@
             {[{connection_reply, request_failure_msg()}], Connection};
 
         true ->
-            SubSysSup = ?GET_INTERNAL_OPT(subsystem_sup, Opts),
-            FwdSup = ssh_subsystem_sup:tcpip_fwd_supervisor(SubSysSup),
+            ConnectionSup = ?GET_INTERNAL_OPT(connection_sup, Opts),
+            FwdSup = ssh_connection_sup:tcpip_fwd_supervisor(ConnectionSup),
             ConnPid = self(),
             case ssh_tcpip_forward_acceptor:supervised_start(FwdSup,
                                                              {ListenAddrStr, ListenPort},
@@ -966,12 +1015,7 @@
 	   #connection{requests = [{_, From, Fun} | Rest]} = Connection0, _, _SSH) ->
     Connection = Fun({success,Data}, Connection0),
     {[{channel_request_reply, From, {success, Data}}],
-     Connection#connection{requests = Rest}};
-
-handle_msg(#ssh_msg_disconnect{code = Code,
-			       description = Description},
-	   Connection, _, _SSH) ->
-    {disconnect, {Code, Description}, handle_stop(Connection)}.
+     Connection#connection{requests = Rest}}.
 
 
 %%%----------------------------------------------------------------
@@ -1127,22 +1171,22 @@
 start_cli(#connection{options = Options, 
 		      cli_spec = CliSpec,
 		      exec = Exec,
-		      sub_system_supervisor = SubSysSup}, ChannelId) ->
+		      connection_supervisor = ConnectionSup}, ChannelId) ->
     case CliSpec of
         no_cli ->
             {error, cli_disabled};
         {CbModule, Args} ->
-            ssh_subsystem_sup:start_channel(server, SubSysSup, self(), CbModule, ChannelId, Args, Exec, Options)
+            ssh_connection_sup:start_channel(server, ConnectionSup, self(), CbModule, ChannelId, Args, Exec, Options)
     end.
 
 
 start_subsystem(BinName, #connection{options = Options,
-                                     sub_system_supervisor = SubSysSup},
+                                     connection_supervisor = ConnectionSup},
 	       #channel{local_id = ChannelId}, _ReplyMsg) ->
     Name = binary_to_list(BinName),
     case check_subsystem(Name, Options) of
 	{Callback, Opts} when is_atom(Callback), Callback =/= none ->
-            ssh_subsystem_sup:start_channel(server, SubSysSup, self(), Callback, ChannelId, Opts, undefined, Options);
+            ssh_connection_sup:start_channel(server, ConnectionSup, self(), Callback, ChannelId, Opts, undefined, Options);
         {none, _} ->
             {error, bad_subsystem};
 	{_, _} ->
diff -ruN a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
--- a/lib/ssh/src/ssh_connection_handler.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/src/ssh_connection_handler.erl	2025-12-22 23:16:56.264838965 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2023. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -34,7 +34,6 @@
 -include("ssh_transport.hrl").
 -include("ssh_auth.hrl").
 -include("ssh_connect.hrl").
-
 -include("ssh_fsm.hrl").
 
 %%====================================================================
@@ -42,7 +41,7 @@
 %%====================================================================
 
 %%% Start and stop
--export([start_link/4, start_link/5,
+-export([start_link/3, start_link/4,
          takeover/4,
 	 stop/1
 	]).
@@ -98,10 +97,10 @@
 %% Start / stop
 %%====================================================================
 
-start_link(Role, Address, Socket, Options) ->
-    start_link(Role, Address, undefined, Socket, Options).
+start_link(Role, Socket, Options) ->
+    start_link(Role, undefined, Socket, Options).
 
-start_link(Role, _Address=#address{}, Id, Socket, Options) ->
+start_link(Role, Id, Socket, Options) ->
     case gen_statem:start_link(?MODULE,
                                [Role, Socket, Options],
                                [{spawn_opt, [{message_queue_data,off_heap}]}]) of
@@ -110,7 +109,7 @@
             %% Announce the ConnectionRef to the system supervisor so it could
             %%   1) initiate the socket handover, and
             %%   2) be returned to whoever called for example ssh:connect; the Pid
-            %%      returned from this function is "consumed" by the subsystem
+            %%      returned from this function is "consumed" by the connection
             %%      supervisor.
             ?GET_INTERNAL_OPT(user_pid,Options) ! {new_connection_ref, Id, Pid},
             {ok, Pid};
@@ -119,21 +118,23 @@
             Others
     end.
 
-
-takeover(ConnPid, client, Socket, Options) ->
-    group_leader(group_leader(), ConnPid),
-    takeover(ConnPid, common, Socket, Options);
-
-takeover(ConnPid, _, Socket, Options) ->
+takeover(ConnPid, Role, Socket, Options) ->
+    case Role of
+        client ->
+            group_leader(group_leader(), ConnPid);
+        _ ->
+            ok
+    end,
     {_, Callback, _} = ?GET_OPT(transport, Options),
     case Callback:controlling_process(Socket, ConnPid) of
         ok ->
+            Ref = erlang:monitor(process, ConnPid),
             gen_statem:cast(ConnPid, socket_control),
             NegTimeout = ?GET_INTERNAL_OPT(negotiation_timeout,
                                            Options,
                                            ?GET_OPT(negotiation_timeout, Options)
                                           ),
-            handshake(ConnPid, erlang:monitor(process,ConnPid), NegTimeout);
+            handshake(ConnPid, Role, Ref, NegTimeout);
         {error, Reason}	->
             {error, Reason}
     end.
@@ -195,8 +196,8 @@
 
 %% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 start_channel(ConnectionHandler, CallbackModule, ChannelId, Args, Exec) ->
-    {ok, {SubSysSup,Role,Opts}} = call(ConnectionHandler, get_misc),
-    ssh_subsystem_sup:start_channel(Role, SubSysSup,
+    {ok, {ConnectionSup,Role,Opts}} = call(ConnectionHandler, get_misc),
+    ssh_connection_sup:start_channel(Role, ConnectionSup,
                                     ConnectionHandler, CallbackModule, ChannelId,
                                     Args, Exec, Opts).
 
@@ -416,7 +417,7 @@
                     suggest_packet_size = PktSz,
                     requests = [],
                     options = Opts,
-                    sub_system_supervisor = ?GET_INTERNAL_OPT(subsystem_sup, Opts)
+                    connection_supervisor = ?GET_INTERNAL_OPT(connection_sup, Opts)
                    },
     case Role of
         server ->
@@ -485,25 +486,41 @@
 		  }
     end.
 
-
-handshake(Pid, Ref, Timeout) ->
+handshake(ConnPid, server, Ref, Timeout) ->
     receive
-	{Pid, ssh_connected} ->
+	{ConnPid, ssh_connected} ->
 	    erlang:demonitor(Ref, [flush]),
-	    {ok, Pid};
-	{Pid, {not_connected, Reason}} ->
+	    {ok, ConnPid};
+	{ConnPid, {not_connected, Reason}} ->
 	    erlang:demonitor(Ref, [flush]),
 	    {error, Reason};
-	{'DOWN', Ref, process, Pid, {shutdown, Reason}} ->
+	{'DOWN', Ref, process, ConnPid, {shutdown, Reason}} ->
 	    {error, Reason};
-	{'DOWN', Ref, process, Pid, Reason} ->
+	{'DOWN', Ref, process, ConnPid, Reason} ->
 	    {error, Reason};
         {'EXIT',_,Reason} ->
-            stop(Pid),
+            stop(ConnPid),
             {error, {exit,Reason}}
     after Timeout ->
 	    erlang:demonitor(Ref, [flush]),
-	    ssh_connection_handler:stop(Pid),
+	    ssh_connection_handler:stop(ConnPid),
+	    {error, timeout}
+    end;
+handshake(ConnPid, client, Ref, Timeout) ->
+    receive
+	{ConnPid, ssh_connected} ->
+	    erlang:demonitor(Ref, [flush]),
+	    {ok, ConnPid};
+	{ConnPid, {not_connected, Reason}} ->
+	    erlang:demonitor(Ref, [flush]),
+	    {error, Reason};
+	{'DOWN', Ref, process, ConnPid, {shutdown, Reason}} ->
+	    {error, Reason};
+	{'DOWN', Ref, process, ConnPid, Reason} ->
+	    {error, Reason}
+    after Timeout ->
+	    erlang:demonitor(Ref, [flush]),
+	    ssh_connection_handler:stop(ConnPid),
 	    {error, timeout}
     end.
 
@@ -592,17 +609,18 @@
 handle_event(internal, socket_ready, {hello,_}=StateName, #data{ssh_params = Ssh0} = D) ->
     VsnMsg = ssh_transport:hello_version_msg(string_version(Ssh0)),
     send_bytes(VsnMsg, D),
-    case inet:getopts(Socket=D#data.socket, [recbuf]) of
-	{ok, [{recbuf,Size}]} ->
+    case inet:getopts(Socket=D#data.socket, [buffer]) of
+	{ok, [{buffer,Size}]} ->
 	    %% Set the socket to the hello text line handling mode:
 	    inet:setopts(Socket, [{packet, line},
 				  {active, once},
 				  % Expecting the version string which might
 				  % be max ?MAX_PROTO_VERSION bytes:
-				  {recbuf, ?MAX_PROTO_VERSION},
+				  {buffer, ?MAX_PROTO_VERSION},
+				  {packet_size, ?MAX_PROTO_VERSION},
 				  {nodelay,true}]),
             Time = ?GET_OPT(hello_timeout, Ssh0#ssh.opts, infinity),
-	    {keep_state, D#data{inet_initial_recbuf_size=Size}, [{state_timeout,Time,no_hello_received}] };
+	    {keep_state, D#data{inet_initial_buffer_size=Size}, [{state_timeout,Time,no_hello_received}] };
 
 	Other ->
             ?call_disconnectfun_and_log_cond("Option return", 
@@ -631,11 +649,12 @@
     case handle_version(NumVsn, StrVsn, D0#data.ssh_params) of
 	{ok, Ssh1} ->
 	    %% Since the hello part is finished correctly, we set the
-	    %% socket to the packet handling mode (including recbuf size):
+	    %% socket to the packet handling mode (including buffer size):
 	    inet:setopts(D0#data.socket, [{packet,0},
 					 {mode,binary},
 					 {active, once},
-					 {recbuf, D0#data.inet_initial_recbuf_size}]),
+					 {buffer, D0#data.inet_initial_buffer_size},
+					 {packet_size, 0}]),
 	    {KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(Ssh1),
 	    send_bytes(SshPacket, D0),
             D = D0#data{ssh_params = Ssh,
@@ -653,17 +672,24 @@
 
 %%% timeout after tcp:connect but then nothing arrives
 handle_event(state_timeout, no_hello_received, {hello,_Role}=StateName, D0 = #data{ssh_params = Ssh0}) ->
-    Time = ?GET_OPT(hello_timeout, Ssh0#ssh.opts),
+    MsgFun =
+        fun (debug) ->
+                Time = ?GET_OPT(hello_timeout, Ssh0#ssh.opts),
+                lists:concat(["No HELLO received within ",ssh_lib:format_time_ms(Time)]);
+            (_) ->
+                ["No HELLO received within hello_timeout"]
+        end,
     {Shutdown, D} =
-        ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR,
-                         lists:concat(["No HELLO received within ",ssh_lib:format_time_ms(Time)]),
-                         StateName, D0),
+        ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR, ?SELECT_MSG(MsgFun), StateName, D0),
     {stop, Shutdown, D};
 
 
-%%% ######## {service_request, client|server} ####
-
-handle_event(internal, Msg = #ssh_msg_service_request{name=ServiceName}, StateName = {service_request,server}, D0) ->
+%%% ######## {service_request, client|server} #### StateName ==
+%% {userauth,server} guard added due to interoperability with clients
+%% sending extra ssh_msg_service_request (e.g. Paramiko for Python,
+%% see GH-6463)
+handle_event(internal, Msg = #ssh_msg_service_request{name=ServiceName}, StateName, D0)
+  when StateName == {service_request,server}; StateName == {userauth,server} ->
     case ServiceName of
 	"ssh-userauth" ->
 	    Ssh0 = #ssh{session_id=SessionId} = D0#data.ssh_params,
@@ -706,16 +732,6 @@
     disconnect_fun("Received disconnect: "++Desc, D),
     {stop_and_reply, {shutdown,Desc}, Actions, D};
 
-handle_event(internal, #ssh_msg_ignore{}, {_StateName, _Role, init},
-             #data{ssh_params = #ssh{kex_strict_negotiated = true,
-                                     send_sequence = SendSeq,
-                                     recv_sequence = RecvSeq}}) ->
-    ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
-                io_lib:format("strict KEX violation: unexpected SSH_MSG_IGNORE "
-                              "send_sequence = ~p  recv_sequence = ~p",
-                              [SendSeq, RecvSeq])
-               );
-
 handle_event(internal, #ssh_msg_ignore{}, _StateName, _) ->
     keep_state_and_data;
 
@@ -1017,8 +1033,8 @@
 
 handle_event({call,From}, get_misc, StateName,
              #data{connection_state = #connection{options = Opts}} = D) when ?CONNECTED(StateName) ->
-    SubSysSup = ?GET_INTERNAL_OPT(subsystem_sup, Opts),
-    Reply = {ok, {SubSysSup, ?role(StateName), Opts}},
+    ConnectionSup = ?GET_INTERNAL_OPT(connection_sup, Opts),
+    Reply = {ok, {ConnectionSup, ?role(StateName), Opts}},
     {keep_state, D, [{reply,From,Reply}]};
 
 handle_event({call,From},
@@ -1074,12 +1090,22 @@
 
 handle_event({call,From}, {close, ChannelId}, StateName, D0)
   when ?CONNECTED(StateName) ->
+    %% Send 'channel-close' only if it has not been sent yet
+    %% e.g. when 'exit-signal' was received from the peer
+    %% and(!) we update the cache so that we remember what we've done
     case ssh_client_channel:cache_lookup(cache(D0), ChannelId) of
-	#channel{remote_id = Id} = Channel ->
+	#channel{remote_id = Id, sent_close = false} = Channel ->
 	    D1 = send_msg(ssh_connection:channel_close_msg(Id), D0),
-	    ssh_client_channel:cache_update(cache(D1), Channel#channel{sent_close = true}),
-	    {keep_state, D1, [cond_set_idle_timer(D1), {reply,From,ok}]};
-	undefined ->
+	    ssh_client_channel:cache_update(cache(D1),
+                                            Channel#channel{sent_close = true}),
+	    {keep_state, D1, [cond_set_idle_timer(D1),
+                              channel_close_timer(D1, Id),
+                              {reply,From,ok}]};
+	_ ->
+            %% Here we match a channel which has already sent 'channel-close'
+            %% AND possible cases of 'broken cache' i.e. when a channel
+            %% disappeared from the cache, but has not been properly shut down
+            %% The latter would be a bug, but hard to chase
 	    {keep_state_and_data, [{reply,From,ok}]}
     end;
 
@@ -1119,11 +1145,14 @@
     of
 	{packet_decrypted, DecryptedBytes, EncryptedDataRest, Ssh1} ->
 	    D1 = D0#data{ssh_params =
-			    Ssh1#ssh{recv_sequence = ssh_transport:next_seqnum(Ssh1#ssh.recv_sequence)},
-			decrypted_data_buffer = <<>>,
-                        undecrypted_packet_length = undefined,
-                        aead_data = <<>>,
-			encrypted_data_buffer = EncryptedDataRest},
+                             Ssh1#ssh{recv_sequence =
+                                          ssh_transport:next_seqnum(StateName,
+                                                                    Ssh1#ssh.recv_sequence,
+                                                                    SshParams)},
+                         decrypted_data_buffer = <<>>,
+                         undecrypted_packet_length = undefined,
+                         aead_data = <<>>,
+                         encrypted_data_buffer = EncryptedDataRest},
 	    try
 		ssh_message:decode(set_kex_overload_prefix(DecryptedBytes,D1))
 	    of
@@ -1155,12 +1184,21 @@
                                       {next_event, internal, Msg}
 				    ]}
 	    catch
-		C:E:ST  ->
-                    MaxLogItemLen = ?GET_OPT(max_log_item_len,SshParams#ssh.opts),
+		Class:Reason0:Stacktrace  ->
+                    Reason = ssh_lib:trim_reason(Reason0),
+                    MsgFun =
+                        fun(debug) ->
+                                io_lib:format("Bad packet: Decrypted, but can't decode~n~p:~p~n~p",
+                                              [Class,Reason,Stacktrace],
+                                              [{chars_limit, ssh_lib:max_log_len(SshParams)}]);
+                           (_) ->
+                                io_lib:format("Bad packet: Decrypted, but can't decode ~p:~p",
+                                              [Class, Reason],
+                                              [{chars_limit, ssh_lib:max_log_len(SshParams)}])
+                        end,
                     {Shutdown, D} =
                         ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR,
-                                         io_lib:format("Bad packet: Decrypted, but can't decode~n~p:~p~n~P",
-                                                       [C,E,ST,MaxLogItemLen]),
+                                         ?SELECT_MSG(MsgFun),
                                          StateName, D1),
                     {stop, Shutdown, D}
 	    end;
@@ -1190,12 +1228,20 @@
                                  StateName, D0),
             {stop, Shutdown, D}
     catch
-	C:E:ST ->
-            MaxLogItemLen = ?GET_OPT(max_log_item_len,SshParams#ssh.opts),
+	Class:Reason0:Stacktrace ->
+            MsgFun =
+                fun(debug) ->
+                        io_lib:format("Bad packet: Couldn't decrypt~n~p:~p~n~p",
+                                      [Class,Reason0,Stacktrace],
+                                      [{chars_limit, ssh_lib:max_log_len(SshParams)}]);
+                   (_) ->
+                        Reason = ssh_lib:trim_reason(Reason0),
+                        io_lib:format("Bad packet: Couldn't decrypt~n~p:~p",
+                                      [Class,Reason],
+                                      [{chars_limit, ssh_lib:max_log_len(SshParams)}])
+                end,
             {Shutdown, D} =
-                ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR,
-                                 io_lib:format("Bad packet: Couldn't decrypt~n~p:~p~n~P",
-                                               [C,E,ST,MaxLogItemLen]),
+                ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR, ?SELECT_MSG(MsgFun),
                                  StateName, D0),
             {stop, Shutdown, D}
     end;
@@ -1237,15 +1283,33 @@
 %%% Handle that ssh channels user process goes down
 handle_event(info, {'DOWN', _Ref, process, ChannelPid, _Reason}, _, D) ->
     Cache = cache(D),
-    ssh_client_channel:cache_foldl(
-      fun(#channel{user=U,
-                   local_id=Id}, Acc) when U == ChannelPid ->
-              ssh_client_channel:cache_delete(Cache, Id),
-              Acc;
-         (_,Acc) ->
-              Acc
-      end, [], Cache),
-    {keep_state, D, cond_set_idle_timer(D)};
+    %% Here we first collect the list of channel id's  handled by the process
+    %% Do NOT remove them from the cache - they are not closed yet!
+    Channels = ssh_client_channel:cache_foldl(
+                 fun(#channel{user=U} = Channel, Acc) when U == ChannelPid ->
+                         [Channel | Acc];
+                    (_,Acc) ->
+                         Acc
+                 end, [], Cache),
+    %% Then for each channel where 'channel-close' has not been sent yet
+    %% we send 'channel-close' and(!) update the cache so that we remember
+    %% what we've done.
+    %% Also set user as 'undefined' as there is no such process anyway
+    {D2, NewTimers} = lists:foldl(
+                        fun(#channel{remote_id = Id, sent_close = false} = Channel,
+                            {D0, Timers}) when Id /= undefined ->
+                                D1 = send_msg(ssh_connection:channel_close_msg(Id), D0),
+                                ssh_client_channel:cache_update(cache(D1),
+                                                                Channel#channel{sent_close = true,
+                                                                                user = undefined}),
+                                ChannelTimer = channel_close_timer(D1, Id),
+                                {D1, [ChannelTimer | Timers]};
+                           (Channel, {D0, _} = Acc) ->
+                                ssh_client_channel:cache_update(cache(D0),
+                                                                Channel#channel{user = undefined}),
+                                Acc
+                        end, {D, []}, Channels),
+    {keep_state, D2, [cond_set_idle_timer(D2) | NewTimers]};
 
 handle_event({timeout,idle_time}, _Data,  _StateName, D) ->
     case ssh_client_channel:cache_info(num_entries, cache(D)) of
@@ -1258,6 +1322,16 @@
 handle_event({timeout,max_initial_idle_time}, _Data,  _StateName, _D) ->
     {stop, {shutdown, "Timeout"}};
 
+handle_event({timeout, {channel_close, ChannelId}}, _Data, _StateName, D) ->
+    Cache = cache(D),
+    case ssh_client_channel:cache_lookup(Cache, ChannelId) of
+        #channel{sent_close = true} ->
+            ssh_client_channel:cache_delete(Cache, ChannelId),
+            {keep_state, D, cond_set_idle_timer(D)};
+        _ ->
+            keep_state_and_data
+    end;
+
 %%% So that terminate will be run when supervisor is shutdown
 handle_event(info, {'EXIT', _Sup, Reason}, StateName, _D) ->
     Role = ?role(StateName),
@@ -1281,9 +1355,9 @@
 handle_event(info, {fwd_connect_received, Sock, ChId, ChanCB}, StateName, #data{connection_state = Connection}) ->
     #connection{options = Options,
                 channel_cache = Cache,
-                sub_system_supervisor = SubSysSup} = Connection,
+                connection_supervisor = ConnectionSup} = Connection,
     Channel = ssh_client_channel:cache_lookup(Cache, ChId),
-    {ok,Pid} = ssh_subsystem_sup:start_channel(?role(StateName), SubSysSup, self(), ChanCB, ChId, [Sock], undefined, Options),
+    {ok,Pid} = ssh_connection_sup:start_channel(?role(StateName), ConnectionSup, self(), ChanCB, ChId, [Sock], undefined, Options),
     ssh_client_channel:cache_update(Cache, Channel#channel{user=Pid}),
     gen_tcp:controlling_process(Sock, Pid),
     inet:setopts(Sock, [{active,once}]),
@@ -1292,8 +1366,8 @@
 handle_event({call,From},
              {handle_direct_tcpip, ListenHost, ListenPort, ConnectToHost, ConnectToPort, _Timeout},
              _StateName,
-             #data{connection_state = #connection{sub_system_supervisor=SubSysSup}}) ->
-    case ssh_tcpip_forward_acceptor:supervised_start(ssh_subsystem_sup:tcpip_fwd_supervisor(SubSysSup),
+             #data{connection_state = #connection{connection_supervisor=ConnectionSup}}) ->
+    case ssh_tcpip_forward_acceptor:supervised_start(ssh_connection_sup:tcpip_fwd_supervisor(ConnectionSup),
                                                      {ListenHost, ListenPort},
                                                      {ConnectToHost, ConnectToPort},
                                                      "direct-tcpip", ssh_tcpip_forward_client,
@@ -2030,6 +2104,10 @@
         _ -> {{timeout,idle_time}, infinity, none}
     end.
 
+channel_close_timer(D, ChannelId) ->
+    {{timeout, {channel_close, ChannelId}},
+     ?GET_OPT(channel_close_timeout, (D#data.ssh_params)#ssh.opts), none}.
+
 %%%----------------------------------------------------------------
 start_channel_request_timer(_,_, infinity) ->
     ok;
diff -ruN a/lib/ssh/src/ssh_connection_sup.erl b/lib/ssh/src/ssh_connection_sup.erl
--- a/lib/ssh/src/ssh_connection_sup.erl	1970-01-01 09:30:00.000000000 +0930
+++ b/lib/ssh/src/ssh_connection_sup.erl	2025-12-22 23:16:56.264838965 +1030
@@ -0,0 +1,105 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2024. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%%     http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+%%
+%%----------------------------------------------------------------------
+%% Purpose: The ssh connection supervisor
+%%----------------------------------------------------------------------
+
+-module(ssh_connection_sup).
+
+-behaviour(supervisor).
+
+-include("ssh.hrl").
+
+-export([start_link/4,
+         start_channel/8,
+         tcpip_fwd_supervisor/1
+	]).
+
+%% Supervisor callback
+-export([init/1]).
+
+%%%=========================================================================
+%%%  API
+%%%=========================================================================
+start_link(Role, Id, Socket, Options) ->
+    case supervisor:start_link(?MODULE, [Role, Id, Socket, Options]) of
+        {error, {shutdown, {failed_to_start_child, _, Error}}} ->
+            {error,Error};
+        Other ->
+            Other
+    end.
+
+start_channel(Role, SupPid, ConnRef, Callback, Id, Args, Exec, Opts) ->
+    ChannelSup = channel_supervisor(SupPid),
+    ssh_channel_sup:start_child(Role, ChannelSup, ConnRef, Callback, Id, Args, Exec, Opts).
+
+tcpip_fwd_supervisor(ConnectionSup) ->
+    find_child(tcpip_forward_acceptor_sup, ConnectionSup).
+
+
+%%%=========================================================================
+%%%  Supervisor callback
+%%%=========================================================================
+init([Role, Id, Socket, Options]) ->
+    ConnectionSup = self(),
+    SupFlags = #{strategy      => one_for_all,
+                 auto_shutdown => any_significant,
+                 intensity     =>    0,
+                 period        => 3600},
+    ChildSpecs =
+        [#{id          => connection,
+           restart     => temporary,
+           type        => worker,
+           significant => true,
+           start       => {ssh_connection_handler,
+                           start_link,
+                           [Role, Id, Socket,
+                            ?PUT_INTERNAL_OPT([{connection_sup, ConnectionSup}], Options)]}
+          },
+         #{id      => channel_sup,
+           restart => temporary,
+           type    => supervisor,
+           start   => {ssh_channel_sup, start_link, [Options]}
+          },
+
+         #{id      => tcpip_forward_acceptor_sup,
+           restart => temporary,
+           type    => supervisor,
+           start   => {ssh_tcpip_forward_acceptor_sup, start_link, []}
+          }],
+    {ok, {SupFlags,ChildSpecs}}.
+
+%%%=========================================================================
+%%%  Internal functions
+%%%=========================================================================
+channel_supervisor(ConnectionSup) -> find_child(channel_sup, ConnectionSup).
+
+find_child(Id, Sup) when is_pid(Sup) ->
+    try
+       {Id, Pid, _, _} = lists:keyfind(Id, 1, supervisor:which_children(Sup)),
+       Pid
+    catch
+        exit:{no_proc,_} ->
+            {error, no_proc};
+        _:_ ->
+            {error, {id_not_found,?MODULE,Id}}
+    end.
+
diff -ruN a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
--- a/lib/ssh/src/ssh.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/src/ssh.erl	2025-12-22 23:16:56.261838930 +1030
@@ -1,7 +1,7 @@
 %
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2004-2023. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -189,7 +189,7 @@
             {error, Reason};
 
         Options ->
-            SocketOpts = [{active,false} | ?GET_OPT(socket_options,Options)],
+            SocketOpts = ?GET_OPT(socket_options,Options) ++ [{active,false}],
             Host = mangle_connect_address(Host0, Options),
             try
                 transport_connect(Host, Port, SocketOpts, Options)
@@ -248,7 +248,7 @@
                        port = SockPort,
                        profile = ?GET_OPT(profile,Options)
                       },
-    ssh_system_sup:start_subsystem(client, Address, Socket, Options).
+    ssh_system_sup:start_connection(client, Address, Socket, Options).
 
 %%--------------------------------------------------------------------
 -spec close(ConnectionRef) -> ok | {error,term()} when
@@ -287,16 +287,18 @@
       | {options, client_options()}
       | {algorithms, conn_info_algs()}
       | {channels, conn_info_channels()}.
-        
--spec connection_info(ConnectionRef) -> InfoTupleList when
+
+-spec connection_info(ConnectionRef) ->
+          InfoTupleList | {error, term()} when
       ConnectionRef :: connection_ref(),
       InfoTupleList :: [InfoTuple],
       InfoTuple :: connection_info_tuple().
 
-connection_info(ConnectionRef) ->                                      
+connection_info(ConnectionRef) ->
     connection_info(ConnectionRef, []).
 
--spec connection_info(ConnectionRef, ItemList|Item) ->  InfoTupleList|InfoTuple when
+-spec connection_info(ConnectionRef, ItemList|Item) ->
+          InfoTupleList | InfoTuple | {error, term()} when
       ConnectionRef :: connection_ref(),
       ItemList :: [Item],
       Item :: client_version | server_version | user | peer | sockname | options | algorithms | sockname,
@@ -343,7 +345,7 @@
                                            profile = ?GET_OPT(profile,Options0)
                                           },
                         Options = ?PUT_INTERNAL_OPT({connected_socket, Socket}, Options0),
-                        case ssh_system_sup:start_subsystem(server, Address, Socket, Options) of
+                        case ssh_system_sup:start_connection(server, Address, Socket, Options) of
                             {ok,Pid} ->
                                 {ok,Pid};
                             {error, {already_started, _}} ->
@@ -392,8 +394,7 @@
 
                 %% throws error:Error if no usable hostkey is found
                 ssh_connection_handler:available_hkey_algorithms(server, Options1),
-                ssh_system_sup:start_system(server,
-                                            #address{address = Host,
+                ssh_system_sup:start_system(#address{address = Host,
                                                      port = Port,
                                                      profile = ?GET_OPT(profile,Options1)},
                                             Options1)
@@ -450,9 +451,13 @@
       NewUserOptions :: daemon_options().
 
 daemon_replace_options(DaemonRef, NewUserOptions) ->
-    {ok,Os0} = ssh_system_sup:get_acceptor_options(DaemonRef),
-    Os1 = ssh_options:merge_options(server, NewUserOptions, Os0),
-    ssh_system_sup:replace_acceptor_options(DaemonRef, Os1).
+    case ssh_system_sup:get_acceptor_options(DaemonRef) of
+        {ok, Os0} ->
+            Os1 = ssh_options:merge_options(server, NewUserOptions, Os0),
+            ssh_system_sup:replace_acceptor_options(DaemonRef, Os1);
+        {error, _Reason} = Error ->
+            Error
+    end.
 
 %%--------------------------------------------------------------------
 -type daemon_info_tuple() ::
@@ -540,8 +545,7 @@
     lists:foreach(fun({Sup,_Addr}) ->
                           stop_listener(Sup)
                   end,
-                  ssh_system_sup:addresses(server,
-                                           #address{address=Address,
+                  ssh_system_sup:addresses(#address{address=Address,
                                                     port=Port,
                                                     profile=Profile})).
 
@@ -552,7 +556,7 @@
 -spec stop_daemon(DaemonRef::daemon_ref()) -> ok.
 
 stop_daemon(SysSup) ->
-    ssh_system_sup:stop_system(server, SysSup).
+    ssh_system_sup:stop_system(SysSup).
 
 
 -spec stop_daemon(inet:ip_address(), inet:port_number()) -> ok.
@@ -567,8 +571,7 @@
     lists:foreach(fun({Sup,_Addr}) ->
                           stop_daemon(Sup)
                   end,
-                  ssh_system_sup:addresses(server,
-                                           #address{address=Address,
+                  ssh_system_sup:addresses(#address{address=Address,
                                                     port=Port,
                                                     profile=Profile})).
 
diff -ruN a/lib/ssh/src/ssh_fsm.hrl b/lib/ssh/src/ssh_fsm.hrl
--- a/lib/ssh/src/ssh_fsm.hrl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/src/ssh_fsm.hrl	2025-12-17 17:25:02.076556417 +1030
@@ -37,7 +37,7 @@
 						 | undefined,
 	  last_size_rekey           = 0         :: non_neg_integer(),
 	  event_queue               = []        :: list(),
-	  inet_initial_recbuf_size              :: pos_integer()
+	  inet_initial_buffer_size              :: pos_integer()
 						 | undefined
 	 }).
 
diff -ruN a/lib/ssh/src/ssh_fsm_kexinit.erl b/lib/ssh/src/ssh_fsm_kexinit.erl
--- a/lib/ssh/src/ssh_fsm_kexinit.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/src/ssh_fsm_kexinit.erl	2025-12-22 23:16:56.264838965 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2023. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -43,6 +43,11 @@
 -export([callback_mode/0, handle_event/4, terminate/3,
 	 format_status/2, code_change/4]).
 
+-behaviour(ssh_dbg).
+-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1,
+         ssh_dbg_on/1, ssh_dbg_off/1,
+         ssh_dbg_format/2]).
+
 %%====================================================================
 %% gen_statem callbacks
 %%====================================================================
@@ -53,8 +58,13 @@
 
 %%--------------------------------------------------------------------
 
-%%% ######## {kexinit, client|server, init|renegotiate} ####
 
+handle_event(Type, Event = prepare_next_packet, StateName, D) ->
+    ssh_connection_handler:handle_event(Type, Event, StateName, D);
+handle_event(Type, Event = {send_disconnect, _, _, _, _}, StateName, D) ->
+    ssh_connection_handler:handle_event(Type, Event, StateName, D);
+
+%%% ######## {kexinit, client|server, init|renegotiate} ####
 handle_event(internal, {#ssh_msg_kexinit{}=Kex, Payload}, {kexinit,Role,ReNeg},
 	     D = #data{key_exchange_init_msg = OwnKex}) ->
     Ssh1 = ssh_transport:key_init(peer_role(Role), D#data.ssh_params, Payload),
@@ -67,11 +77,10 @@
 	  end,
     {next_state, {key_exchange,Role,ReNeg}, D#data{ssh_params=Ssh}};
 
-
 %%% ######## {key_exchange, client|server, init|renegotiate} ####
-
 %%%---- diffie-hellman
 handle_event(internal, #ssh_msg_kexdh_init{} = Msg, {key_exchange,server,ReNeg}, D) ->
+    ok = check_kex_strict(Msg, D),
     {ok, KexdhReply, Ssh1} = ssh_transport:handle_kexdh_init(Msg, D#data.ssh_params),
     ssh_connection_handler:send_bytes(KexdhReply, D),
     {ok, NewKeys, Ssh2} = ssh_transport:new_keys_message(Ssh1),
@@ -81,6 +90,7 @@
     {next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}};
 
 handle_event(internal, #ssh_msg_kexdh_reply{} = Msg, {key_exchange,client,ReNeg}, D) ->
+    ok = check_kex_strict(Msg, D),
     {ok, NewKeys, Ssh1} = ssh_transport:handle_kexdh_reply(Msg, D#data.ssh_params),
     ssh_connection_handler:send_bytes(NewKeys, D),
     {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh1),
@@ -89,24 +99,28 @@
 
 %%%---- diffie-hellman group exchange
 handle_event(internal, #ssh_msg_kex_dh_gex_request{} = Msg, {key_exchange,server,ReNeg}, D) ->
+    ok = check_kex_strict(Msg, D),
     {ok, GexGroup, Ssh1} = ssh_transport:handle_kex_dh_gex_request(Msg, D#data.ssh_params),
     ssh_connection_handler:send_bytes(GexGroup, D),
     Ssh = ssh_transport:parallell_gen_key(Ssh1),
     {next_state, {key_exchange_dh_gex_init,server,ReNeg}, D#data{ssh_params=Ssh}};
 
 handle_event(internal, #ssh_msg_kex_dh_gex_request_old{} = Msg, {key_exchange,server,ReNeg}, D) ->
+    ok = check_kex_strict(Msg, D),
     {ok, GexGroup, Ssh1} = ssh_transport:handle_kex_dh_gex_request(Msg, D#data.ssh_params),
     ssh_connection_handler:send_bytes(GexGroup, D),
     Ssh = ssh_transport:parallell_gen_key(Ssh1),
     {next_state, {key_exchange_dh_gex_init,server,ReNeg}, D#data{ssh_params=Ssh}};
 
 handle_event(internal, #ssh_msg_kex_dh_gex_group{} = Msg, {key_exchange,client,ReNeg}, D) ->
+    ok = check_kex_strict(Msg, D),
     {ok, KexGexInit, Ssh} = ssh_transport:handle_kex_dh_gex_group(Msg, D#data.ssh_params),
     ssh_connection_handler:send_bytes(KexGexInit, D),
     {next_state, {key_exchange_dh_gex_reply,client,ReNeg}, D#data{ssh_params=Ssh}};
 
 %%%---- elliptic curve diffie-hellman
 handle_event(internal, #ssh_msg_kex_ecdh_init{} = Msg, {key_exchange,server,ReNeg}, D) ->
+    ok = check_kex_strict(Msg, D),
     {ok, KexEcdhReply, Ssh1} = ssh_transport:handle_kex_ecdh_init(Msg, D#data.ssh_params),
     ssh_connection_handler:send_bytes(KexEcdhReply, D),
     {ok, NewKeys, Ssh2} = ssh_transport:new_keys_message(Ssh1),
@@ -116,16 +130,25 @@
     {next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}};
 
 handle_event(internal, #ssh_msg_kex_ecdh_reply{} = Msg, {key_exchange,client,ReNeg}, D) ->
+    ok = check_kex_strict(Msg, D),
     {ok, NewKeys, Ssh1} = ssh_transport:handle_kex_ecdh_reply(Msg, D#data.ssh_params),
     ssh_connection_handler:send_bytes(NewKeys, D),
     {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh1),
     ssh_connection_handler:send_bytes(ExtInfo, D),
     {next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh}};
 
+%%% ######## handle KEX strict
+handle_event(internal, _Event, {key_exchange,_Role,init},
+             #data{ssh_params = #ssh{algorithms = #alg{kex_strict_negotiated = true},
+                                     send_sequence = SendSeq,
+                                     recv_sequence = RecvSeq}}) ->
+    ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+                io_lib:format("KEX strict violation: send_sequence = ~p  recv_sequence = ~p",
+                              [SendSeq, RecvSeq]));
 
 %%% ######## {key_exchange_dh_gex_init, server, init|renegotiate} ####
-
 handle_event(internal, #ssh_msg_kex_dh_gex_init{} = Msg, {key_exchange_dh_gex_init,server,ReNeg}, D) ->
+    ok = check_kex_strict(Msg, D),
     {ok, KexGexReply, Ssh1} =  ssh_transport:handle_kex_dh_gex_init(Msg, D#data.ssh_params),
     ssh_connection_handler:send_bytes(KexGexReply, D),
     {ok, NewKeys, Ssh2} = ssh_transport:new_keys_message(Ssh1),
@@ -133,20 +156,33 @@
     {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh2),
     ssh_connection_handler:send_bytes(ExtInfo, D),
     {next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}};
-
+%%% ######## handle KEX strict
+handle_event(internal, _Event, {key_exchange_dh_gex_init,_Role,init},
+             #data{ssh_params = #ssh{algorithms = #alg{kex_strict_negotiated = true},
+                                     send_sequence = SendSeq,
+                                     recv_sequence = RecvSeq}}) ->
+    ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+                io_lib:format("KEX strict violation: send_sequence = ~p  recv_sequence = ~p",
+                              [SendSeq, RecvSeq]));
 
 %%% ######## {key_exchange_dh_gex_reply, client, init|renegotiate} ####
-
 handle_event(internal, #ssh_msg_kex_dh_gex_reply{} = Msg, {key_exchange_dh_gex_reply,client,ReNeg}, D) ->
+    ok = check_kex_strict(Msg, D),
     {ok, NewKeys, Ssh1} = ssh_transport:handle_kex_dh_gex_reply(Msg, D#data.ssh_params),
     ssh_connection_handler:send_bytes(NewKeys, D),
     {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh1),
     ssh_connection_handler:send_bytes(ExtInfo, D),
     {next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh}};
-
+%%% ######## handle KEX strict
+handle_event(internal, _Event, {key_exchange_dh_gex_reply,_Role,init},
+             #data{ssh_params = #ssh{algorithms = #alg{kex_strict_negotiated = true},
+                                     send_sequence = SendSeq,
+                                     recv_sequence = RecvSeq}}) ->
+    ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+                io_lib:format("KEX strict violation: send_sequence = ~p  recv_sequence = ~p",
+                              [SendSeq, RecvSeq]));
 
 %%% ######## {new_keys, client|server} ####
-
 %% First key exchange round:
 handle_event(internal, #ssh_msg_newkeys{} = Msg, {new_keys,client,init}, D0) ->
     {ok, Ssh1} = ssh_transport:handle_new_keys(Msg, D0#data.ssh_params),
@@ -162,6 +198,15 @@
     %% ssh_connection_handler:send_bytes(ExtInfo, D),
     {next_state, {ext_info,server,init}, D#data{ssh_params=Ssh}};
 
+%%% ######## handle KEX strict
+handle_event(internal, _Event, {new_keys,_Role,init},
+             #data{ssh_params = #ssh{algorithms = #alg{kex_strict_negotiated = true},
+                                     send_sequence = SendSeq,
+                                     recv_sequence = RecvSeq}}) ->
+    ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+                io_lib:format("KEX strict violation (send_sequence = ~p recv_sequence = ~p)",
+                              [SendSeq, RecvSeq]));
+
 %% Subsequent key exchange rounds (renegotiation):
 handle_event(internal, #ssh_msg_newkeys{} = Msg, {new_keys,Role,renegotiate}, D) ->
     {ok, Ssh} = ssh_transport:handle_new_keys(Msg, D#data.ssh_params),
@@ -183,7 +228,6 @@
 handle_event(internal, #ssh_msg_newkeys{}=Msg, {ext_info,_Role,renegotiate}, D) ->
     {ok, Ssh} = ssh_transport:handle_new_keys(Msg, D#data.ssh_params),
     {keep_state, D#data{ssh_params = Ssh}};
-    
 
 handle_event(internal, Msg, {ext_info,Role,init}, D) when is_tuple(Msg) ->
     %% If something else arrives, goto next state and handle the event in that one
@@ -217,3 +261,70 @@
 peer_role(client) -> server;
 peer_role(server) -> client.
 
+check_kex_strict(Msg,
+                 #data{ssh_params =
+                           #ssh{algorithms =
+                                    #alg{
+                                       kex = Kex,
+                                       kex_strict_negotiated = KexStrictNegotiated},
+                                send_sequence = SendSeq,
+                                recv_sequence = RecvSeq}}) ->
+    case check_msg_group(Msg, get_alg_group(Kex), KexStrictNegotiated) of
+        ok ->
+            ok;
+        error ->
+            ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+                        io_lib:format("KEX strict violation: send_sequence = ~p  recv_sequence = ~p",
+                                      [SendSeq, RecvSeq]))
+    end.
+
+get_alg_group(Kex) when Kex == 'diffie-hellman-group16-sha512';
+                        Kex == 'diffie-hellman-group18-sha512';
+                        Kex == 'diffie-hellman-group14-sha256';
+                        Kex == 'diffie-hellman-group14-sha1';
+                        Kex == 'diffie-hellman-group1-sha1' ->
+    dh_alg;
+get_alg_group(Kex) when Kex == 'diffie-hellman-group-exchange-sha256';
+                        Kex == 'diffie-hellman-group-exchange-sha1' ->
+    dh_gex_alg;
+get_alg_group(Kex) when Kex == 'curve25519-sha256';
+                        Kex == 'curve25519-sha256@libssh.org';
+                        Kex == 'curve448-sha512';
+                        Kex == 'ecdh-sha2-nistp521';
+                        Kex == 'ecdh-sha2-nistp384';
+                        Kex == 'ecdh-sha2-nistp256' ->
+    ecdh_alg.
+
+check_msg_group(_Msg, _AlgGroup, false) -> ok;
+check_msg_group(#ssh_msg_kexdh_init{},  dh_alg, true) -> ok;
+check_msg_group(#ssh_msg_kexdh_reply{}, dh_alg, true) -> ok;
+check_msg_group(#ssh_msg_kex_dh_gex_request_old{}, dh_gex_alg, true) -> ok;
+check_msg_group(#ssh_msg_kex_dh_gex_request{},     dh_gex_alg, true) -> ok;
+check_msg_group(#ssh_msg_kex_dh_gex_group{},       dh_gex_alg, true) -> ok;
+check_msg_group(#ssh_msg_kex_dh_gex_init{},        dh_gex_alg, true) -> ok;
+check_msg_group(#ssh_msg_kex_dh_gex_reply{},       dh_gex_alg, true) -> ok;
+check_msg_group(#ssh_msg_kex_ecdh_init{},  ecdh_alg, true) -> ok;
+check_msg_group(#ssh_msg_kex_ecdh_reply{}, ecdh_alg, true) -> ok;
+check_msg_group(_Msg, _AlgGroup, _) -> error.
+
+%%%################################################################
+%%%#
+%%%# Tracing
+%%%#
+
+ssh_dbg_trace_points() -> [connection_events].
+
+ssh_dbg_flags(connection_events) -> [c].
+
+ssh_dbg_on(connection_events) -> dbg:tp(?MODULE,   handle_event, 4, x).
+
+ssh_dbg_off(connection_events) -> dbg:ctpg(?MODULE, handle_event, 4).
+
+ssh_dbg_format(connection_events, {call, {?MODULE,handle_event, [EventType, EventContent, State, _Data]}}) ->
+    ["Connection event\n",
+     io_lib:format("[~w] EventType: ~p~nEventContent: ~p~nState: ~p~n", [?MODULE, EventType, EventContent, State])
+    ];
+ssh_dbg_format(connection_events, {return_from, {?MODULE,handle_event,4}, Ret}) ->
+    ["Connection event result\n",
+     io_lib:format("[~w] ~p~n", [?MODULE, ssh_dbg:reduce_state(Ret, #data{})])
+    ].
diff -ruN a/lib/ssh/src/ssh_fsm_userauth_client.erl b/lib/ssh/src/ssh_fsm_userauth_client.erl
--- a/lib/ssh/src/ssh_fsm_userauth_client.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/src/ssh_fsm_userauth_client.erl	2025-12-22 23:16:56.265838977 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2021. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -105,7 +105,10 @@
     end;
 
 %%---- banner to client
-handle_event(internal, #ssh_msg_userauth_banner{message = Msg}, {userauth,client}, D) ->
+handle_event(internal, #ssh_msg_userauth_banner{message = Msg}, {S,client}, D)
+  when S == userauth; S == userauth_keyboard_interactive;
+       S == userauth_keyboard_interactive_extra;
+       S == userauth_keyboard_interactive_info_response ->
     case D#data.ssh_params#ssh.userauth_quiet_mode of
 	false -> io:format("~s", [Msg]);
 	true -> ok
diff -ruN a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl
--- a/lib/ssh/src/ssh.hrl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/src/ssh.hrl	2025-12-22 23:16:56.261838930 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2004-2023. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -561,5 +561,11 @@
 -define(CIRC_BUF_IN_ONCE(VALUE),
         ((fun(V) -> ?CIRC_BUF_IN(V), V end)(VALUE))
        ).
-                 
+
+-define(SELECT_MSG(__Fun),
+        (fun() ->
+                #{level := __Level} = logger:get_primary_config(),
+                __Fun(__Level)
+        end)()).
+
 -endif. % SSH_HRL defined
diff -ruN a/lib/ssh/src/ssh_info.erl b/lib/ssh/src/ssh_info.erl
--- a/lib/ssh/src/ssh_info.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/src/ssh_info.erl	2025-12-22 23:16:56.265838977 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2021. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2024. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -131,24 +131,36 @@
      walk_tree(server, Children, ?inc(Indent)),
      io_lib:nl() % Separate system supervisors by an empty line
     ];
-format_sup(client, {{{ssh_system_sup,LocalAddress},Pid,supervisor,[ssh_system_sup]}, _Spec, Children}, Indent) ->
-    [indent(Indent),
-     io_lib:format("Local:  ~s sys_sup=~s~n", [format_address(LocalAddress), print_pid(Pid)]),
-     walk_tree(client, Children, ?inc(Indent)),
-     io_lib:nl() % Separate system supervisors by an empty line
+format_sup(client,
+           {{Ref,ConnSup,supervisor,[ssh_connection_sup]}, _ConnSupSpec,
+            [{{connection,ConnPid,worker,[ssh_connection_handler]}, _ConnSpec}
+             | Children]
+           },
+           Indent) when is_reference(Ref) ->
+    [io_lib:format("~sLocal: ~s~n"
+                   "~sRemote: ~s (Version: ~s)~n"
+                   "~sConnectionRef=~s, connection_sup=~s~n",
+                   [indent(Indent), local_addr(ConnPid),
+                    indent(Indent), peer_addr(ConnPid), peer_version(client,ConnPid),
+                    indent(Indent), print_pid(ConnPid), print_pid(ConnSup)
+                   ]),
+     walk_tree(client,
+               [{H,{connref,ConnPid},Cs} || {H,_,Cs} <- Children],
+               ?inc(Indent)),
+     io_lib:nl() % Separate sub system supervisors by an empty line
     ];
-format_sup(Role,
-           {{Ref,SubSysSup,supervisor,[ssh_subsystem_sup]}, _SubSysSpec,
-            [{{connection,ConnPid,worker,[ssh_connection_handler]}, _ConnSpec} 
+format_sup(server,
+           {{Ref,ConnSup,supervisor,[ssh_connection_sup]}, _ConnSupSpec,
+            [{{connection,ConnPid,worker,[ssh_connection_handler]}, _ConnSpec}
              | Children]
            },
            Indent) when is_reference(Ref) ->
     [io_lib:format("~sRemote: ~s (Version: ~s)~n"
-                   "~sConnectionRef=~s, subsys_sup=~s~n",
-                   [indent(Indent), peer_addr(ConnPid), peer_version(Role,ConnPid),
-                    indent(Indent), print_pid(ConnPid), print_pid(SubSysSup)
+                   "~sConnectionRef=~s, connection_sup=~s~n",
+                   [indent(Indent), peer_addr(ConnPid), peer_version(server,ConnPid),
+                    indent(Indent), print_pid(ConnPid), print_pid(ConnSup)
                    ]),
-     walk_tree(Role,
+     walk_tree(server,
                [{H,{connref,ConnPid},Cs} || {H,_,Cs} <- Children],
                ?inc(Indent)),
      io_lib:nl() % Separate sub system supervisors by an empty line
@@ -250,7 +262,16 @@
     catch
         _:_ -> "?"
     end.
-    
+
+local_addr(Pid) ->
+    try
+        [{socket,Socket}] =
+            ssh_connection_handler:connection_info(Pid, [socket]),
+        {ok, AddrPort} = inet:sockname(Socket),
+        ssh_lib:format_address_port(AddrPort)
+    catch
+        _:_ -> "?"
+    end.
 
 format_address(#address{address=Addr, port=Port, profile=Prof}) ->
     io_lib:format("~s (profile ~p)", [ssh_lib:format_address_port({Addr,Port}),Prof]);
diff -ruN a/lib/ssh/src/ssh_lib.erl b/lib/ssh/src/ssh_lib.erl
--- a/lib/ssh/src/ssh_lib.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/src/ssh_lib.erl	2025-12-22 23:16:56.267839000 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2004-2021. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -28,7 +28,9 @@
          format_address_port/2, format_address_port/1,
          format_address/1,
          format_time_ms/1,
-         comp/2
+         comp/2,
+         trim_reason/1,
+         max_log_len/1
         ]).
 
 -include("ssh.hrl").
@@ -86,3 +88,14 @@
 
 comp(_, _, _) ->
     false.
+%% We don't want to process badmatch details, potentially containing
+%% malicious data of unknown size
+trim_reason({badmatch, V}) when is_binary(V) ->
+    badmatch;
+trim_reason(E) ->
+    E.
+
+max_log_len(#ssh{opts = Opts}) ->
+    ?GET_OPT(max_log_item_len, Opts);
+max_log_len(Opts) when is_map(Opts) ->
+    ?GET_OPT(max_log_item_len, Opts).
diff -ruN a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl
--- a/lib/ssh/src/ssh_message.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/src/ssh_message.erl	2025-12-22 23:16:56.267839000 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2013-2022. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
 -module(ssh_message).
 
 -include_lib("public_key/include/public_key.hrl").
+-include_lib("kernel/include/logger.hrl").
 
 -include("ssh.hrl").
 -include("ssh_connect.hrl").
@@ -42,11 +43,12 @@
 
 -behaviour(ssh_dbg).
 -export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2]).
+-define(ALG_NAME_LIMIT, 64). % RFC4251 sec6
 
 ucl(B) ->
     try unicode:characters_to_list(B) of
 	L when is_list(L) -> L;
-	{error,_Matched,Rest} -> throw({error,{bad_unicode,Rest}})
+	{error,_Matched,_Rest} -> throw({error,bad_unicode})
     catch
 	_:_ -> throw({error,bad_unicode})
     end.
@@ -206,12 +208,12 @@
 encode(#ssh_msg_service_request{
 	  name = Service
 	 }) ->
-    <<?Ebyte(?SSH_MSG_SERVICE_REQUEST), ?Estring_utf8(Service)>>;
+    <<?Ebyte(?SSH_MSG_SERVICE_REQUEST), ?Estring(Service)>>;
 
 encode(#ssh_msg_service_accept{
 	  name = Service
 	 }) ->
-    <<?Ebyte(?SSH_MSG_SERVICE_ACCEPT), ?Estring_utf8(Service)>>;
+    <<?Ebyte(?SSH_MSG_SERVICE_ACCEPT), ?Estring(Service)>>;
 
 encode(#ssh_msg_ext_info{
           nr_extensions = N,
@@ -374,7 +376,7 @@
     try
         #ssh_msg_channel_request{
            recipient_channel = Recipient,
-           request_type = ?unicode_list(RequestType),
+           request_type = binary:bin_to_list(RequestType),
            want_reply = erl_boolean(Bool),
            data  = Data
           }
@@ -404,8 +406,8 @@
 	 Data/binary>>) ->
     #ssh_msg_userauth_request{
        user =    ?unicode_list(User),
-       service = ?unicode_list(Service),
-       method =  ?unicode_list(Method),
+       service = binary:bin_to_list(Service),
+       method =  binary:bin_to_list(Method),
        data = Data
       };
 
@@ -413,7 +415,7 @@
 	 ?DEC_BIN(Auths,__0),
 	 ?BYTE(Bool)>>) ->
     #ssh_msg_userauth_failure {
-       authentications = ?unicode_list(Auths),
+       authentications = binary:bin_to_list(Auths),
        partial_success = erl_boolean(Bool)
       };
 
@@ -527,12 +529,12 @@
 
 decode(<<?SSH_MSG_SERVICE_REQUEST, ?DEC_BIN(Service,__0)>>) ->
     #ssh_msg_service_request{
-       name = ?unicode_list(Service)
+       name = binary:bin_to_list(Service)
       };
 
 decode(<<?SSH_MSG_SERVICE_ACCEPT, ?DEC_BIN(Service,__0)>>) ->
     #ssh_msg_service_accept{
-       name = ?unicode_list(Service)
+       name = binary:bin_to_list(Service)
       };
 
 decode(<<?BYTE(?SSH_MSG_DISCONNECT), ?UINT32(Code), ?DEC_BIN(Desc,__0), ?DEC_BIN(Lang,__1)>>) ->
@@ -819,9 +821,33 @@
     %% See rfc 4253 7.1
     X = 0,
     list_to_tuple(lists:reverse([X, erl_boolean(Bool) | Acc]));
-decode_kex_init(<<?DEC_BIN(Data,__0), Rest/binary>>, Acc, N) ->
-    Names = string:tokens(?unicode_list(Data), ","),
-    decode_kex_init(Rest, [Names | Acc], N -1).
+decode_kex_init(<<?DEC_BIN(Data,__0), Rest/binary>>, Acc, N) when
+      byte_size(Data) < ?MAX_NUM_ALGORITHMS * ?ALG_NAME_LIMIT ->
+    BinParts = binary:split(Data, <<$,>>, [global]),
+    AlgCount = length(BinParts),
+    case AlgCount =< ?MAX_NUM_ALGORITHMS of
+        true ->
+            Process =
+                fun(<<>>, PAcc) ->
+                        PAcc;
+                   (Part, PAcc) ->
+                        case byte_size(Part) =< ?ALG_NAME_LIMIT of
+                            true ->
+                                Name = binary:bin_to_list(Part),
+                                [Name | PAcc];
+                            false ->
+                                ?LOG_DEBUG("Ignoring too long name", []),
+                                PAcc
+                        end
+                end,
+            Names = lists:foldr(Process, [], BinParts),
+            decode_kex_init(Rest, [Names | Acc], N - 1);
+        false ->
+            throw({error, {kexinit_error, N, {alg_count, AlgCount}}})
+    end;
+decode_kex_init(<<?DEC_BIN(Data,__0), _Rest/binary>>, _Acc, N) ->
+    throw({error, {kexinit, N, {string_size, byte_size(Data)}}}).
+
 
 
 %%%================================================================
diff -ruN a/lib/ssh/src/ssh_options.erl b/lib/ssh/src/ssh_options.erl
--- a/lib/ssh/src/ssh_options.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/src/ssh_options.erl	2025-12-22 23:16:56.267839000 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2004-2023. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -885,6 +885,12 @@
            #{default => ?MAX_RND_PADDING_LEN,
              chk => fun(V) -> check_non_neg_integer(V) end,
              class => undoc_user_option
+            },
+
+       channel_close_timeout =>
+           #{default => 5 * 1000,
+             chk => fun(V) -> check_non_neg_integer(V) end,
+             class => undoc_user_option
             }
      }.
 
diff -ruN a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl
--- a/lib/ssh/src/ssh_sftpd.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/src/ssh_sftpd.erl	2025-12-22 23:16:56.268839012 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2005-2021. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@
 -behaviour(ssh_server_channel).
 
 -include_lib("kernel/include/file.hrl").
-
+-include_lib("kernel/include/logger.hrl").
 -include("ssh.hrl").
 -include("ssh_xfer.hrl").
 -include("ssh_connect.hrl"). %% For ?DEFAULT_PACKET_SIZE and ?DEFAULT_WINDOW_SIZE
@@ -52,6 +52,8 @@
 	  file_handler,			% atom() - callback module 
 	  file_state,                   % state for the file callback module
 	  max_files,                    % integer >= 0 max no files sent during READDIR
+	  max_handles,                  % integer > 0  - max number of file handles
+	  max_path,                     % integer > 0 - max length of path
 	  options,			% from the subsystem declaration
 	  handles			% list of open handles
 	  %% handle is either {<int>, directory, {Path, unread|eof}} or
@@ -65,6 +67,8 @@
       Options :: [ {cwd, string()} |
                    {file_handler, CbMod | {CbMod, FileState}} |
                    {max_files, integer()} |
+                   {max_handles, integer()} |
+                   {max_path, integer()} |
                    {root, string()} |
                    {sftpd_vsn, integer()}
                  ],
@@ -115,8 +119,14 @@
 		{Root0, State0}
 	end,
     MaxLength = proplists:get_value(max_files, Options, 0),
+    MaxHandles = proplists:get_value(max_handles, Options, 1000),
+    MaxPath = proplists:get_value(max_path, Options, 4096),
     Vsn = proplists:get_value(sftpd_vsn, Options, 5),
-    {ok,  State#state{cwd = CWD, root = Root, max_files = MaxLength,
+    {ok,  State#state{cwd = CWD,
+                      root = Root,
+                      max_files = MaxLength,
+                      max_handles = MaxHandles,
+                      max_path = MaxPath,
 		      options = Options,
 		      handles = [], pending = <<>>,
 		      xf = #ssh_xfer{vsn = Vsn, ext = []}}}.
@@ -128,9 +138,8 @@
 %% Description: Handles channel messages
 %%--------------------------------------------------------------------
 handle_ssh_msg({ssh_cm, _ConnectionManager,
-		{data, _ChannelId, Type, Data}}, State) ->
-    State1 = handle_data(Type, Data, State),
-    {ok, State1};
+		{data, ChannelId, Type, Data}}, State) ->
+    handle_data(Type, ChannelId, Data, State);
 
 handle_ssh_msg({ssh_cm, _, {eof, ChannelId}}, State) ->
     {stop, ChannelId, State};
@@ -187,24 +196,77 @@
 %%--------------------------------------------------------------------
 %%% Internal functions
 %%--------------------------------------------------------------------
-handle_data(0, <<?UINT32(Len), Msg:Len/binary, Rest/binary>>, 
+handle_data(0, ChannelId, <<?UINT32(Len), Msg:Len/binary, Rest/binary>>,
 	    State = #state{pending = <<>>}) ->
     <<Op, ?UINT32(ReqId), Data/binary>> = Msg,
     NewState = handle_op(Op, ReqId, Data, State),
     case Rest of
 	<<>> ->
-	    NewState;   
+	    {ok, NewState};
 	_ ->
-	    handle_data(0, Rest, NewState)
+	    handle_data(0, ChannelId, Rest, NewState)
     end;
-	     
-handle_data(0, Data, State = #state{pending = <<>>}) ->
-    State#state{pending = Data};
-
-handle_data(Type, Data, State = #state{pending = Pending}) -> 
-     handle_data(Type, <<Pending/binary, Data/binary>>, 
-                 State#state{pending = <<>>}).
- 
+handle_data(0, _ChannelId, Data, State = #state{pending = <<>>}) ->
+    {ok, State#state{pending = Data}};
+handle_data(Type, ChannelId, Data0, State = #state{pending = Pending}) ->
+    Data = <<Pending/binary, Data0/binary>>,
+    Size = byte_size(Data),
+    case Size > ?SSH_MAX_PACKET_SIZE of
+        true ->
+            ReportFun =
+                fun([S]) ->
+                        Report =
+                            #{label => {error_logger, error_report},
+                              report =>
+                                  io_lib:format("SFTP packet size (~B) exceeds the limit!",
+                                                [S])},
+                        Meta =
+                            #{error_logger =>
+                                  #{tag => error_report,type => std_error},
+                             report_cb => fun(#{report := Msg}) -> {Msg, []} end},
+                        {Report, Meta}
+                end,
+            ?LOG_ERROR(ReportFun, [Size]),
+            {stop, ChannelId, State};
+        _ ->
+            handle_data(Type, ChannelId, Data, State#state{pending = <<>>})
+    end.
+
+%% From draft-ietf-secsh-filexfer-02 "The file handle strings MUST NOT be longer than 256 bytes."
+handle_op(Request, ReqId, <<?UINT32(HLen), _/binary>>, State = #state{xf = XF})
+  when (Request == ?SSH_FXP_CLOSE orelse
+        Request == ?SSH_FXP_FSETSTAT orelse
+        Request == ?SSH_FXP_FSTAT orelse
+        Request == ?SSH_FXP_READ orelse
+        Request == ?SSH_FXP_READDIR orelse
+        Request == ?SSH_FXP_WRITE),
+       HLen > 256 ->
+    ssh_xfer:xf_send_status(XF, ReqId, ?SSH_FX_INVALID_HANDLE, "Invalid handle"),
+    State;
+handle_op(Request, ReqId, <<?UINT32(PLen), _/binary>>,
+          State = #state{max_path = MaxPath, xf = XF})
+  when (Request == ?SSH_FXP_LSTAT orelse
+        Request == ?SSH_FXP_MKDIR orelse
+        Request == ?SSH_FXP_OPEN orelse
+        Request == ?SSH_FXP_OPENDIR orelse
+        Request == ?SSH_FXP_READLINK orelse
+        Request == ?SSH_FXP_REALPATH orelse
+        Request == ?SSH_FXP_REMOVE orelse
+        Request == ?SSH_FXP_RMDIR orelse
+        Request == ?SSH_FXP_SETSTAT orelse
+        Request == ?SSH_FXP_STAT),
+       PLen > MaxPath ->
+    ssh_xfer:xf_send_status(XF, ReqId, ?SSH_FX_NO_SUCH_PATH,
+                            "No such path"),
+    State;
+handle_op(Request, ReqId, <<?UINT32(PLen), _:PLen/binary, ?UINT32(PLen2), _/binary>>,
+          State = #state{max_path = MaxPath, xf = XF})
+  when (Request == ?SSH_FXP_RENAME orelse
+        Request == ?SSH_FXP_SYMLINK),
+       (PLen > MaxPath orelse PLen2 > MaxPath) ->
+    ssh_xfer:xf_send_status(XF, ReqId, ?SSH_FX_NO_SUCH_PATH,
+                            "No such path"),
+    State;
 handle_op(?SSH_FXP_INIT, Version, B, State) when is_binary(B) ->
     XF = State#state.xf,
     Vsn = lists:min([XF#ssh_xfer.vsn, Version]),
@@ -212,7 +274,7 @@
     ssh_xfer:xf_send_reply(XF1, ?SSH_FXP_VERSION, <<?UINT32(Vsn)>>),
     State#state{xf = XF1};
 handle_op(?SSH_FXP_REALPATH, ReqId,
-	  <<?UINT32(Rlen), RPath:Rlen/binary>>,
+	  <<?UINT32(RLen), RPath:RLen/binary>>,
 	  State0) ->
     RelPath = relate_file_name(RPath, State0, _Canonicalize=false),
     {Res, State} = resolve_symlinks(RelPath, State0),
@@ -228,14 +290,16 @@
     end;
 handle_op(?SSH_FXP_OPENDIR, ReqId,
 	 <<?UINT32(RLen), RPath:RLen/binary>>,
-	  State0 = #state{xf = #ssh_xfer{vsn = Vsn}, 
-			  file_handler = FileMod, file_state = FS0}) ->
+	  State0 = #state{xf = #ssh_xfer{vsn = Vsn},
+			  file_handler = FileMod, file_state = FS0,
+                          max_handles = MaxHandles}) ->
     RelPath = unicode:characters_to_list(RPath),
     AbsPath = relate_file_name(RelPath, State0),
     
     XF = State0#state.xf,
     {IsDir, FS1} = FileMod:is_dir(AbsPath, FS0),
     State1 = State0#state{file_state = FS1},
+    HandlesCnt = length(State0#state.handles),
     case IsDir of
 	false when Vsn > 5 ->
 	    ssh_xfer:xf_send_status(XF, ReqId, ?SSH_FX_NOT_A_DIRECTORY,
@@ -245,8 +309,12 @@
 	    ssh_xfer:xf_send_status(XF, ReqId, ?SSH_FX_FAILURE,
 				    "Not a directory"),
 	    State1;
-	true ->
-	    add_handle(State1, XF, ReqId, directory, {RelPath,unread})
+	true when HandlesCnt < MaxHandles ->
+	    add_handle(State1, XF, ReqId, directory, {RelPath,unread});
+        true ->
+	    ssh_xfer:xf_send_status(XF, ReqId, ?SSH_FX_FAILURE,
+				    "max_handles limit reached"),
+	    State1
     end;
 handle_op(?SSH_FXP_READDIR, ReqId,
 	  <<?UINT32(HLen), BinHandle:HLen/binary>>,
@@ -381,14 +449,12 @@
     send_status(Status, ReqId, State1);
 
 handle_op(?SSH_FXP_RENAME, ReqId,
-  	  Bin = <<?UINT32(PLen), _:PLen/binary, ?UINT32(PLen2),
-  		 _:PLen2/binary>>,
+  	  Bin = <<?UINT32(PLen), _:PLen/binary, ?UINT32(PLen2), _:PLen2/binary>>,
   	  State = #state{xf = #ssh_xfer{vsn = Vsn}}) when Vsn==3; Vsn==4  ->
     handle_op(?SSH_FXP_RENAME, ReqId, <<Bin/binary, 0:32>>, State);
 
 handle_op(?SSH_FXP_RENAME, ReqId,
-	  <<?UINT32(PLen), BPath:PLen/binary, ?UINT32(PLen2), 
-	   BPath2:PLen2/binary, ?UINT32(Flags)>>,
+	  <<?UINT32(PLen), BPath:PLen/binary, ?UINT32(PLen2), BPath2:PLen2/binary, ?UINT32(Flags)>>,
 	  State0 = #state{file_handler = FileMod, file_state = FS0}) ->
     Path = relate_file_name(BPath, State0),
     Path2 = relate_file_name(BPath2, State0),
@@ -426,23 +492,29 @@
     State1 = State0#state{file_state = FS1},
     send_status(Status, ReqId, State1).
 
-new_handle([], H) ->
-    H;
-new_handle([{N, _,_} | Rest], H) when N =< H ->
-    new_handle(Rest, N+1);
-new_handle([_ | Rest], H) ->
-    new_handle(Rest, H).
+new_handle_id([]) -> 0;
+new_handle_id([{_, _, _} | _] = Handles) ->
+    {HandleIds, _, _} = lists:unzip3(Handles),
+    new_handle_id(lists:sort(HandleIds));
+new_handle_id(HandleIds) ->
+    find_gap(HandleIds).
+
+find_gap([Id]) -> % no gap found
+    Id + 1;
+find_gap([Id1, Id2 | _]) when Id2 - Id1 > 1 -> % gap found
+    Id1 + 1;
+find_gap([_, Id | Rest]) ->
+    find_gap([Id | Rest]).
 
 add_handle(State, XF, ReqId, Type, DirFileTuple) ->
     Handles = State#state.handles,
-    Handle = new_handle(Handles, 0),
-    ssh_xfer:xf_send_handle(XF, ReqId, integer_to_list(Handle)),
-    %% OBS: If you change handles-tuple also change new_handle!
-    %% Is this this the best way to implement new handle?
-    State#state{handles = [{Handle, Type, DirFileTuple} | Handles]}.
+    HandleId = new_handle_id(Handles),
+    ssh_xfer:xf_send_handle(XF, ReqId, integer_to_list(HandleId)),
+    %% OBS: If you change handles-tuple also change new_handle_id!
+    State#state{handles = [{HandleId, Type, DirFileTuple} | Handles]}.
     
 get_handle(Handles, BinHandle) ->
-    case (catch list_to_integer(binary_to_list(BinHandle))) of
+    case (catch binary_to_integer(BinHandle)) of
 	I when is_integer(I) ->
 	    case lists:keysearch(I, 1, Handles) of
 		{value, T} -> T;
@@ -697,7 +769,9 @@
     do_open(ReqId, State, Path, Flags).
 
 do_open(ReqId, State0, Path, Flags) ->
-    #state{file_handler = FileMod, file_state = FS0, xf = #ssh_xfer{vsn = Vsn}} = State0,
+    #state{file_handler = FileMod, file_state = FS0, xf = #ssh_xfer{vsn = Vsn},
+           max_handles = MaxHandles} = State0,
+    HandlesCnt = length(State0#state.handles),
     AbsPath = relate_file_name(Path, State0),
     {IsDir, _FS1} = FileMod:is_dir(AbsPath, FS0),
     case IsDir of 
@@ -709,7 +783,7 @@
 	    ssh_xfer:xf_send_status(State0#state.xf, ReqId,
 				    ?SSH_FX_FAILURE, "File is a directory"),
 	    State0;
-	false ->
+	false when HandlesCnt < MaxHandles ->
 	    OpenFlags = [binary | Flags],
 	    {Res, FS1} = FileMod:open(AbsPath, OpenFlags, FS0),
 	    State1 = State0#state{file_state = FS1},
@@ -720,7 +794,11 @@
 		    ssh_xfer:xf_send_status(State1#state.xf, ReqId,
 					    ssh_xfer:encode_erlang_status(Error)),
 		    State1
-	    end
+	    end;
+        false ->
+	    ssh_xfer:xf_send_status(State0#state.xf, ReqId,
+				    ?SSH_FX_FAILURE, "max_handles limit reached"),
+	    State0
     end.
 
 %% resolve all symlinks in a path
diff -ruN a/lib/ssh/src/ssh_sftp.erl b/lib/ssh/src/ssh_sftp.erl
--- a/lib/ssh/src/ssh_sftp.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/src/ssh_sftp.erl	2025-12-22 23:16:56.268839012 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2005-2023. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -776,14 +776,15 @@
       Timeout :: timeout(),
       Error :: {error, reason()}.
 read_file(Pid, Name, FileOpTimeout) ->
-    case open(Pid, Name, [read, binary], FileOpTimeout) of
-	{ok, Handle} ->
-	    {ok,{_WindowSz,PacketSz}} = recv_window(Pid, FileOpTimeout),
-	    Res = read_file_loop(Pid, Handle, PacketSz, FileOpTimeout, []),
-	    close(Pid, Handle),
-	    Res;
-	Error ->
-	    Error
+    try
+        {ok, Handle} = open(Pid, Name, [read, binary], FileOpTimeout),
+        {ok, {_WindowSz,PacketSz}} = recv_window(Pid, FileOpTimeout),
+        Res = read_file_loop(Pid, Handle, PacketSz, FileOpTimeout, []),
+        close(Pid, Handle),
+        Res
+    catch
+        error:{badmatch, Error = {error,_}} ->
+            Error
     end.
 
 read_file_loop(Pid, Handle, PacketSz, FileOpTimeout, Acc) ->
@@ -813,15 +814,16 @@
 write_file(Pid, Name, List, FileOpTimeout) when is_list(List) ->
     write_file(Pid, Name, to_bin(List), FileOpTimeout);
 write_file(Pid, Name, Bin, FileOpTimeout) ->
-    case open(Pid, Name, [write, binary], FileOpTimeout) of
-	{ok, Handle} ->
-	    {ok,{_Window,Packet}} = send_window(Pid, FileOpTimeout),
-	    Res = write_file_loop(Pid, Handle, 0, Bin, byte_size(Bin), Packet,
-				  FileOpTimeout),
-	    close(Pid, Handle, FileOpTimeout),
-	    Res;
-	Error ->
-	    Error
+    try
+        {ok, Handle} = open(Pid, Name, [write, binary], FileOpTimeout),
+        {ok, {_Window,Packet}} = send_window(Pid, FileOpTimeout),
+        Res = write_file_loop(Pid, Handle, 0, Bin, byte_size(Bin), Packet,
+                              FileOpTimeout),
+        close(Pid, Handle, FileOpTimeout),
+        Res
+    catch
+        error:{badmatch, Error = {error, _}} ->
+            Error
     end.
 
 write_file_loop(_Pid, _Handle, _Pos, _Bin, 0, _PacketSz,_FileOpTimeout) ->
@@ -1634,8 +1636,13 @@
 
 
 read_repeat(Pid, Handle, Len, FileOpTimeout) ->
-    {ok,{_WindowSz,PacketSz}} = recv_window(Pid, FileOpTimeout),
-    read_rpt(Pid, Handle, Len, PacketSz, FileOpTimeout, <<>>).
+    try
+        {ok,{_WindowSz,PacketSz}} = recv_window(Pid, FileOpTimeout),
+        read_rpt(Pid, Handle, Len, PacketSz, FileOpTimeout, <<>>)
+    catch
+        error:{badmatch, Error = {error, _}} ->
+            Error
+    end.
 
 read_rpt(Pid, Handle, WantedLen, PacketSz, FileOpTimeout, Acc) when WantedLen > 0 ->
     case read(Pid, Handle, min(WantedLen,PacketSz), FileOpTimeout) of
@@ -1653,8 +1660,13 @@
 write_to_remote_tar(_Pid, _SftpHandle, <<>>, _FileOpTimeout) ->
     ok;
 write_to_remote_tar(Pid, SftpHandle, Bin, FileOpTimeout) ->
-    {ok,{_Window,Packet}} = send_window(Pid, FileOpTimeout),
-    write_file_loop(Pid, SftpHandle, 0, Bin, byte_size(Bin), Packet, FileOpTimeout).
+    try
+        {ok,{_Window,Packet}} = send_window(Pid, FileOpTimeout),
+        write_file_loop(Pid, SftpHandle, 0, Bin, byte_size(Bin), Packet, FileOpTimeout)
+    catch
+        error:{badmatch, Error = {error, _}} ->
+            Error
+    end.
 
 position_buf(Pid, SftpHandle, BufHandle, Pos, FileOpTimeout) ->
     {ok,#bufinf{mode = Mode,
@@ -1691,18 +1703,24 @@
       end.
 
 read_buf(Pid, SftpHandle, BufHandle, WantedLen, FileOpTimeout) ->
-    {ok,{_Window,Packet}} = send_window(Pid, FileOpTimeout),
-    {ok,B0}  = call(Pid, {get_bufinf,BufHandle}, FileOpTimeout),
-    case do_the_read_buf(Pid, SftpHandle, WantedLen, Packet, FileOpTimeout, B0) of
-	{ok,ResultBin,B} ->
-	    call(Pid, {put_bufinf,BufHandle,B}, FileOpTimeout),
-	    {ok,ResultBin};
-	{error,Error} ->
-	    {error,Error};
-	{eof,B} ->
-	    call(Pid, {put_bufinf,BufHandle,B}, FileOpTimeout),
-	    eof
-      end.
+    try
+        {ok, {_Window, Packet}} = send_window(Pid, FileOpTimeout),
+        {ok, B0} = call(Pid, {get_bufinf, BufHandle}, FileOpTimeout),
+        case do_the_read_buf(Pid, SftpHandle, WantedLen, Packet, FileOpTimeout, B0) of
+            {ok, ResultBin, B} ->
+                call(Pid, {put_bufinf, BufHandle, B}, FileOpTimeout),
+                {ok, ResultBin};
+            {eof, B} ->
+                call(Pid, {put_bufinf, BufHandle, B}, FileOpTimeout),
+                eof
+        end
+    catch
+        error:{badmatch, Error = {error, _}} ->
+            Error;
+        error:{case_clause, Error = {error, _}} ->
+            Error
+    end.
+
 
 do_the_read_buf(_Pid, _SftpHandle, WantedLen, _Packet, _FileOpTimeout,
 		B=#bufinf{plain_text_buf=PlainBuf0,
@@ -1754,15 +1772,16 @@
 
 
 write_buf(Pid, SftpHandle, BufHandle, PlainBin, FileOpTimeout) ->
-    {ok,{_Window,Packet}} = send_window(Pid, FileOpTimeout),
-    {ok,B0=#bufinf{plain_text_buf=PTB}}  = call(Pid, {get_bufinf,BufHandle}, FileOpTimeout),
-    case do_the_write_buf(Pid, SftpHandle, Packet, FileOpTimeout,
-			  B0#bufinf{plain_text_buf = <<PTB/binary,PlainBin/binary>>}) of
-	{ok, B} ->
-	    call(Pid, {put_bufinf,BufHandle,B}, FileOpTimeout),
-	    ok;
-	{error,Error} ->
-	    {error,Error}
+    try
+        {ok, {_Window,Packet}} = send_window(Pid, FileOpTimeout),
+        {ok, B0=#bufinf{plain_text_buf=PTB}} = call(Pid, {get_bufinf,BufHandle}, FileOpTimeout),
+        {ok, B} = do_the_write_buf(Pid, SftpHandle, Packet, FileOpTimeout,
+                                   B0#bufinf{plain_text_buf = <<PTB/binary,PlainBin/binary>>}),
+        call(Pid, {put_bufinf,BufHandle,B}, FileOpTimeout),
+        ok
+    catch
+        error:{badmatch, Error = {error, _}} ->
+            Error
     end.
 
 do_the_write_buf(Pid, SftpHandle, Packet, FileOpTimeout,
diff -ruN a/lib/ssh/src/ssh_subsystem_sup.erl b/lib/ssh/src/ssh_subsystem_sup.erl
--- a/lib/ssh/src/ssh_subsystem_sup.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/src/ssh_subsystem_sup.erl	1970-01-01 09:30:00.000000000 +0930
@@ -1,110 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2021. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%%     http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% 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.
-%%
-%% %CopyrightEnd%
-%%
-%%
-%%----------------------------------------------------------------------
-%% Purpose: The ssh subsystem supervisor 
-%%----------------------------------------------------------------------
-
--module(ssh_subsystem_sup).
-
--behaviour(supervisor).
-
--include("ssh.hrl").
-
--export([start_link/5,
-         start_channel/8,
-         tcpip_fwd_supervisor/1
-	]).
-
-%% Supervisor callback
--export([init/1]).
-
-%%%=========================================================================
-%%%  API
-%%%=========================================================================
-start_link(Role, Address=#address{}, Id, Socket, Options) ->
-    case supervisor:start_link(?MODULE, [Role, Address, Id, Socket, Options]) of
-        {error, {shutdown, {failed_to_start_child, _, Error}}} ->
-            {error,Error};
-        Other ->
-            Other
-    end.
-
-start_channel(Role, SupPid, ConnRef, Callback, Id, Args, Exec, Opts) ->
-    ChannelSup = channel_supervisor(SupPid),
-    ssh_channel_sup:start_child(Role, ChannelSup, ConnRef, Callback, Id, Args, Exec, Opts).
-
-tcpip_fwd_supervisor(SubSysSup) ->
-    find_child(tcpip_forward_acceptor_sup, SubSysSup).
-
-
-%%%=========================================================================
-%%%  Supervisor callback
-%%%=========================================================================
-init([Role, Address, Id, Socket, Options]) ->
-    SubSysSup = self(),
-    SupFlags = #{strategy      => one_for_all,
-                 auto_shutdown => any_significant,
-                 intensity     =>    0,
-                 period        => 3600
-                },
-    ChildSpecs = [#{id          => connection,
-                    restart     => temporary,
-                    type        => worker,
-                    significant => true,
-                    start       => {ssh_connection_handler,
-                                    start_link,
-                                    [Role, Address, Id, Socket,
-                                     ?PUT_INTERNAL_OPT([
-                                                        {subsystem_sup, SubSysSup}
-                                                       ], Options)
-                                    ]
-                                   }
-                   },
-                  #{id      => channel_sup,
-                    restart => temporary,
-                    type    => supervisor,
-                    start   => {ssh_channel_sup, start_link, [Options]}
-                   },
-
-                 #{id      => tcpip_forward_acceptor_sup,
-                   restart => temporary,
-                   type    => supervisor,
-                   start   => {ssh_tcpip_forward_acceptor_sup, start_link, []}
-                  }
-                 ],
-    {ok, {SupFlags,ChildSpecs}}.
-
-%%%=========================================================================
-%%%  Internal functions
-%%%=========================================================================
-channel_supervisor(SubSysSup) -> find_child(channel_sup, SubSysSup).
-
-find_child(Id, Sup) when is_pid(Sup) ->
-    try
-       {Id, Pid, _, _} = lists:keyfind(Id, 1, supervisor:which_children(Sup)),
-       Pid
-    catch
-        exit:{no_proc,_} ->
-            {error, no_proc};
-        _:_ ->
-            {error, {id_not_found,?MODULE,Id}}
-    end.
-
diff -ruN a/lib/ssh/src/ssh_system_sup.erl b/lib/ssh/src/ssh_system_sup.erl
--- a/lib/ssh/src/ssh_system_sup.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/src/ssh_system_sup.erl	2025-12-22 23:16:56.268839012 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2023. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -33,11 +33,11 @@
 
 -export([start_link/3,
          stop_listener/1,
-	 stop_system/2,
-         start_system/3,
-         start_subsystem/4,
+	 stop_system/1,
+         start_system/2,
+         start_connection/4,
 	 get_daemon_listen_address/1,
-         addresses/2,
+         addresses/1,
          get_options/2,
          get_acceptor_options/1,
          replace_acceptor_options/2
@@ -50,27 +50,27 @@
 %%% API
 %%%=========================================================================
 
-start_system(Role, Address0, Options) ->
-    case find_system_sup(Role, Address0) of
+start_system(Address0, Options) ->
+    case find_system_sup(Address0) of
         {ok,{SysPid,Address}} ->
             restart_acceptor(SysPid, Address, Options);
         {error,not_found} ->
-            supervisor:start_child(sup(Role),
+            supervisor:start_child(sshd_sup,
                                    #{id       => {?MODULE,Address0},
-                                     start    => {?MODULE, start_link, [Role, Address0, Options]},
+                                     start    => {?MODULE, start_link, [server, Address0, Options]},
                                      restart  => temporary,
                                      type     => supervisor
                                     })
     end.
 
 %%%----------------------------------------------------------------
-stop_system(Role, SysSup) when is_pid(SysSup) ->
-    case lists:keyfind(SysSup, 2, supervisor:which_children(sup(Role))) of
-        {{?MODULE, Id}, SysSup, _, _} -> stop_system(Role, Id);
-        false -> undefined % FIXME ssh:stop_daemon doc missing that ?
+stop_system(SysSup) when is_pid(SysSup) ->
+    case lists:keyfind(SysSup, 2, supervisor:which_children(sup(server))) of
+        {{?MODULE, Id}, SysSup, _, _} -> stop_system(Id);
+        false -> ok
     end;
-stop_system(Role, Id) ->
-    supervisor:terminate_child(sup(Role), {?MODULE, Id}).
+stop_system(Id) ->
+    supervisor:terminate_child(sup(server), {?MODULE, Id}).
 
 
 %%%----------------------------------------------------------------
@@ -93,42 +93,49 @@
     end.
 
 %%%----------------------------------------------------------------
-%%% Start the subsystem child. It is a child of the system supervisor (callback = this module)
-start_subsystem(Role, Address=#address{}, Socket, Options0) ->
-    Options = ?PUT_INTERNAL_OPT([{user_pid, self()}], Options0),
+%%% Start the connection child. It is a significant child of the system
+%%% supervisor (callback = this module) for server and non-significant
+%%% child of sshc_sup for client
+start_connection(Role = client, _, Socket, Options) ->
+    do_start_connection(Role, sup(client), false, Socket, Options);
+start_connection(Role = server, Address=#address{}, Socket, Options) ->
+    case get_system_sup(Address, Options) of
+        {ok, SysPid} ->
+            do_start_connection(Role, SysPid, true, Socket, Options);
+        Others ->
+            Others
+    end.
+
+do_start_connection(Role, SupPid, Significant, Socket, Options0) ->
     Id = make_ref(),
-    case get_system_sup(Role, Address, Options) of
-        {ok,SysPid} ->
-            case supervisor:start_child(SysPid,
-                                        #{id          => Id,
-                                          start       => {ssh_subsystem_sup, start_link,
-                                                          [Role,Address,Id,Socket,Options]
-                                                         },
-                                          restart     => temporary,
-                                          significant => true,
-                                          type        => supervisor
-                                         })
-            of
-                {ok,_SubSysPid} ->
-                    try
-                        receive
-                            {new_connection_ref, Id, ConnPid} ->
-                                ssh_connection_handler:takeover(ConnPid, Role, Socket, Options)
-                        after 10000 ->
-                                error(timeout)
-                        end
-                    catch
-                        error:{badmatch,{error,Error}} ->
-                            {error,Error};
-                        error:timeout ->
-                            %% The connection was started, but the takover procedure timed out,
-                            %% therefore it exists a subtree, but it is not quite ready and
-                            %% must be removed (by the supervisor above):
-                            supervisor:terminate_child(SysPid, Id),
-                            {error, connection_start_timeout}
-                    end;
-                Others ->
-                    Others
+    Options = ?PUT_INTERNAL_OPT([{user_pid, self()}], Options0),
+    case supervisor:start_child(SupPid,
+                                #{id          => Id,
+                                  start       => {ssh_connection_sup, start_link,
+                                                  [Role,Id,Socket,Options]
+                                                 },
+                                  restart     => temporary,
+                                  significant => Significant,
+                                  type        => supervisor
+                                 })
+    of
+        {ok,_ConnectionSupPid} ->
+            try
+                receive
+                    {new_connection_ref, Id, ConnPid} ->
+                        ssh_connection_handler:takeover(ConnPid, Role, Socket, Options)
+                after 10000 ->
+                        error(timeout)
+                end
+            catch
+                error:{badmatch,{error,Error}} ->
+                    {error,Error};
+                error:timeout ->
+                    %% The connection was started, but the takover procedure timed out,
+                    %% therefore it exists a subtree, but it is not quite ready and
+                    %% must be removed (by the supervisor above):
+                    supervisor:terminate_child(SupPid, Id),
+                    {error, connection_start_timeout}
             end;
         Others ->
             Others
@@ -139,9 +146,9 @@
     supervisor:start_link(?MODULE, [Role, Address, Options]).
 
 %%%----------------------------------------------------------------
-addresses(Role,  #address{address=Address, port=Port, profile=Profile}) ->
+addresses(#address{address=Address, port=Port, profile=Profile}) ->
     [{SysSup,A} || {{ssh_system_sup,A},SysSup,supervisor,_} <-
-                     supervisor:which_children(sup(Role)),
+                     supervisor:which_children(sshd_sup),
                  Address == any orelse A#address.address == Address,
                  Port == any    orelse A#address.port == Port,
                  Profile == any orelse A#address.profile == Profile].
@@ -152,8 +159,8 @@
     case get_daemon_listen_address(SysPid) of
         {ok,Address} ->
             get_options(SysPid, Address);
-        {error,Error} ->
-            {error,Error}
+        {error,not_found} ->
+            {error,bad_daemon_ref}
     end.
 
 replace_acceptor_options(SysPid, NewOpts) ->
@@ -224,19 +231,20 @@
 lookup(SupModule, SystemSup) ->
     lists:keyfind([SupModule], 4, supervisor:which_children(SystemSup)).
 
-get_system_sup(Role, Address0, Options) ->
-    case find_system_sup(Role, Address0) of
+get_system_sup(Address0, Options) ->
+    case find_system_sup(Address0) of
         {ok,{SysPid,_Address}} ->
             {ok,SysPid};
         {error,not_found} ->
-            start_system(Role, Address0, Options);
+            start_system(Address0, Options);
         {error,Error} ->
             {error,Error}
     end.
 
-find_system_sup(Role, Address0) ->
-    case addresses(Role, Address0) of
-        [{SysSupPid,Address}] -> {ok,{SysSupPid,Address}};
+find_system_sup(Address0) ->
+    case addresses(Address0) of
+        [{SysSupPid,Address}] ->
+            {ok,{SysSupPid,Address}};
         [] -> {error,not_found};
         [_,_|_] -> {error,ambiguous}
     end.
diff -ruN a/lib/ssh/src/ssh_tcpip_forward_acceptor.erl b/lib/ssh/src/ssh_tcpip_forward_acceptor.erl
--- a/lib/ssh/src/ssh_tcpip_forward_acceptor.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/src/ssh_tcpip_forward_acceptor.erl	2025-12-22 23:16:56.268839012 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2021. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2024. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -35,9 +35,8 @@
     case get_fwd_listen_opts(ListenAddrStr) of
         {ok,Opts} ->
             %% start listening on Addr:BoundPort
-            case gen_tcp:listen(ListenPort, [binary,
-                                             {reuseaddr,true},
-                                             {active,false} | Opts]) of
+            case gen_tcp:listen(ListenPort,
+                                Opts ++ [binary, {reuseaddr,true}, {active,false}]) of
                 {ok,LSock} ->
                     {ok,{_, TrueListenPort}} = inet:sockname(LSock),
                     ssh_tcpip_forward_acceptor_sup:start_child(FwdSup,
diff -ruN a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
--- a/lib/ssh/src/ssh_transport.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/src/ssh_transport.erl	2025-12-22 23:16:56.269839023 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2004-2023. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -26,12 +26,11 @@
 
 -include_lib("public_key/include/public_key.hrl").
 -include_lib("kernel/include/inet.hrl").
-
 -include("ssh_transport.hrl").
 -include("ssh.hrl").
 
 -export([versions/2, hello_version_msg/1]).
--export([next_seqnum/1, 
+-export([next_seqnum/3,
 	 supported_algorithms/0, supported_algorithms/1,
 	 default_algorithms/0, default_algorithms/1,
          clear_default_algorithms_env/0,
@@ -202,18 +201,16 @@
 supported_algorithms(kex) ->
     select_crypto_supported(
       [
-       {'ecdh-sha2-nistp384',                   [{public_keys,ecdh}, {curves,secp384r1}, {hashs,sha384}]},
+       {'curve25519-sha256',                    [{public_keys,ecdh}, {curves,x25519}, {hashs,sha256}]},
+       {'curve25519-sha256@libssh.org',         [{public_keys,ecdh}, {curves,x25519}, {hashs,sha256}]},
+       {'curve448-sha512',                      [{public_keys,ecdh}, {curves,x448},   {hashs,sha512}]},
        {'ecdh-sha2-nistp521',                   [{public_keys,ecdh}, {curves,secp521r1}, {hashs,sha512}]},
+       {'ecdh-sha2-nistp384',                   [{public_keys,ecdh}, {curves,secp384r1}, {hashs,sha384}]},
        {'ecdh-sha2-nistp256',                   [{public_keys,ecdh}, {curves,secp256r1}, {hashs,sha256}]},
        {'diffie-hellman-group-exchange-sha256', [{public_keys,dh},   {hashs,sha256}]},
        {'diffie-hellman-group16-sha512',        [{public_keys,dh},   {hashs,sha512}]}, % In OpenSSH 7.3.p1
        {'diffie-hellman-group18-sha512',        [{public_keys,dh},   {hashs,sha512}]}, % In OpenSSH 7.3.p1
        {'diffie-hellman-group14-sha256',        [{public_keys,dh},   {hashs,sha256}]}, % In OpenSSH 7.3.p1
-       %% https://tools.ietf.org/html/draft-ietf-curdle-ssh-curves
-       %% Secure Shell (SSH) Key Exchange Method using Curve25519 and Curve448
-       {'curve25519-sha256',                    [{public_keys,ecdh}, {curves,x25519}, {hashs,sha256}]},
-       {'curve25519-sha256@libssh.org',         [{public_keys,ecdh}, {curves,x25519}, {hashs,sha256}]},
-       {'curve448-sha512',                      [{public_keys,ecdh}, {curves,x448},   {hashs,sha512}]},
        {'diffie-hellman-group14-sha1',          [{public_keys,dh},   {hashs,sha}]},
        {'diffie-hellman-group-exchange-sha1',   [{public_keys,dh},   {hashs,sha}]},
        {'diffie-hellman-group1-sha1',           [{public_keys,dh},   {hashs,sha}]}
@@ -221,13 +218,13 @@
 supported_algorithms(public_key) ->
     select_crypto_supported(
       [
-       {'ecdsa-sha2-nistp384',  [{public_keys,ecdsa}, {hashs,sha384}, {curves,secp384r1}]},
-       {'ecdsa-sha2-nistp521',  [{public_keys,ecdsa}, {hashs,sha512}, {curves,secp521r1}]},
-       {'ecdsa-sha2-nistp256',  [{public_keys,ecdsa}, {hashs,sha256}, {curves,secp256r1}]},
        {'ssh-ed25519',          [{public_keys,eddsa}, {curves,ed25519}                    ]},
        {'ssh-ed448',            [{public_keys,eddsa}, {curves,ed448}                      ]},
-       {'rsa-sha2-256',         [{public_keys,rsa},   {hashs,sha256}                      ]},
+       {'ecdsa-sha2-nistp521',  [{public_keys,ecdsa}, {hashs,sha512}, {curves,secp521r1}]},
+       {'ecdsa-sha2-nistp384',  [{public_keys,ecdsa}, {hashs,sha384}, {curves,secp384r1}]},
+       {'ecdsa-sha2-nistp256',  [{public_keys,ecdsa}, {hashs,sha256}, {curves,secp256r1}]},
        {'rsa-sha2-512',         [{public_keys,rsa},   {hashs,sha512}                      ]},
+       {'rsa-sha2-256',         [{public_keys,rsa},   {hashs,sha256}                      ]},
        {'ssh-rsa',              [{public_keys,rsa},   {hashs,sha}                         ]},
        {'ssh-dss',              [{public_keys,dss},   {hashs,sha}                         ]} % Gone in OpenSSH 7.3.p1
       ]);
@@ -253,15 +250,15 @@
 supported_algorithms(mac) ->
     same(
       select_crypto_supported(
-	[{'hmac-sha2-256-etm@openssh.com', [{macs,hmac}, {hashs,sha256}]},
-         {'hmac-sha2-512-etm@openssh.com', [{macs,hmac}, {hashs,sha256}]},
-         {'hmac-sha2-256',    [{macs,hmac}, {hashs,sha256}]},
+	[{'hmac-sha2-512-etm@openssh.com', [{macs,hmac}, {hashs,sha256}]},
+         {'hmac-sha2-256-etm@openssh.com', [{macs,hmac}, {hashs,sha256}]},
 	 {'hmac-sha2-512',    [{macs,hmac}, {hashs,sha512}]},
+         {'hmac-sha2-256',    [{macs,hmac}, {hashs,sha256}]},
          {'hmac-sha1-etm@openssh.com', [{macs,hmac}, {hashs,sha256}]},
 	 {'hmac-sha1',        [{macs,hmac}, {hashs,sha}]},
 	 {'hmac-sha1-96',     [{macs,hmac}, {hashs,sha}]},
-	 {'AEAD_AES_128_GCM', [{ciphers,aes_128_gcm}]},
-	 {'AEAD_AES_256_GCM', [{ciphers,aes_256_gcm}]}
+         {'AEAD_AES_256_GCM', [{ciphers,aes_256_gcm}]},
+	 {'AEAD_AES_128_GCM', [{ciphers,aes_128_gcm}]}
 	]
        ));
 supported_algorithms(compression) ->
@@ -297,7 +294,12 @@
 hello_version_msg(Data) ->
     [Data,"\r\n"].
 
-next_seqnum(SeqNum) ->
+next_seqnum({State, _Role, init}, 16#ffffffff,
+            #ssh{algorithms = #alg{kex_strict_negotiated = true}})
+  when State == kexinit; State == key_exchange; State == new_keys ->
+    ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+                io_lib:format("KEX strict violation: recv_sequence = 16#ffffffff", []));
+next_seqnum(_State, SeqNum, _) ->
     (SeqNum + 1) band 16#ffffffff.
 
 is_valid_mac(_, _ , #ssh{recv_mac_size = 0}) ->
@@ -401,8 +403,9 @@
 	    key_exchange_first_msg(Algos#alg.kex, 
 				   Ssh#ssh{algorithms = Algos})
     catch
-        Class:Error ->
-            Msg = kexinit_error(Class, Error, client, Own, CounterPart),
+        Class:Reason0 ->
+            Reason = ssh_lib:trim_reason(Reason0),
+            Msg = kexinit_error(Class, Reason, client, Own, CounterPart, Ssh),
             ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, Msg)
         end;
 
@@ -418,31 +421,38 @@
 	Algos ->
             {ok, Ssh#ssh{algorithms = Algos}}
     catch
-        Class:Error ->
-            Msg = kexinit_error(Class, Error, server, Own, CounterPart),
+        Class:Reason0 ->
+            Reason = ssh_lib:trim_reason(Reason0),
+            Msg = kexinit_error(Class, Reason, server, Own, CounterPart, Ssh),
             ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, Msg)
     end.
 
-kexinit_error(Class, Error, Role, Own, CounterPart) ->
+kexinit_error(Class, Error, Role, Own, CounterPart, Ssh) ->
     {Fmt,Args} =
         case {Class,Error} of
             {error, {badmatch,{false,Alg}}} ->
                 {Txt,W,C} = alg_info(Role, Alg),
-                {"No common ~s algorithm,~n"
-                 "  we have:~n    ~s~n"
-                 "  peer have:~n    ~s~n",
-                 [Txt,
-                  lists:join(", ", element(W,Own)),
-                  lists:join(", ", element(C,CounterPart))
-                 ]};
+                MsgFun =
+                    fun(debug) ->
+                            {"No common ~s algorithm,~n"
+                             "  we have:~n    ~s~n"
+                             "  peer have:~n    ~s~n",
+                             [Txt,
+                              lists:join(", ", element(W,Own)),
+                              lists:join(", ", element(C,CounterPart))]};
+                       (_) ->
+                            {"No common ~s algorithm", [Txt]}
+                    end,
+                ?SELECT_MSG(MsgFun);
             _ ->
                 {"Kexinit failed in ~p: ~p:~p", [Role,Class,Error]}
         end,
-    try io_lib:format(Fmt, Args) of
+    try io_lib:format(Fmt, Args, [{chars_limit, ssh_lib:max_log_len(Ssh)}]) of
         R -> R
     catch
         _:_ ->
-            io_lib:format("Kexinit failed in ~p: ~p:~p", [Role, Class, Error])
+            io_lib:format("Kexinit failed in ~p: ~p:~p", [Role, Class, Error],
+                          [{chars_limit, ssh_lib:max_log_len(Ssh)}])
     end.
 
 alg_info(client, Alg) ->
@@ -594,14 +604,19 @@
                                              session_id = sid(Ssh1, H)}};
                 {error,unsupported_sign_alg} ->
                     ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
-                                io_lib:format("Unsupported algorithm ~p", [SignAlg])
-                               )
+                                io_lib:format("Unsupported algorithm ~p", [SignAlg],
+                                              [{chars_limit, ssh_lib:max_log_len(Opts)}]))
             end;
 	true ->
-            ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+            MsgFun =
+                fun(debug) ->
                         io_lib:format("Kexdh init failed, received 'e' out of bounds~n  E=~p~n  P=~p",
-                                      [E,P])
-                       )
+                                      [E,P], [{chars_limit, ssh_lib:max_log_len(Opts)}]);
+                   (_) ->
+                        io_lib:format("Kexdh init failed, received 'e' out of bounds", [],
+                                      [{chars_limit, ssh_lib:max_log_len(Opts)}] )
+                end,
+            ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, ?SELECT_MSG(MsgFun))
     end.
 
 handle_kexdh_reply(#ssh_msg_kexdh_reply{public_host_key = PeerPubHostKey,
@@ -622,14 +637,15 @@
                                                              session_id = sid(Ssh, H)})};
 		Error ->
                     ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
-                                io_lib:format("Kexdh init failed. Verify host key: ~p",[Error])
+                                io_lib:format("Kexdh init failed. Verify host key: ~p",[Error],
+                                              [{chars_limit, ssh_lib:max_log_len(Ssh0)}])
                                )
 	    end;
 
 	true ->
             ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
                         io_lib:format("Kexdh init failed, received 'f' out of bounds~n  F=~p~n  P=~p",
-                                      [F,P])
+                                      [F,P], [{chars_limit, ssh_lib:max_log_len(Ssh0)}])
                        )
     end.
 
@@ -655,7 +671,8 @@
 		    }};
 	{error,_} ->
             ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
-                        io_lib:format("No possible diffie-hellman-group-exchange group found",[])
+                        io_lib:format("No possible diffie-hellman-group-exchange group found",[],
+                                      [{chars_limit, ssh_lib:max_log_len(Opts)}])
                        )
     end;
 
@@ -687,8 +704,8 @@
 		    }};
 	{error,_} ->
             ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
-                        io_lib:format("No possible diffie-hellman-group-exchange group found",[])
-                       )
+                        io_lib:format("No possible diffie-hellman-group-exchange group found",[],
+                                      [{chars_limit, ssh_lib:max_log_len(Opts)}]))
     end;
 
 handle_kex_dh_gex_request(_, _) ->
@@ -714,7 +731,6 @@
     {Public, Private} = generate_key(dh, [P,G,2*Sz]),
     {SshPacket, Ssh1} = 
 	ssh_packet(#ssh_msg_kex_dh_gex_init{e = Public}, Ssh0),	% Pub = G^Priv mod P (def)
-
     {ok, SshPacket, 
      Ssh1#ssh{keyex_key = {{Private, Public}, {G, P}}}}.
 
@@ -745,19 +761,22 @@
                                                    }};
                         {error,unsupported_sign_alg} ->
                             ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
-                                        io_lib:format("Unsupported algorithm ~p", [SignAlg])
-                                       )
+                                        io_lib:format("Unsupported algorithm ~p", [SignAlg],
+                                                     [{chars_limit, ssh_lib:max_log_len(Opts)}]))
                     end;
 		true ->
                     ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
-                                "Kexdh init failed, received 'k' out of bounds"
-                               )
+                                "Kexdh init failed, received 'k' out of bounds")
 	    end;
 	true ->
-            ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
-                        io_lib:format("Kexdh gex init failed, received 'e' out of bounds~n  E=~p~n  P=~p",
-                                      [E,P])
-                       )
+            MsgFun =
+                fun(debug) ->
+                        io_lib:format("Kexdh gex init failed, received 'e' out of bounds~n"
+                                      "  E=~p~n  P=~p", [E,P]);
+                   (_) ->
+                        io_lib:format("Kexdh gex init failed, received 'e' out of bounds", [])
+                end,
+            ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, ?SELECT_MSG(MsgFun))
     end.
 
 handle_kex_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{public_host_key = PeerPubHostKey, 
@@ -782,20 +801,18 @@
                                                                      session_id = sid(Ssh, H)})};
                         Error ->
                             ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
-                                        io_lib:format("Kexdh gex reply failed. Verify host key: ~p",[Error])
-                                       )
+                                        io_lib:format("Kexdh gex reply failed. Verify host key: ~p",
+                                                      [Error], [{chars_limit, ssh_lib:max_log_len(Ssh0)}]))
 		    end;
 
 		true ->
                     ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
-                                "Kexdh gex init failed, 'K' out of bounds"
-                               )
+                                "Kexdh gex init failed, 'K' out of bounds")
 	    end;
 	true ->
             ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
                         io_lib:format("Kexdh gex init failed, received 'f' out of bounds~n  F=~p~n  P=~p",
-                                      [F,P])
-                       )
+                                      [F,P], [{chars_limit, ssh_lib:max_log_len(Ssh0)}]))
     end.
 
 %%%----------------------------------------------------------------
@@ -829,17 +846,25 @@
                                              session_id = sid(Ssh1, H)}};
                 {error,unsupported_sign_alg} ->
                     ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
-                                io_lib:format("Unsupported algorithm ~p", [SignAlg])
-                               )
+                                io_lib:format("Unsupported algorithm ~p", [SignAlg],
+                                             [{chars_limit, ssh_lib:max_log_len(Opts)}]))
             end
     catch
-        Class:Error ->
-            ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+        Class:Reason0 ->
+            Reason = ssh_lib:trim_reason(Reason0),
+            MsgFun =
+                fun(debug) ->
                         io_lib:format("ECDH compute key failed in server: ~p:~p~n"
                                       "Kex: ~p, Curve: ~p~n"
                                       "PeerPublic: ~p",
-                                      [Class,Error,Kex,Curve,PeerPublic])
-                       )
+                                      [Class,Reason,Kex,Curve,PeerPublic],
+                                      [{chars_limit, ssh_lib:max_log_len(Ssh0)}]);
+                   (_) ->
+                        io_lib:format("ECDH compute key failed in server: ~p:~p",
+                                      [Class,Reason],
+                                      [{chars_limit, ssh_lib:max_log_len(Ssh0)}])
+                end,
+            ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, ?SELECT_MSG(MsgFun))
     end.
 
 handle_kex_ecdh_reply(#ssh_msg_kex_ecdh_reply{public_host_key = PeerPubHostKey,
@@ -862,15 +887,14 @@
                                                              session_id = sid(Ssh, H)})};
 		Error ->
                     ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
-                                io_lib:format("ECDH reply failed. Verify host key: ~p",[Error])
-                               )
+                                io_lib:format("ECDH reply failed. Verify host key: ~p",[Error],
+                                              [{chars_limit, ssh_lib:max_log_len(Ssh0)}]))
 	    end
     catch
         Class:Error ->
             ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
                         io_lib:format("Peer ECDH public key seem invalid: ~p:~p",
-                                      [Class,Error])
-                       )
+                                      [Class,Error], [{chars_limit, ssh_lib:max_log_len(Ssh0)}]))
     end.
 
 
@@ -1082,7 +1106,7 @@
 %%   algorithm.  Each string MUST contain at least one algorithm name.
 select_algorithm(Role, Client, Server,
                  #ssh{opts = Opts,
-                         kex_strict_negotiated = KexStrictNegotiated0},
+                      kex_strict_negotiated = KexStrictNegotiated0},
                  ReNeg) ->
     KexStrictNegotiated =
         case ReNeg of
@@ -1099,8 +1123,7 @@
                     end,
                 case Result of
                     true ->
-                        error_logger:info_report(
-                          lists:concat([Role, " will use strict KEX ordering"]));
+                        logger:debug(lists:concat([Role, " will use strict KEX ordering"]));
                     _ ->
                         ok
                 end,
@@ -1108,7 +1131,6 @@
             _ ->
                 KexStrictNegotiated0
         end,
-
     {Encrypt0, Decrypt0} = select_encrypt_decrypt(Role, Client, Server),
     {SendMac0, RecvMac0} = select_send_recv_mac(Role, Client, Server),
 
@@ -1122,14 +1144,9 @@
 		   Server#ssh_msg_kexinit.languages_client_to_server),
     S_Lng = select(Client#ssh_msg_kexinit.languages_server_to_client,
 		   Server#ssh_msg_kexinit.languages_server_to_client),
-    HKey = select_all(Client#ssh_msg_kexinit.server_host_key_algorithms,
-		      Server#ssh_msg_kexinit.server_host_key_algorithms),
-    HK = case HKey of
-	     [] -> undefined;
-	     [HK0|_] -> HK0
-	 end,
-    %% Fixme verify Kex against HKey list and algorithms
-    
+    HKey = select(Client#ssh_msg_kexinit.server_host_key_algorithms,
+                  Server#ssh_msg_kexinit.server_host_key_algorithms),
+    %% FIXME verify Kex against HKey list and algorithms (see RFC4253 sec 7.1)
     Kex = select(Client#ssh_msg_kexinit.kex_algorithms,
 		 Server#ssh_msg_kexinit.kex_algorithms),
 
@@ -1149,7 +1166,7 @@
         ?GET_OPT(recv_ext_info,Opts),
 
     {ok, #alg{kex = Kex,
-              hkey = HK,
+              hkey = HKey,
               encrypt = Encrypt,
               decrypt = Decrypt,
               send_mac = SendMac,
@@ -1301,38 +1318,27 @@
     {ok,SSH3} = decompress_final(SSH2),
     SSH3.
 
-
-select_all(CL, SL) when length(CL) + length(SL) < ?MAX_NUM_ALGORITHMS ->
-    %% algorithms only used by client
-    %% NOTE: an algorithm occurring more than once in CL will still be present
-    %%       in CLonly. This is not a problem for nice clients.
-    CLonly = CL -- SL,
-
-    %% algorithms used by client and server (client pref)
-    lists:foldr(fun(ALG, Acc) -> 
-                      try [list_to_existing_atom(ALG) | Acc]
-                      catch
-                          %% If an malicious client uses the same non-existing algorithm twice,
-                          %% we will end up here
-                          _:_ -> Acc
-                      end
-              end, [], (CL -- CLonly));
-
-select_all(CL, SL) ->
-    Error = lists:concat(["Received too many algorithms (",length(CL),"+",length(SL)," >= ",?MAX_NUM_ALGORITHMS,")."]),
-    ?DISCONNECT(?SSH_DISCONNECT_PROTOCOL_ERROR,
-                Error).
-
-
 select([], []) ->
     none;
 select(CL, SL) ->
-    C = case select_all(CL,SL) of
-	    [] -> undefined;
-	    [ALG|_] -> ALG
-	end,
-    C.
-	    
+    select_first(CL, SL).
+
+select_first([ClientAlg | ClientRest], SL) ->
+    case lists:member(ClientAlg, SL) of
+        true ->
+            try list_to_existing_atom(ClientAlg) of
+                Alg when is_atom(Alg) ->
+                    Alg
+            catch
+                error:badarg ->
+                    select_first(ClientRest, SL)
+            end;
+        false ->
+            select_first(ClientRest, SL)
+    end;
+select_first([], _) ->
+    undefined.
+
 ssh_packet(#ssh_msg_kexinit{} = Msg, Ssh0) ->
     BinMsg = ssh_message:encode(Msg),
     Ssh = key_init(Ssh0#ssh.role, Ssh0, BinMsg),
diff -ruN a/lib/ssh/src/ssh_xfer.erl b/lib/ssh/src/ssh_xfer.erl
--- a/lib/ssh/src/ssh_xfer.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/src/ssh_xfer.erl	2025-12-22 23:16:56.269839023 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2005-2023. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2024. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -660,7 +660,7 @@
 	     encode_As(Vsn, As,Flags bor ?SSH_FILEXFER_ATTR_UIDGID,
 		       [?uint32(X) | Acc]);
 	ownergroup when Vsn>=5 ->
-	    X1 = list_to_binary(integer_to_list(X)), % TODO: check owner and group
+	    X1 = integer_to_binary(X), % TODO: check owner and group
 	    encode_As(Vsn, As,Flags bor ?SSH_FILEXFER_ATTR_OWNERGROUP,
 		      [?binary(X1) | Acc]);
 	permissions ->
@@ -736,13 +736,11 @@
 	    decode_As(Vsn, As, setelement(AField, R, X), Flags, Tail2);
 	ownergroup when ?is_set(?SSH_FILEXFER_ATTR_OWNERGROUP, Flags),Vsn>=5 ->
 	    <<?UINT32(Len), Bin:Len/binary, Tail2/binary>> = Tail,
-	    X = binary_to_list(Bin),
+	    X = binary_to_integer(Bin),
 	    decode_As(Vsn, As, setelement(AField, R, X), Flags, Tail2);
-
 	permissions when ?is_set(?SSH_FILEXFER_ATTR_PERMISSIONS,Flags),Vsn>=5->
 	    <<?UINT32(X), Tail2/binary>> = Tail,
 	    decode_As(Vsn, As, setelement(AField, R, X), Flags, Tail2);
-
 	permissions when ?is_set(?SSH_FILEXFER_ATTR_PERMISSIONS,Flags),Vsn=<3->
 	    <<?UINT32(X), Tail2/binary>> = Tail,
 	    R1 = setelement(AField, R, X),
@@ -757,7 +755,6 @@
 		       _ -> unknown
 		   end,
 	    decode_As(Vsn, As, R1#ssh_xfer_attr { type=Type}, Flags, Tail2);
-
 	acmodtime when ?is_set(?SSH_FILEXFER_ATTR_ACMODTIME,Flags),Vsn=<3 ->
 	    <<?UINT32(X), Tail2/binary>> = Tail,
 	    decode_As(Vsn, As, setelement(AField, R, X), Flags, Tail2);
diff -ruN a/lib/ssh/test/.gitignore b/lib/ssh/test/.gitignore
--- a/lib/ssh/test/.gitignore	1970-01-01 09:30:00.000000000 +0930
+++ b/lib/ssh/test/.gitignore	2023-04-26 15:10:42.429802138 +0930
@@ -0,0 +1,7 @@
+*COVER.html
+
+ssh_sftp_SUITE_data/test_data*
+
+property_test/ssh_eqc_client_server_dirs/system
+property_test/ssh_eqc_client_server_dirs/user
+
diff -ruN a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl
--- a/lib/ssh/test/ssh_connection_SUITE.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/test/ssh_connection_SUITE.erl	2025-12-22 23:16:56.269839023 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2023. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -21,9 +21,10 @@
 %%
 -module(ssh_connection_SUITE).
 
--include_lib("common_test/include/ct.hrl").
 -include("ssh_connect.hrl").
 -include("ssh_test_lib.hrl").
+-include_lib("common_test/include/ct.hrl").
+-include_lib("stdlib/include/assert.hrl").
 
 -export([
          suite/0,
@@ -58,7 +59,7 @@
          connect_timeout/1,
          daemon_sock_not_passive/1,
          daemon_sock_not_tcp/1,
-         do_interrupted_send/3,
+         do_interrupted_send/4,
          do_simple_exec/1,
          encode_decode_pty_opts/1,
          exec_disabled/1,
@@ -81,6 +82,7 @@
          send_after_exit/1,
          simple_eval/1,
          simple_exec/1,
+         simple_exec_more_data/1,
          simple_exec_sock/1,
          simple_exec_two_socks/1,
          small_cat/1,
@@ -97,6 +99,7 @@
          start_shell_exec_direct_fun1_error_type/1,
          start_shell_exec_direct_fun2/1,
          start_shell_exec_direct_fun3/1,
+         start_shell_exec_direct_fun_more_data/1,
          start_shell_exec_fun/1,
          start_shell_exec_fun2/1,
          start_shell_exec_fun3/1,
@@ -105,6 +108,9 @@
          start_shell_sock_exec_fun/1,
          start_subsystem_on_closed_channel/1,
          stop_listener/1,
+         trap_exit_connect/1,
+         trap_exit_daemon/1,
+         handler_down_before_open/1,
          ssh_exec_echo/2 % called as an MFA
         ]).
 
@@ -132,6 +138,8 @@
      start_shell,
      new_shell_dumb_term,
      new_shell_xterm_term,
+     trap_exit_connect,
+     trap_exit_daemon,
      start_shell_pty,
      start_shell_exec,
      start_shell_exec_fun,
@@ -140,6 +148,7 @@
      start_shell_exec_direct_fun,
      start_shell_exec_direct_fun2,
      start_shell_exec_direct_fun3,
+     start_shell_exec_direct_fun_more_data,
      start_shell_exec_direct_fun1_error,
      start_shell_exec_direct_fun1_error_type,
      start_exec_direct_fun1_read_write,
@@ -173,13 +182,15 @@
      stop_listener,
      no_sensitive_leak,
      start_subsystem_on_closed_channel,
-     max_channels_option
+     max_channels_option,
+     handler_down_before_open
     ].
 groups() ->
     [{openssh, [], payload() ++ ptty() ++ sock()}].
 
 payload() ->
     [simple_exec,
+     simple_exec_more_data,
      simple_exec_sock,
      simple_exec_two_socks,
      small_cat,
@@ -228,7 +239,7 @@
     %% end_per_testcase will be run!
     end_per_testcase(any, Config),
     ssh:start(),
-    Config.
+    ssh_test_lib:verify_sanity_check(Config).
 
 end_per_testcase(_TestCase, _Config) ->
     ssh:stop().
@@ -240,6 +251,10 @@
     ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, []),
     do_simple_exec(ConnectionRef).
 
+simple_exec_more_data(Config) when is_list(Config) ->
+    ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, []),
+    %% more data received, SSH window adjust needs to be sent by client
+    do_simple_exec(ConnectionRef, 60000).
 %%--------------------------------------------------------------------
 simple_exec_sock(_Config) ->
     {ok, Sock} = ssh_test_lib:gen_tcp_connect(?SSH_DEFAULT_PORT, [{active,false}]),
@@ -630,15 +645,24 @@
     ssh:close(ConnectionRef).
 
 %%--------------------------------------------------------------------
-small_interrupted_send(Config) -> 
+%%- small_interrupted_send is interrupted by ssh_echo_server which is
+%%  done with transferring data towards client and terminates the
+%%  channel (this results with {error, closed} return value from
+%%  ssh_connection:send on the client side)
+%%- interrupted_send is interrupted when ssh_echo_server ran
+%%  out of ssh data window and closed channel
+small_interrupted_send(Config) ->
     K = 1024,
-    M = K*K,
-    do_interrupted_send(Config, 10*M, 4*K).
+    SendSize = 10 * K * K,
+    EchoSize = 4 * K,
+    do_interrupted_send(Config, SendSize, EchoSize, {error, closed}).
 interrupted_send(Config) ->
-    M = 1024*1024,
-    do_interrupted_send(Config, 10*M, 4*M).
+    K = 1024,
+    SendSize = 10 * K * K,
+    EchoSize = 4 * K * K,
+    do_interrupted_send(Config, SendSize, EchoSize, ok).
 
-do_interrupted_send(Config, SendSize, EchoSize) ->
+do_interrupted_send(Config, SendSize, EchoSize, SenderResult) ->
     PrivDir = proplists:get_value(priv_dir, Config),
     UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
     file:make_dir(UserDir),
@@ -648,7 +672,6 @@
 					     {user_dir, UserDir},
 					     {password, "morot"},
 					     {subsystems, [{"echo_n",EchoSS_spec}]}]),
-    
     ct:log("~p:~p connect", [?MODULE,?LINE]),
     ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
 						      {user, "foo"},
@@ -656,28 +679,24 @@
 						      {user_interaction, false},
 						      {user_dir, UserDir}]),
     ct:log("~p:~p connected", [?MODULE,?LINE]),
-
     %% build big binary
     Data = << <<X:32>> || X <- lists:seq(1,SendSize div 4)>>,
-
     %% expect remote end to send us EchoSize back
     <<ExpectedData:EchoSize/binary, _/binary>> = Data,
-
     %% Spawn listener. Otherwise we could get a deadlock due to filled buffers
     Parent = self(),
     ResultPid = spawn(
 		  fun() ->
 			  ct:log("~p:~p open channel",[?MODULE,?LINE]),
 			  {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
-			  ct:log("~p:~p start subsystem", [?MODULE,?LINE]),
+			  ct:log("~p:~p start ssh subsystem", [?MODULE,?LINE]),
 			  case ssh_connection:subsystem(ConnectionRef, ChannelId, "echo_n", infinity) of
 			      success ->
 				  Parent ! {self(), channelId, ChannelId},
-				  
-				  Result = 
+				  Result =
 				      try collect_data(ConnectionRef, ChannelId, EchoSize)
 				      of
-					  ExpectedData -> 
+					  ExpectedData ->
 					      ct:log("~p:~p got expected data",[?MODULE,?LINE]),
 					      ok;
 					  Other ->
@@ -692,40 +711,40 @@
 				  Parent ! {self(), channelId, error, Other}
 			  end
 		  end),
-    
     receive
 	{ResultPid, channelId, error, Other} ->
 	    ct:log("~p:~p channelId error ~p", [?MODULE,?LINE,Other]),
 	    ssh:close(ConnectionRef),
 	    ssh:stop_daemon(Pid),
 	    {fail, "ssh_connection:subsystem"};
-
 	{ResultPid, channelId, ChannelId} ->
 	    ct:log("~p:~p ~p going to send ~p bytes", [?MODULE,?LINE,self(),size(Data)]),
 	    SenderPid = spawn(fun() ->
 				      Parent ! {self(),  ssh_connection:send(ConnectionRef, ChannelId, Data, 30000)}
 			      end),
+            ct:log("SenderPid = ~p", [SenderPid]),
 	    receive
 	    	{ResultPid, result, {fail, Fail}} ->
 		    ct:log("~p:~p Listener failed: ~p", [?MODULE,?LINE,Fail]),
 		    {fail, Fail};
-
 		{ResultPid, result, Result} ->
 		    ct:log("~p:~p Got result: ~p", [?MODULE,?LINE,Result]),
 		    ssh:close(ConnectionRef),
 		    ssh:stop_daemon(Pid),
 		    ct:log("~p:~p Check sender", [?MODULE,?LINE]),
 		    receive
-			{SenderPid, {error, closed}} ->
-			    ct:log("~p:~p {error,closed} - That's what we expect :)",[?MODULE,?LINE]),
+			{SenderPid, SenderResult} ->
+			    ct:log("~p:~p ~p - That's what we expect :)",
+                                   [?MODULE,?LINE, SenderResult]),
 			    ok;
 			Msg ->
 			    ct:log("~p:~p Not expected send result: ~p",[?MODULE,?LINE,Msg]),
 			    {fail, "Not expected msg"}
 		    end;
-
 		{SenderPid, {error, closed}} ->
-		    ct:log("~p:~p {error,closed} - That's what we expect, but client channel handler has not reported yet",[?MODULE,?LINE]),
+		    ct:log("~p:~p ~p - That's what we expect, "
+                           "but client channel handler has not reported yet",
+                           [?MODULE,?LINE, SenderResult]),
 		    receive
 			{ResultPid, result, Result} ->
 			    ct:log("~p:~p Now got the result: ~p", [?MODULE,?LINE,Result]),
@@ -736,7 +755,6 @@
 			    ct:log("~p:~p Got an unexpected msg ~p",[?MODULE,?LINE,Msg]),
 			    {fail, "Un-expected msg"}
 		    end;
-
 		Msg ->
 		    ct:log("~p:~p Got unexpected ~p",[?MODULE,?LINE,Msg]),
 		    {fail, "Unexpected msg"}
@@ -1005,6 +1023,24 @@
                             "testing", <<"echo foo testing">>, 0,
                             Config).
 
+start_shell_exec_direct_fun_more_data(Config) ->
+    N = 60000,
+    ExpectedBin = <<"testing\n">>,
+    ReceiveFun =
+        fun(ConnectionRef, ChannelId, _Expect, _ExpectType) ->
+                receive_bytes(ConnectionRef, ChannelId,
+                              N * byte_size(ExpectedBin), 0)
+        end,
+    do_start_shell_exec_fun({direct,
+                             fun(_Cmd) ->
+                                     {ok,
+                                      [io_lib:format("testing~n",[]) ||
+                                          _ <- lists:seq(1, N)]}
+                             end},
+                            "not_relevant", <<"not_used\n">>, 0,
+                            ReceiveFun,
+                            Config).
+
 start_shell_exec_direct_fun1_error(Config) ->
     do_start_shell_exec_fun({direct, fun(_Cmd) -> {error, {bad}} end},
                             "testing", <<"**Error** {bad}">>, 1,
@@ -1139,6 +1175,28 @@
 
 
 do_start_shell_exec_fun(Fun, Command, Expect, ExpectType, Config) ->
+    DefaultReceiveFun =
+        fun(ConnectionRef, ChannelId, _Expect, _ExpectType) ->
+                receive
+                    {ssh_cm, ConnectionRef, {data, ChannelId, ExpectType, Expect}} ->
+                        ok
+                after 5000 ->
+                        receive
+                            Other ->
+                                ct:log("Received other:~n~p~nExpected: ~p~n",
+                                       [Other,
+                                        {ssh_cm, ConnectionRef,
+                                         {data, ChannelId, ExpectType, Expect}}]),
+                                         %% {data, '_ChannelId', ExpectType, Expect}}]),
+                                ct:fail("Unexpected response")
+                        after 0 ->
+                                ct:fail("Exec Timeout")
+                        end
+                end
+        end,
+    do_start_shell_exec_fun(Fun, Command, Expect, ExpectType, DefaultReceiveFun, Config).
+
+do_start_shell_exec_fun(Fun, Command, Expect, ExpectType, ReceiveFun, Config) ->
     PrivDir = proplists:get_value(priv_dir, Config),
     UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
     file:make_dir(UserDir),
@@ -1154,26 +1212,64 @@
 						      {user_interaction, true},
 						      {user_dir, UserDir}]),
 
-    {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+    {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
+    success = ssh_connection:exec(ConnectionRef, ChannelId, Command, infinity),
+    ReceiveFun(ConnectionRef, ChannelId, Expect, ExpectType),
+    ssh:close(ConnectionRef),
+    ssh:stop_daemon(Pid).
 
-    success = ssh_connection:exec(ConnectionRef, ChannelId0, Command, infinity),
+%%--------------------------------------------------------------------
+%% Issue GH-8223
+trap_exit_connect(Config) when is_list(Config) ->
+    PrivDir = proplists:get_value(priv_dir, Config),
+    UserDir = filename:join(PrivDir, nopubkey),
+    file:make_dir(UserDir),
+    SysDir = proplists:get_value(data_dir, Config),
+    {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+                                             {user_dir, UserDir},
+                                             {password, "morot"}]),
+    %% Fake an EXIT message
+    ExitMsg = {'EXIT', self(), make_ref()},
+    self() ! ExitMsg,
+
+    {ok, ConnectionRef} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
+                                                   {save_accepted_host, false},
+                                                   {user, "foo"},
+                                                   {password, "morot"},
+                                                   {user_interaction, true},
+                                                   {user_dir, UserDir}]),
+    ssh:close(ConnectionRef),
+    ssh:stop_daemon(Pid),
 
+    %% Ensure the EXIT message is still there
     receive
-	{ssh_cm, ConnectionRef, {data, _ChannelId, ExpectType, Expect}} ->
-	    ok
-    after 5000 ->
-            receive
-                Other ->
-                    ct:log("Received other:~n~p~nExpected: ~p~n",
-                           [Other, {ssh_cm, ConnectionRef, {data, '_ChannelId', ExpectType, Expect}} ]),
-                    ct:fail("Unexpected response")
-            after 0 ->
-                    ct:fail("Exec Timeout")
-            end
-    end,
+        ExitMsg -> ok
+    after 0 ->
+        ct:fail("No EXIT message")
+    end.
 
-    ssh:close(ConnectionRef),
-    ssh:stop_daemon(Pid).
+%%--------------------------------------------------------------------
+%% Issue GH-8223
+trap_exit_daemon(Config) when is_list(Config) ->
+    PrivDir = proplists:get_value(priv_dir, Config),
+    UserDir = filename:join(PrivDir, nopubkey),
+    file:make_dir(UserDir),
+    SysDir = proplists:get_value(data_dir, Config),
+
+    %% Fake an EXIT message
+    ExitMsg = {'EXIT', self(), make_ref()},
+    self() ! ExitMsg,
+
+    {ok, DaemonRef} = ssh:daemon(0, [{system_dir, SysDir},
+                                     {user_dir, UserDir}]),
+    ssh:stop_daemon(DaemonRef),
+
+    %% Ensure the EXIT message is still there
+    receive
+        ExitMsg -> ok
+    after 0 ->
+        ct:fail("No EXIT message")
+    end.
 
 %%--------------------------------------------------------------------
 start_shell_sock_exec_fun(Config) when is_list(Config) ->
@@ -1417,6 +1513,8 @@
     end.
 
 kex_error(Config) ->
+    #{level := Level} = logger:get_primary_config(),
+    ok = logger:set_primary_config(level, debug),
     PrivDir = proplists:get_value(priv_dir, Config),
     UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
     file:make_dir(UserDir),
@@ -1437,6 +1535,10 @@
                                    ok % Other msg
                            end,
                            self()),
+    Cleanup = fun() ->
+                      ok = logger:remove_handler(kex_error),
+                      ok = logger:set_primary_config(level, Level)
+              end,
     try
         ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
                                           {user, "foo"},
@@ -1454,7 +1556,7 @@
             %% ok
             receive
                 {Ref, ErrMsgTxt} ->
-                    ok = logger:remove_handler(kex_error),
+                    Cleanup(),
                     ct:log("ErrMsgTxt = ~n~s", [ErrMsgTxt]),
                     Lines = lists:map(fun string:trim/1, string:tokens(ErrMsgTxt, "\n")),
                     OK = (lists:all(fun(S) -> lists:member(S,Lines) end,
@@ -1472,12 +1574,12 @@
                             ct:fail("unexpected error text msg", [])
                     end
             after 20000 ->
-                    ok = logger:remove_handler(kex_error),
+                    Cleanup(),
                     ct:fail("timeout", [])
             end;
 
         error:{badmatch,{error,_}} ->
-            ok = logger:remove_handler(kex_error),
+            Cleanup(),
             ct:fail("unexpected error msg", [])
     end.
 
@@ -1729,41 +1831,191 @@
     ssh:close(ConnectionRef),
     ssh:stop_daemon(Pid).
 
+handler_down_before_open(Config) ->
+    %% Start echo subsystem with a delay in init() - until a signal is received
+    %% One client opens a channel on the connection
+    %% the other client requests the echo subsystem on the second channel and then immediately goes down
+    %% the test monitors the client and when receiving 'DOWN' signals 'echo' to proceed
+    %% a) there should be no crash after 'channel-open-confirmation'
+    %% b) there should be proper 'channel-close' exchange
+    %% c) the 'exec' channel should not be affected after the 'echo' channel goes down
+    PrivDir = proplists:get_value(priv_dir, Config),
+    UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+    file:make_dir(UserDir),
+    SysDir = proplists:get_value(data_dir, Config),
+    Parent = self(),
+    EchoSS_spec = {ssh_echo_server, [8, [{dbg, true}, {parent, Parent}]]},
+    {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+					     {user_dir, UserDir},
+					     {password, "morot"},
+					     {exec, fun ssh_exec_echo/1},
+					     {subsystems, [{"echo_n",EchoSS_spec}]}]),
+    ct:log("~p:~p connect", [?MODULE,?LINE]),
+    ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+						      {user, "foo"},
+						      {password, "morot"},
+						      {user_interaction, false},
+						      {user_dir, UserDir}]),
+    ct:log("~p:~p connected", [?MODULE,?LINE]),
+
+    ExecChannelPid =
+        spawn(
+          fun() ->
+                  {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+
+                  %% This is to get peer's connection handler PID ({conn_peer ...} below) and suspend it
+                  {ok, ChannelId1} = ssh_connection:session_channel(ConnectionRef, infinity),
+                  ssh_connection:subsystem(ConnectionRef, ChannelId1, "echo_n", infinity),
+                  ssh_connection:close(ConnectionRef, ChannelId1),
+                  receive
+                      {ssh_cm, ConnectionRef, {closed, 1}} -> ok
+                  end,
+
+                  Parent ! {self(), channelId, ChannelId0},
+                  Result = receive
+                               cmd ->
+                                   ct:log("~p:~p Channel ~p executing", [?MODULE, ?LINE, ChannelId0]),
+                                   success = ssh_connection:exec(ConnectionRef, ChannelId0, "testing", infinity),
+                                   Expect = <<"echo testing\n">>,
+                                   ExpSz = size(Expect),
+                                   receive
+                                       {ssh_cm, ConnectionRef, {data, ChannelId0, 0,
+                                                                <<Expect:ExpSz/binary, _/binary>>}} = R ->
+                                           ct:log("~p:~p Got expected ~p",[?MODULE,?LINE, R]),
+                                           ok;
+                                       Other ->
+                                           ct:log("~p:~p Got unexpected ~p~nExpect: ~p~n",
+                                                  [?MODULE,?LINE, Other, {ssh_cm, ConnectionRef,
+                                                                          {data, ChannelId0, 0, Expect}}]),
+                                           {fail, "Unexpected data"}
+                                   after 5000 ->
+                                           {fail, "Exec Timeout"}
+                                   end;
+                               stop -> {fail, "Stopped"}
+                           end,
+                  Parent ! {self(), Result}
+          end),
+    try
+        receive
+            {ExecChannelPid, channelId, ExId} ->
+                ct:log("~p:~p Channel that should stay: ~p pid ~p",
+                       [?MODULE, ?LINE, ExId, ExecChannelPid]),
+                %% This is sent by the echo subsystem as a reaction to channel1 above
+                ConnPeer = receive {conn_peer, CM} -> CM end,
+                %% The sole purpose of this channel is to go down
+                %% before the opening procedure is complete
+                DownChannelPid = spawn(
+                    fun() ->
+                        ct:log("~p:~p open channel (incomplete)",[?MODULE,?LINE]),
+                        Parent ! {self(), channelId, ok},
+                        %% This is to prevent the peer from answering our 'channel-open' in time
+                        sys:suspend(ConnPeer),
+                        {ok, _} = ssh_connection:session_channel(ConnectionRef, infinity)
+                    end),
+                MonRef = erlang:monitor(process, DownChannelPid),
+                receive
+                    {DownChannelPid, channelId, ok} ->
+                        ct:log("~p:~p Channel handler that won't continue: pid ~p",
+                               [?MODULE, ?LINE, DownChannelPid]),
+                        ensure_channels(ConnectionRef, 2),
+                        channel_down_sequence(DownChannelPid, ExecChannelPid,
+                                              ExId, MonRef, ConnectionRef, ConnPeer)
+                end
+        end,
+        ensure_channels(ConnectionRef, 0)
+    after
+        ssh:close(ConnectionRef),
+        ssh:stop_daemon(Pid)
+    end.
+
+ensure_channels(ConnRef, Expected) ->
+    {ok, ChannelList} = ssh_connection_handler:info(ConnRef),
+    do_ensure_channels(ConnRef, Expected, length(ChannelList)).
+
+do_ensure_channels(_ConnRef, NumExpected, NumExpected) ->
+    ok;
+do_ensure_channels(ConnRef, NumExpected, _ChannelListLen) ->
+    ct:sleep(100),
+    {ok, ChannelList} = ssh_connection_handler:info(ConnRef),
+    do_ensure_channels(ConnRef, NumExpected, length(ChannelList)).
+
+channel_down_sequence(DownChannelPid, ExecChannelPid, ExecChannelId, MonRef, ConnRef, Peer) ->
+    ct:log("~p:~p sending order to ~p to go down", [?MODULE, ?LINE, DownChannelPid]),
+    exit(DownChannelPid, die),
+    receive {'DOWN', MonRef, _, _, _} -> ok end,
+    ct:log("~p:~p order executed, sending order to ~p to proceed", [?MODULE, ?LINE, Peer]),
+    %% Resume the peer connection to let it clean up among its channels
+    sys:resume(Peer),
+    ensure_channels(ConnRef, 1),
+    ExecChannelPid ! cmd,
+    try
+        receive
+            {ExecChannelPid, ok} ->
+                ct:log("~p:~p expected exec result: ~p", [?MODULE, ?LINE, ok]),
+                ok;
+            {ExecChannelPid, Result} ->
+                ct:log("~p:~p Unexpected exec result: ~p", [?MODULE, ?LINE, Result]),
+                {fail, "Unexpected exec result"}
+        after 5000 ->
+            {fail, "Exec result timeout"}
+        end
+    after
+        ssh_connection:close(ConnRef, ExecChannelId)
+    end.
+
 %%--------------------------------------------------------------------
 %% Internal functions ------------------------------------------------
 %%--------------------------------------------------------------------
-
 do_simple_exec(ConnectionRef) ->
+    do_simple_exec(ConnectionRef, 1).
+
+do_simple_exec(ConnectionRef, N) ->
     {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
-    success = ssh_connection:exec(ConnectionRef, ChannelId0,
-				  "echo testing", infinity),
-    %% receive response to input
-    receive
-	{ssh_cm, ConnectionRef, {data, ChannelId0, 0, <<"testing\n">>}} ->
-	    ok
-    after
-	10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
+    Cmd = "yes testing | head -n " ++ integer_to_list(N),
+    ct:log("Cmd to be invoked over SSH shell: ~p", [Cmd]),
+    success = ssh_connection:exec(ConnectionRef, ChannelId0, Cmd, infinity),
+    ExpectedBin = <<"testing\n">>,
+    case N of
+        1 ->
+            %% receive response to input
+            receive
+                {ssh_cm, ConnectionRef, {data, ChannelId0, 0, ExpectedBin}} ->
+                    ok
+            after
+                10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
+            end;
+        _ ->
+            receive_bytes(ConnectionRef, ChannelId0, N * byte_size(ExpectedBin), 0)
     end,
-
     %% receive close messages
-    receive
-	{ssh_cm, ConnectionRef, {eof, ChannelId0}} ->
-	    ok
-    after
-	10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
-    end,
-    receive
-	{ssh_cm, ConnectionRef, {exit_status, ChannelId0, 0}} ->
-	    ok
+    CloseMessages =
+        [{ssh_cm, ConnectionRef, {eof, ChannelId0}},
+         {ssh_cm, ConnectionRef, {closed, ChannelId0}}],
+    Timeout = 10000,
+    [receive
+         M ->
+             ct:log("Received M = ~w", [M]),
+             ok
+     after
+         Timeout ->
+             ct:log("M = ~w not found !", [M]),
+             ct:log("Messages in queue =~n~p", [process_info(self(), messages)]),
+             ct:fail("timeout ~p:~p",[?MODULE,?LINE])
+     end || M <- CloseMessages],
+    receive
+        %% 141 is exit status of `yes testing | head -n 1` on tcsh
+        %% other shells return 0
+        ExitMsg = {ssh_cm, ConnectionRef, {exit_status, ChannelId0, ExitStatus}}
+          when ExitStatus == 0; ExitStatus == 141 ->
+            ct:log("Received M = ~w", [ExitMsg]),
+            ok
     after
-	10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
+        Timeout ->
+            ct:log("Acceptable exit status not received"),
+            ct:log("Messages in queue =~n~p", [process_info(self(), messages)]),
+            ct:fail("timeout ~p:~p",[?MODULE,?LINE])
     end,
-    receive
-	{ssh_cm, ConnectionRef,{closed, ChannelId0}} ->
-	    ok
-    after
-	10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
-    end.
+    ok.
 
 
 %%--------------------------------------------------------------------
@@ -1866,6 +2118,7 @@
         {ssh_cm, ConnectionRef, {data, ChannelId, 0, <<Expect:ExpSz/binary, _/binary>>}} = R ->
             ct:log("~p:~p Got expected ~p",[?MODULE,?LINE,R]);
         Other ->
+            %% FIXME - should this testcase fail when unexpected data is received?
             ct:log("~p:~p Got unexpected ~p~nExpect: ~p~n",
                    [?MODULE,?LINE, Other, {ssh_cm, ConnectionRef, {data, ChannelId, 0, Expect}} ])
     after 5000 ->
@@ -1879,8 +2132,6 @@
 big_cat_rx(ConnectionRef, ChannelId, Acc) ->
     receive
 	{ssh_cm, ConnectionRef, {data, ChannelId, 0, Data}} ->
-	    %% ssh_connection:adjust_window(ConnectionRef, ChannelId, size(Data)),
-	    %% window was pre-adjusted, don't adjust again here
 	    big_cat_rx(ConnectionRef, ChannelId, [Data | Acc]);
 	{ssh_cm, ConnectionRef, {eof, ChannelId}} ->
 	    {ok, iolist_to_binary(lists:reverse(Acc))}
@@ -1889,7 +2140,8 @@
     end.
 
 collect_data(ConnectionRef, ChannelId, EchoSize) ->
-    ct:log("~p:~p Listener ~p running! ConnectionRef=~p, ChannelId=~p",[?MODULE,?LINE,self(),ConnectionRef,ChannelId]),
+    ct:log("~p:~p Listener ~p running! ConnectionRef=~p, ChannelId=~p",
+           [?MODULE,?LINE,self(),ConnectionRef,ChannelId]),
     collect_data(ConnectionRef, ChannelId, EchoSize, [], 0).
 
 collect_data(ConnectionRef, ChannelId, EchoSize, Acc, Sum) ->
@@ -1898,18 +2150,15 @@
 	{ssh_cm, ConnectionRef, {data, ChannelId, 0, Data}} when is_binary(Data) ->
 	    ct:log("~p:~p collect_data: received ~p bytes. total ~p bytes,  want ~p more",
 		   [?MODULE,?LINE,size(Data),Sum+size(Data),EchoSize-Sum]),
-	    ssh_connection:adjust_window(ConnectionRef, ChannelId, size(Data)),
+            ssh_connection:adjust_window(ConnectionRef, ChannelId, size(Data)),
 	    collect_data(ConnectionRef, ChannelId, EchoSize, [Data | Acc], Sum+size(Data));
 	{ssh_cm, ConnectionRef, Msg={eof, ChannelId}} ->
 	    collect_data_report_end(Acc, Msg, EchoSize);
-
 	{ssh_cm, ConnectionRef, Msg={closed,ChannelId}} ->
 	    collect_data_report_end(Acc, Msg, EchoSize);
-
 	Msg ->
 	    ct:log("~p:~p collect_data: ***** unexpected message *****~n~p",[?MODULE,?LINE,Msg]),
 	    collect_data(ConnectionRef, ChannelId, EchoSize, Acc, Sum)
-
     after TO ->
 	    ct:log("~p:~p collect_data: ----- Nothing received for ~p seconds -----~n",[?MODULE,?LINE,TO]),
 	    collect_data(ConnectionRef, ChannelId, EchoSize, Acc, Sum)
@@ -1953,3 +2202,24 @@
     spawn(fun() ->
                   io:format("echo ~s ~s\n",[User,Cmd])
           end).
+%% FIXME - upon refactoring this test suite, check if function below is reduntant to collect_data
+receive_bytes(_, _, 0, _) ->
+    ct:log("ALL DATA RECEIVED Budget = 0"),
+    ct:log("================================ ExpectBudget = 0 (reception completed)"),
+    ok;
+receive_bytes(ConnectionRef, ChannelId0, Budget, AccSize) when Budget > 0 ->
+    receive
+        {ssh_cm, ConnectionRef, {data, ChannelId0, 0, D}} ->
+            Fmt = "================================ ExpectBudget = "
+                "~p bytes Received/Total = ~p/~p bytes",
+            Args = [Budget, byte_size(D), AccSize + byte_size(D)],
+            ct:log(Fmt, Args),
+            ssh_connection:adjust_window(ConnectionRef, ChannelId0, size(D)),
+            receive_bytes(ConnectionRef, ChannelId0,
+                          Budget - byte_size(D), AccSize + byte_size(D))
+    after
+        10000 ->
+            ct:log("process_info(self(), messages) = ~p",
+                   [process_info(self(), messages)]),
+            ct:fail("timeout ~p:~p",[?MODULE,?LINE])
+    end.
diff -ruN a/lib/ssh/test/ssh.cover b/lib/ssh/test/ssh.cover
--- a/lib/ssh/test/ssh.cover	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/test/ssh.cover	2025-12-17 17:25:02.078556443 +1030
@@ -7,7 +7,7 @@
 
   %% %% Supervisors
   %% ssh_acceptor_sup, ssh_channel_sup,
-  %% sshc_sup, sshd_sup, ssh_subsystem_sup, ssh_sup,
+  %% sshc_sup, sshd_sup, ssh_connection_sup, ssh_sup,
   %% ssh_system_sup, ssh_tcpip_forward_acceptor_sup,
 
   %% Test and/or info modules:
diff -ruN a/lib/ssh/test/ssh_dbg_SUITE.erl b/lib/ssh/test/ssh_dbg_SUITE.erl
--- a/lib/ssh/test/ssh_dbg_SUITE.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/test/ssh_dbg_SUITE.erl	2025-12-17 17:25:02.079556456 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2018-2021. All Rights Reserved.
+%% Copyright Ericsson AB 2018-2024. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -336,6 +336,12 @@
     ?DBG_RECEIVE("Received SSH_MSG_KEXINIT:",      Ref, C, Pid),
 
     case atom_to_list( (ssh_connection_handler:alg(C))#alg.kex ) of
+        "curve"++_ ->
+            ?DBG_RECEIVE("Going to send SSH_MSG_KEX_ECDH_INIT:",  Ref, C, Pid),
+            ?DBG_RECEIVE("Received SSH_MSG_KEX_ECDH_INIT:",       Ref, D, Pid),
+            ?DBG_RECEIVE("Going to send SSH_MSG_KEX_ECDH_REPLY:", Ref, D, Pid),
+            ?DBG_RECEIVE("Received SSH_MSG_KEX_ECDH_REPLY:",      Ref, C, Pid);
+
         "ecdh-"++_ ->
             ?DBG_RECEIVE("Going to send SSH_MSG_KEX_ECDH_INIT:",  Ref, C, Pid),
             ?DBG_RECEIVE("Received SSH_MSG_KEX_ECDH_INIT:",       Ref, D, Pid),
diff -ruN a/lib/ssh/test/ssh_echo_server.erl b/lib/ssh/test/ssh_echo_server.erl
--- a/lib/ssh/test/ssh_echo_server.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/test/ssh_echo_server.erl	2025-12-17 17:25:02.079556456 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2005-2021. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -27,7 +27,8 @@
 	  n,
 	  id,
 	  cm,
-	  dbg = false
+	  dbg = false,
+          parent
 	 }).
 -export([init/1, handle_msg/2, handle_ssh_msg/2, terminate/2]).
 
@@ -42,13 +43,19 @@
     {ok, #state{n = N}};
 init([N,Opts]) ->
     State = #state{n = N,
-		   dbg = proplists:get_value(dbg,Opts,false)
+		   dbg = proplists:get_value(dbg,Opts,false),
+                   parent = proplists:get_value(parent, Opts)
 		  },
     ?DBG(State, "init([~p])",[N]),
     {ok, State}.
 
 handle_msg({ssh_channel_up, ChannelId, ConnectionManager}, State) ->
     ?DBG(State, "ssh_channel_up Cid=~p ConnMngr=~p",[ChannelId,ConnectionManager]),
+    Pid = State#state.parent,
+    if Pid /= undefined ->
+            Pid ! {conn_peer, ConnectionManager};
+       true -> ok
+    end,
     {ok, State#state{id = ChannelId,
 		     cm = ConnectionManager}}.
 
diff -ruN a/lib/ssh/test/ssh_limited.cover b/lib/ssh/test/ssh_limited.cover
--- a/lib/ssh/test/ssh_limited.cover	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/test/ssh_limited.cover	2025-12-17 17:25:02.080556468 +1030
@@ -9,7 +9,7 @@
 
   %% Supervisors
   ssh_acceptor_sup, ssh_channel_sup,
-  sshc_sup, sshd_sup, ssh_subsystem_sup, ssh_sup,
+  sshc_sup, sshd_sup, ssh_connection_sup, ssh_sup,
   ssh_system_sup, ssh_tcpip_forward_acceptor_sup,
 
   %% Test and/or info modules:
diff -ruN a/lib/ssh/test/ssh_options_SUITE.erl b/lib/ssh/test/ssh_options_SUITE.erl
--- a/lib/ssh/test/ssh_options_SUITE.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/test/ssh_options_SUITE.erl	2025-12-22 23:16:56.269839023 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2022. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -88,7 +88,8 @@
          daemon_replace_options_simple/1,
          daemon_replace_options_algs/1,
          daemon_replace_options_algs_connect/1,
-         daemon_replace_options_algs_conf_file/1
+         daemon_replace_options_algs_conf_file/1,
+         daemon_replace_options_not_found/1
 	]).
 
 %%% Common test callbacks
@@ -159,6 +160,7 @@
      daemon_replace_options_algs,
      daemon_replace_options_algs_connect,
      daemon_replace_options_algs_conf_file,
+     daemon_replace_options_not_found,
      {group, hardening_tests}
     ].
 
@@ -2033,6 +2035,14 @@
     end.
 
 %%--------------------------------------------------------------------
+daemon_replace_options_not_found(_Config) ->
+    %% when the daemon doesn't exist the error should be the same
+    %% in daemon_info and daemon_replace_options
+    %% which is {error, bad_daemon_ref}
+    Error = ssh:daemon_info(self()),
+    Error = ssh:daemon_replace_options(self(), []).
+
+%%--------------------------------------------------------------------
 %% Internal functions ------------------------------------------------
 %%--------------------------------------------------------------------
 
diff -ruN a/lib/ssh/test/ssh_protocol_SUITE.erl b/lib/ssh/test/ssh_protocol_SUITE.erl
--- a/lib/ssh/test/ssh_protocol_SUITE.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/test/ssh_protocol_SUITE.erl	2025-12-22 23:16:56.270839035 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2023. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@
 -include_lib("kernel/include/inet.hrl").
 -include("ssh.hrl").		% ?UINT32, ?BYTE, #ssh{} ...
 -include("ssh_transport.hrl").
+-include("ssh_connect.hrl").
 -include("ssh_auth.hrl").
 -include("ssh_test_lib.hrl").
 
@@ -48,6 +49,7 @@
          bad_service_name_then_correct/1,
          bad_very_long_service_name/1,
          client_handles_keyboard_interactive_0_pwds/1,
+         client_handles_banner_keyboard_interactive/1,
          client_info_line/1,
          do_gex_client_init/3,
          do_gex_client_init_old/3,
@@ -55,7 +57,10 @@
          ext_info_c/1,
          ext_info_s/1,
          kex_strict_negotiated/1,
-         kex_strict_msg_ignore/1,
+         kex_strict_violation_key_exchange/1,
+         kex_strict_violation_new_keys/1,
+         kex_strict_violation/1,
+         kex_strict_violation_2/1,
          kex_strict_msg_unknown/1,
          gex_client_init_option_groups/1,
          gex_client_init_option_groups_file/1,
@@ -73,6 +78,8 @@
          modify_rm/1,
          no_common_alg_client_disconnects/1,
          no_common_alg_server_disconnects/1,
+         custom_kexinit/1,
+         early_rce/1,
          no_ext_info_s1/1,
          no_ext_info_s2/1,
          packet_length_too_large/1,
@@ -80,7 +87,9 @@
          preferred_algorithms/1,
          service_name_length_too_large/1,
          service_name_length_too_short/1,
-         client_close_after_hello/1
+         client_close_after_hello/1,
+         channel_close_timeout/1,
+         extra_ssh_msg_service_request/1
         ]).
 
 -define(NEWLINE, <<"\r\n">>).
@@ -94,11 +103,19 @@
                                    [{client2server,Ciphs}, {server2client,Ciphs}]
                           end)()
         ).
-
-
 -define(v(Key, Config), proplists:get_value(Key, Config)).
 -define(v(Key, Config, Default), proplists:get_value(Key, Config, Default)).
-
+-define(HARDCODED_KEXDH_REPLY,
+        #ssh_msg_kexdh_reply{
+           public_host_key = {{{'ECPoint',<<73,72,235,162,96,101,154,59,217,114,123,192,96,105,250,29,214,76,60,63,167,21,221,118,246,168,152,2,7,172,137,125>>},
+                               {namedCurve,{1,3,101,112}}},
+                              'ssh-ed25519'},
+           f = 18504393053016436370762156176197081926381112956345797067569792020930728564439992620494295053804030674742529174859108487694089045521619258420515443400605141150065440678508889060925968846155921972385560196703381004650914261218463420313738628465563288022895912907728767735629532940627575655703806353550720122093175255090704443612257683903495753071530605378193139909567971489952258218767352348904221407081210633467414579377014704081235998044497191940270966762124544755076128392259615566530695493013708460088312025006678879288856957348606386230195080105197251789635675011844976120745546472873505352732719507783227210178188,
+           h_sig = <<90,247,44,240,136,196,82,215,56,165,53,33,230,101,253,
+                     34,112,201,21,131,162,169,10,129,174,14,69,25,39,174,
+                     92,210,130,249,103,2,215,245,7,213,110,235,136,134,11,
+                     124,248,139,79,17,225,77,125,182,204,84,137,167,99,186,
+                     167,42,192,10>>}).
 
 %%--------------------------------------------------------------------
 %% Common Test interface functions -----------------------------------
@@ -111,6 +128,7 @@
 all() -> 
     [{group,tool_tests},
      client_info_line,
+     early_rce,
      {group,kex},
      {group,service_requests},
      {group,authentication},
@@ -118,7 +136,8 @@
      {group,field_size_error},
      {group,ext_info},
      {group,preferred_algorithms},
-     {group,client_close_early}
+     {group,client_close_early},
+     {group,channel_close}
     ].
 
 groups() ->
@@ -129,11 +148,10 @@
 		      ]},
      {packet_size_error, [], [packet_length_too_large,
 			      packet_length_too_short]},
-      
      {field_size_error, [], [service_name_length_too_large,
 			     service_name_length_too_short]},
-      
-     {kex, [], [no_common_alg_server_disconnects,
+     {kex, [], [custom_kexinit,
+                no_common_alg_server_disconnects,
 		no_common_alg_client_disconnects,
 		gex_client_init_option_groups,
 		gex_server_gex_limit,
@@ -142,15 +160,20 @@
 		gex_client_old_request_exact,
 		gex_client_old_request_noexact,
                 kex_strict_negotiated,
-                kex_strict_msg_ignore,
+                kex_strict_violation_key_exchange,
+                kex_strict_violation_new_keys,
+                kex_strict_violation,
+                kex_strict_violation_2,
                 kex_strict_msg_unknown]},
      {service_requests, [], [bad_service_name,
 			     bad_long_service_name,
 			     bad_very_long_service_name,
 			     empty_service_name,
-			     bad_service_name_then_correct
+			     bad_service_name_then_correct,
+                             extra_ssh_msg_service_request
 			    ]},
-     {authentication, [], [client_handles_keyboard_interactive_0_pwds
+     {authentication, [], [client_handles_keyboard_interactive_0_pwds,
+                           client_handles_banner_keyboard_interactive
 			  ]},
      {ext_info, [], [no_ext_info_s1,
                      no_ext_info_s2,
@@ -163,8 +186,8 @@
                                  modify_rm,
                                  modify_combo
                                 ]},
-     {client_close_early, [], [client_close_after_hello
-                               ]}
+     {client_close_early, [], [client_close_after_hello]},
+     {channel_close, [], [channel_close_timeout]}
     ].
 
 
@@ -174,7 +197,8 @@
 end_per_suite(Config) ->
     stop_apps(Config).
 
-init_per_testcase(no_common_alg_server_disconnects, Config) ->
+init_per_testcase(Tc, Config) when Tc == no_common_alg_server_disconnects;
+                                   Tc == custom_kexinit ->
     start_std_daemon(Config, [{preferred_algorithms,[{public_key,['ssh-rsa']},
                                                      {cipher,?DEFAULT_CIPHERS}
                                                     ]}]);
@@ -220,7 +244,8 @@
 init_per_testcase(_TestCase, Config) ->
     check_std_daemon_works(Config, ?LINE).
 
-end_per_testcase(no_common_alg_server_disconnects, Config) ->
+end_per_testcase(Tc, Config) when Tc == no_common_alg_server_disconnects;
+                                  Tc == custom_kexinit ->
     stop_std_daemon(Config);
 end_per_testcase(kex_strict_negotiated, Config) ->
     Config;
@@ -381,6 +406,90 @@
 	  ]
 	 ).
 
+early_rce(Config) ->
+    {ok,InitialState} =
+        ssh_trpt_test_lib:exec([{set_options, [print_ops, print_seqnums, print_messages]}]),
+    TypeOpen = "session",
+    ChannelId = 0,
+    WinSz = 425984,
+    PktSz = 65536,
+    DataOpen = <<>>,
+    SshMsgChannelOpen = ssh_connection:channel_open_msg(TypeOpen, ChannelId, WinSz, PktSz, DataOpen),
+
+    Id = 0,
+    TypeReq = "exec",
+    WantReply = true,
+    DataReq = <<?STRING(<<"lists:seq(1,10).">>)>>,
+    SshMsgChannelRequest =
+        ssh_connection:channel_request_msg(Id, TypeReq, WantReply, DataReq),
+    {ok, _AfterKexState} =
+        ssh_trpt_test_lib:exec(
+          [{connect,
+            server_host(Config),server_port(Config),
+            [{preferred_algorithms,[{kex,[?DEFAULT_KEX]},
+                                    {cipher,?DEFAULT_CIPHERS}
+                                   ]},
+             {silently_accept_hosts, true},
+             {recv_ext_info, false},
+             {user_dir, user_dir(Config)},
+             {user_interaction, false}
+            | proplists:get_value(extra_options,Config,[])]},
+           receive_hello,
+           {send, hello},
+           {send, ssh_msg_kexinit},
+           {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+           {send, SshMsgChannelOpen},
+           {send, SshMsgChannelRequest},
+           {match, disconnect(), receive_msg}
+          ], InitialState),
+    ok.
+
+custom_kexinit(Config) ->
+    %% 16#C0 value causes unicode:characters_to_list to return a big error value
+    Trash = lists:duplicate(260_000, 16#C0),
+    FunnyAlg = "curve25519-sha256",
+    KexInit =
+        #ssh_msg_kexinit{cookie = <<"Ã/Ï!9zñKá:ñÀv¿JÜ">>,
+                         kex_algorithms =
+                             [FunnyAlg ++ Trash],
+                         server_host_key_algorithms = ["ssh-rsa"],
+                         encryption_algorithms_client_to_server =
+                             ["aes256-ctr","aes192-ctr","aes128-ctr","aes128-cbc","3des-cbc"],
+                         encryption_algorithms_server_to_client =
+                             ["aes256-ctr","aes192-ctr","aes128-ctr","aes128-cbc","3des-cbc"],
+                         mac_algorithms_client_to_server =
+                             ["hmac-sha2-512-etm@openssh.com","hmac-sha2-256-etm@openssh.com",
+                              "hmac-sha2-512","hmac-sha2-256","hmac-sha1-etm@openssh.com","hmac-sha1"],
+                         mac_algorithms_server_to_client =
+                             ["hmac-sha2-512-etm@openssh.com","hmac-sha2-256-etm@openssh.com",
+                              "hmac-sha2-512","hmac-sha2-256","hmac-sha1-etm@openssh.com","hmac-sha1"],
+                         compression_algorithms_client_to_server = ["none","zlib@openssh.com","zlib"],
+                         compression_algorithms_server_to_client = ["none","zlib@openssh.com","zlib"],
+                         languages_client_to_server = [],
+                         languages_server_to_client = [],
+                         first_kex_packet_follows = false,
+                         reserved = 0
+                        },
+    {ok,_} =
+	ssh_trpt_test_lib:exec(
+	  [{set_options, [print_ops, {print_messages,detail}]},
+	   {connect,
+	    server_host(Config),server_port(Config),
+	    [{silently_accept_hosts, true},
+	     {user_dir, user_dir(Config)},
+	     {user_interaction, false},
+	     {preferred_algorithms,[{public_key,['ssh-rsa']},
+                                    {cipher,?DEFAULT_CIPHERS}
+                                   ]}
+	    ]},
+	   receive_hello,
+	   {send, hello},
+	   {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+	   {send, KexInit},  % with server unsupported 'ssh-dss' !
+	   {match, disconnect(), receive_msg}
+	  ]
+	 ).
+
 %%--------------------------------------------------------------------
 %%% Algo negotiation fail.  This should result in a ssh_msg_disconnect
 %%% being sent from the client.
@@ -689,7 +798,82 @@
                                                 ]}]
 			).
 
+%%%--------------------------------------------------------------------
+%%% SSH_MSG_USERAUTH_BANNER can be sent at any time during user auth.
+%%% The following test mimics a SSH server implementation that sends the banner
+%%% immediately before sending SSH_MSG_USERAUTH_SUCCESS.
+client_handles_banner_keyboard_interactive(Config) ->
+    {User,_Pwd} = server_user_password(Config),
 
+    %% Create a listening socket as server socket:
+    {ok,InitialState} = ssh_trpt_test_lib:exec(listen),
+    HostPort = ssh_trpt_test_lib:server_host_port(InitialState),
+
+    %% Start a process handling one connection on the server side:
+    spawn_link(
+      fun() ->
+	      {ok,_} =
+		  ssh_trpt_test_lib:exec(
+		    [{set_options, [print_ops, print_messages]},
+		     {accept, [{system_dir, system_dir(Config)},
+			       {user_dir, user_dir(Config)}]},
+		     receive_hello,
+		     {send, hello},
+
+		     {send, ssh_msg_kexinit},
+		     {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+
+		     {match, #ssh_msg_kexdh_init{_='_'}, receive_msg},
+		     {send, ssh_msg_kexdh_reply},
+
+		     {send, #ssh_msg_newkeys{}},
+		     {match,  #ssh_msg_newkeys{_='_'}, receive_msg},
+
+		     {match, #ssh_msg_service_request{name="ssh-userauth"}, receive_msg},
+		     {send, #ssh_msg_service_accept{name="ssh-userauth"}},
+
+		     {match, #ssh_msg_userauth_request{service="ssh-connection",
+						       method="none",
+						       user=User,
+						       _='_'}, receive_msg},
+		     {send, #ssh_msg_userauth_failure{authentications = "keyboard-interactive",
+						      partial_success = false}},
+
+		     {match, #ssh_msg_userauth_request{service="ssh-connection",
+						       method="keyboard-interactive",
+						       user=User,
+						       _='_'}, receive_msg},
+		     {send, #ssh_msg_userauth_info_request{name = "",
+							   instruction = "",
+							   language_tag = "",
+							   num_prompts = 1,
+							   data = <<0,0,0,10,80,97,115,115,119,111,114,100,58,32,0>>
+							  }},
+		     {match, #ssh_msg_userauth_info_response{num_responses = 1,
+							     _='_'}, receive_msg},
+		     {send, #ssh_msg_userauth_info_request{name = "",
+							   instruction = "",
+							   language_tag = "",
+							   num_prompts = 0,
+							   data = <<>>
+							  }},
+		     {match, #ssh_msg_userauth_info_response{num_responses = 0,
+							     data = <<>>,
+							     _='_'}, receive_msg},
+                     {send, #ssh_msg_userauth_banner{message = "Banner\n"}},
+		     {send, #ssh_msg_userauth_success{}},
+		     close_socket,
+		     print_state
+		    ],
+		    InitialState)
+      end),
+
+    %% and finally connect to it with a regular Erlang SSH client:
+    {ok,_} = std_connect(HostPort, Config,
+			 [{preferred_algorithms,[{kex,[?DEFAULT_KEX]},
+                                                 {cipher,?DEFAULT_CIPHERS}
+                                                ]}]
+			).
 
 %%%--------------------------------------------------------------------
 client_info_line(Config) ->
@@ -828,49 +1012,199 @@
 %%%--------------------------------------------------------------------
 %%%
 kex_strict_negotiated(Config0) ->
-    {ok,Pid} = ssh_test_lib:add_report_handler(),
+    {ok, TestRef} = ssh_test_lib:add_log_handler(),
     Config = start_std_daemon(Config0, []),
     {Server, Host, Port} = proplists:get_value(server, Config),
-    #{level := Level} = logger:get_primary_config(),
-    logger:set_primary_config(level, notice),
+    Level = ssh_test_lib:get_log_level(),
+    ssh_test_lib:set_log_level(debug),
     {ok, ConnRef} = std_connect({Host, Port}, Config, []),
-    {algorithms, A} = ssh:connection_info(ConnRef, algorithms),
+    {algorithms, _A} = ssh:connection_info(ConnRef, algorithms),
     ssh:stop_daemon(Server),
-    {ok, Reports} = ssh_test_lib:get_reports(Pid),
-    ct:log("Reports = ~p", [Reports]),
-    true = ssh_test_lib:kex_strict_negotiated(client, Reports),
-    true = ssh_test_lib:kex_strict_negotiated(server, Reports),
-    logger:set_primary_config(Level),
+    {ok, Events} = ssh_test_lib:get_log_events(TestRef),
+    true = ssh_test_lib:kex_strict_negotiated(client, Events),
+    true = ssh_test_lib:kex_strict_negotiated(server, Events),
+    ssh_test_lib:set_log_level(Level),
+    ssh_test_lib:rm_log_handler(),
     ok.
 
-%% Connect to an erlang server and inject unexpected SSH ignore
-kex_strict_msg_ignore(Config) ->
-    ct:log("START: ~p~n=================================", [?FUNCTION_NAME]),
-    ExpectedReason = "strict KEX violation: unexpected SSH_MSG_IGNORE",
-    TestMessages =
-        [{send, ssh_msg_ignore},
-         {match, #ssh_msg_kexdh_reply{_='_'}, receive_msg},
-         {match, disconnect(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED), receive_msg}],
-    kex_strict_helper(Config, TestMessages, ExpectedReason).
+%% Connect to an erlang server and inject unexpected SSH message
+%% ssh_fsm_kexinit in key_exchange state
+kex_strict_violation_key_exchange(Config) ->
+    ExpectedReason = "KEX strict violation",
+    Injections = [ssh_msg_ignore, ssh_msg_debug, ssh_msg_unimplemented],
+    TestProcedure =
+        fun(M) ->
+                ct:log(
+                  "=================== START: ~p Message: ~p Expected Fail =================================",
+                  [?FUNCTION_NAME, M]),
+                [receive_hello,
+                 {send, hello},
+                 {send, ssh_msg_kexinit},
+                 {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+                 {send, M},
+                 {match, disconnect(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED), receive_msg}]
+        end,
+    [kex_strict_helper(Config, TestProcedure(Msg), ExpectedReason) ||
+        Msg <- Injections],
+    ct:log("========== END ========"),
+    ok.
+
+%% Connect to an erlang server and inject unexpected SSH message
+%% ssh_fsm_kexinit in new_keys state
+kex_strict_violation_new_keys(Config) ->
+    ExpectedReason = "KEX strict violation",
+    Injections = [ssh_msg_ignore, ssh_msg_debug, ssh_msg_unimplemented],
+    TestProcedure =
+        fun(M) ->
+                ct:log(
+                  "=================== START: ~p Message: ~p Expected Fail =================================",
+                  [?FUNCTION_NAME, M]),
+                [receive_hello,
+                 {send, hello},
+                 {send, ssh_msg_kexinit},
+                 {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+                 {send, ssh_msg_kexdh_init},
+                 {send, M},
+                 {match, #ssh_msg_kexdh_reply{_='_'}, receive_msg},
+                 {match, disconnect(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED), receive_msg}]
+        end,
+    [kex_strict_helper(Config, TestProcedure(Msg), ExpectedReason) ||
+        Msg <- Injections],
+    ct:log("========== END ========"),
+    ok.
+
+%% Connect to an erlang server and inject unexpected SSH message
+%% duplicated KEXINIT
+kex_strict_violation(Config) ->
+    TestFlows =
+        [{kexinit, "KEX strict violation",
+          [receive_hello,
+           {send, hello},
+           {send, ssh_msg_kexinit},
+           {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+           {send, ssh_msg_kexinit},
+           {match, disconnect(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED), receive_msg}]},
+         {ssh_msg_kexdh_init, "KEX strict violation",
+          [receive_hello,
+           {send, hello},
+           {send, ssh_msg_kexinit},
+           {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+           {send, ssh_msg_kexdh_init_dup},
+           {match,# ssh_msg_kexdh_reply{_='_'}, receive_msg},
+           {match, disconnect(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED), receive_msg}]},
+         {new_keys, "Message ssh_msg_newkeys in wrong state",
+          [receive_hello,
+           {send, hello},
+           {send, ssh_msg_kexinit},
+           {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+           {send, ssh_msg_kexdh_init},
+           {match,# ssh_msg_kexdh_reply{_='_'}, receive_msg},
+           {send, #ssh_msg_newkeys{}},
+           {match, #ssh_msg_newkeys{_='_'}, receive_msg},
+           {send, #ssh_msg_newkeys{}},
+           {match, disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR), receive_msg}]},
+         {ssh_msg_unexpected_dh_gex, "KEX strict violation",
+          [receive_hello,
+           {send, hello},
+           {send, ssh_msg_kexinit},
+           {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+           %% dh_alg is expected but dh_gex_alg is provided
+	   {send, #ssh_msg_kex_dh_gex_request{min = 1000, n = 3000, max = 4000}},
+           {match, disconnect(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED), receive_msg}]},
+         {wrong_role, "KEX strict violation",
+          [receive_hello,
+           {send, hello},
+           {send, ssh_msg_kexinit},
+           {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+           %% client should not send message below
+           {send, ?HARDCODED_KEXDH_REPLY},
+           {match, disconnect(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED), receive_msg}]}],
+    TestProcedure =
+        fun({Msg, _, P}) ->
+                ct:log(
+                  "==== START: ~p (duplicated ~p) Expected Fail ====~n~p",
+                  [?FUNCTION_NAME, Msg, P]),
+                P
+        end,
+    [kex_strict_helper(Config, TestProcedure(Procedure), Reason) ||
+        Procedure = {_, Reason, _} <- TestFlows],
+    ct:log("==== END ====="),
+    ok.
+
+kex_strict_violation_2(Config) ->
+    ExpectedReason = "KEX strict violation",
+    {ok, TestRef} = ssh_test_lib:add_log_handler(),
+    Level = ssh_test_lib:get_log_level(),
+    ssh_test_lib:set_log_level(debug),
+    %% Connect and negotiate keys
+    {ok, InitialState} = ssh_trpt_test_lib:exec(
+                           [{set_options, [print_ops, print_seqnums, print_messages]}]),
+    {ok, UpToUnexpectedKexDHReply} =
+        ssh_trpt_test_lib:exec(
+          [{connect,
+            server_host(Config),server_port(Config),
+            [{preferred_algorithms,[{kex,[?DEFAULT_KEX]},
+                                    {cipher,?DEFAULT_CIPHERS}
+                                   ]},
+             {silently_accept_hosts, true},
+             {recv_ext_info, false},
+             {user_dir, user_dir(Config)},
+             {user_interaction, false}
+            | proplists:get_value(extra_options,Config,[])
+            ]}] ++
+              [receive_hello,
+               {send, hello},
+               {send, ssh_msg_kexinit},
+               {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+               {send, ssh_msg_kexdh_init},
+               {match, #ssh_msg_kexdh_reply{_='_'}, receive_msg},
+               %% client should not send message below
+               {send, ?HARDCODED_KEXDH_REPLY},
+               {match, {'or', [#ssh_msg_newkeys{_='_'},
+                               disconnect(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED)]},
+                receive_msg}],
+          InitialState),
+    case ssh_trpt_test_lib:return_value(UpToUnexpectedKexDHReply) of
+        {ssh_msg_newkeys} ->
+            ct:log("1st flow - extra match for disconnect needed"),
+            ssh_trpt_test_lib:exec(
+              [{match, disconnect(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED), receive_msg}],
+              UpToUnexpectedKexDHReply);
+        _ ->
+            ct:log("2nd flow disconnect already received")
+    end,
+    ct:sleep(100),
+    {ok, Events} = ssh_test_lib:get_log_events(TestRef),
+    ssh_test_lib:rm_log_handler(),
+    ct:log("Events = ~p", [Events]),
+    true = ssh_test_lib:kex_strict_negotiated(client, Events),
+    true = ssh_test_lib:kex_strict_negotiated(server, Events),
+    true = ssh_test_lib:event_logged(server, Events, ExpectedReason),
+    ssh_test_lib:set_log_level(Level),
+    ok.
 
 %% Connect to an erlang server and inject unexpected non-SSH binary
 kex_strict_msg_unknown(Config) ->
     ct:log("START: ~p~n=================================", [?FUNCTION_NAME]),
     ExpectedReason = "Bad packet: Size",
     TestMessages =
-        [{send, ssh_msg_unknown},
+        [receive_hello,
+         {send, hello},
+         {send, ssh_msg_kexinit},
+         {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+         {send, ssh_msg_kexdh_init},
+         {send, ssh_msg_unknown},
          {match, #ssh_msg_kexdh_reply{_='_'}, receive_msg},
          {match, disconnect(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED), receive_msg}],
     kex_strict_helper(Config, TestMessages, ExpectedReason).
 
 kex_strict_helper(Config, TestMessages, ExpectedReason) ->
-    {ok,HandlerPid} = ssh_test_lib:add_report_handler(),
-    #{level := Level} = logger:get_primary_config(),
-    logger:set_primary_config(level, notice),
+    {ok, TestRef} = ssh_test_lib:add_log_handler(),
+    Level = ssh_test_lib:get_log_level(),
+    ssh_test_lib:set_log_level(debug),
     %% Connect and negotiate keys
     {ok, InitialState} = ssh_trpt_test_lib:exec(
-			  [{set_options, [print_ops, print_seqnums, print_messages]}]
-			 ),
+                           [{set_options, [print_ops, print_seqnums, print_messages]}]),
     {ok, _AfterKexState} =
         ssh_trpt_test_lib:exec(
           [{connect,
@@ -883,21 +1217,17 @@
              {user_dir, user_dir(Config)},
              {user_interaction, false}
             | proplists:get_value(extra_options,Config,[])
-            ]},
-           receive_hello,
-           {send, hello},
-           {send, ssh_msg_kexinit},
-           {match, #ssh_msg_kexinit{_='_'}, receive_msg},
-           {send, ssh_msg_kexdh_init}] ++
+            ]}] ++
               TestMessages,
           InitialState),
     ct:sleep(100),
-    {ok, Reports} = ssh_test_lib:get_reports(HandlerPid),
-    ct:log("HandlerPid = ~p~nReports = ~p", [HandlerPid, Reports]),
-    true = ssh_test_lib:kex_strict_negotiated(client, Reports),
-    true = ssh_test_lib:kex_strict_negotiated(server, Reports),
-    true = ssh_test_lib:event_logged(server, Reports, ExpectedReason),
-    logger:set_primary_config(Level),
+    {ok, Events} = ssh_test_lib:get_log_events(TestRef),
+    ssh_test_lib:rm_log_handler(),
+    ct:log("Events = ~p", [Events]),
+    true = ssh_test_lib:kex_strict_negotiated(client, Events),
+    true = ssh_test_lib:kex_strict_negotiated(server, Events),
+    true = ssh_test_lib:event_logged(server, Events, ExpectedReason),
+    ssh_test_lib:set_log_level(Level),
     ok.
 
 %%%----------------------------------------------------------------
@@ -1054,6 +1384,44 @@
             {fail, no_handshakers}
     end.
 
+%%% Connect to an erlang server and pretend client sending extra
+%%% ssh_msg_service_request (Paramiko client behavior)
+extra_ssh_msg_service_request(Config) ->
+    %% Connect and negotiate keys
+    {ok,InitialState} = ssh_trpt_test_lib:exec(
+			  [{set_options, [print_ops, print_seqnums, print_messages]}]
+			 ),
+    {ok,AfterKexState} = connect_and_kex(Config, InitialState),
+    %% Do the authentcation
+    {User,Pwd} = server_user_password(Config),
+    UserAuthFlow =
+        fun(P) ->
+                [{send, #ssh_msg_service_request{name = "ssh-userauth"}},
+                 {match, #ssh_msg_service_accept{name = "ssh-userauth"}, receive_msg},
+                 {send, #ssh_msg_userauth_request{user = User,
+                                                  service = "ssh-connection",
+                                                  method = "password",
+                                                  data = <<?BOOLEAN(?FALSE),
+                                                           ?STRING(unicode:characters_to_binary(P))>>
+                                                 }}]
+        end,
+    {ok,EndState} =
+	ssh_trpt_test_lib:exec(
+          UserAuthFlow("WRONG") ++
+              [{match, #ssh_msg_userauth_failure{_='_'}, receive_msg}] ++
+              UserAuthFlow(Pwd) ++
+              [{match, #ssh_msg_userauth_success{_='_'}, receive_msg}],
+          AfterKexState),
+    %% Disconnect
+    {ok,_} =
+	ssh_trpt_test_lib:exec(
+	  [{send, #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
+				      description = "End of the fun",
+				      language = ""
+				     }},
+	   close_socket
+	  ], EndState),
+    ok.
 
 %%%================================================================
 %%%==== Internal functions ========================================
@@ -1220,6 +1588,84 @@
       ],
       InitialState).
 
+channel_close_timeout(Config) ->
+    {User,_Pwd} = server_user_password(Config),
+    %% Create a listening socket as server socket:
+    {ok,InitialState} = ssh_trpt_test_lib:exec(listen),
+    HostPort = ssh_trpt_test_lib:server_host_port(InitialState),
+    %% Start a process handling one connection on the server side:
+    spawn_link(
+      fun() ->
+	      {ok,_} =
+		  ssh_trpt_test_lib:exec(
+		    [{set_options, [print_ops, print_messages]},
+		     {accept, [{system_dir, system_dir(Config)},
+			       {user_dir, user_dir(Config)},
+                               {idle_time, 50000}]},
+		     receive_hello,
+		     {send, hello},
+		     {send, ssh_msg_kexinit},
+		     {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+		     {match, #ssh_msg_kexdh_init{_='_'}, receive_msg},
+		     {send, ssh_msg_kexdh_reply},
+		     {send, #ssh_msg_newkeys{}},
+		     {match,  #ssh_msg_newkeys{_='_'}, receive_msg},
+		     {match, #ssh_msg_service_request{name="ssh-userauth"}, receive_msg},
+		     {send, #ssh_msg_service_accept{name="ssh-userauth"}},
+		     {match, #ssh_msg_userauth_request{service="ssh-connection",
+						       method="none",
+						       user=User,
+						       _='_'}, receive_msg},
+		     {send, #ssh_msg_userauth_failure{authentications = "password",
+						      partial_success = false}},
+		     {match, #ssh_msg_userauth_request{service="ssh-connection",
+						       method="password",
+						       user=User,
+						       _='_'}, receive_msg},
+		     {send, #ssh_msg_userauth_success{}},
+                     {match, #ssh_msg_channel_open{channel_type="session",
+                                                   sender_channel=0,
+                                                   _='_'}, receive_msg},
+		     {send, #ssh_msg_channel_open_confirmation{recipient_channel= 0,
+                                                               sender_channel = 0,
+                                                               initial_window_size = 64*1024,
+                                                               maximum_packet_size = 32*1024
+                                                               }},
+                     {match, #ssh_msg_channel_open{channel_type="session",
+                                                   sender_channel=1,
+                                                   _='_'}, receive_msg},
+		     {send, #ssh_msg_channel_open_confirmation{recipient_channel= 1,
+                                                               sender_channel = 1,
+                                                               initial_window_size = 64*1024,
+                                                               maximum_packet_size = 32*1024}},
+                     {match, #ssh_msg_channel_close{recipient_channel = 0}, receive_msg},
+                     {match, disconnect(), receive_msg},
+		     print_state],
+		    InitialState)
+      end),
+    %% connect to it with a regular Erlang SSH client:
+    ChannelCloseTimeout = 3000,
+    {ok, ConnRef} = std_connect(HostPort, Config,
+				[{preferred_algorithms,[{kex,[?DEFAULT_KEX]},
+                                                        {cipher,?DEFAULT_CIPHERS}
+                                                       ]},
+                                 {channel_close_timeout, ChannelCloseTimeout},
+                                 {idle_time, 50000}
+                                ]
+			       ),
+    {ok,  Channel0} = ssh_connection:session_channel(ConnRef, 50000),
+    {ok, _Channel1} = ssh_connection:session_channel(ConnRef, 50000),
+    %% Close the channel from client side, the server does not reply with 'channel-close'
+    %% After the timeout, the client should drop the cache entry
+    _ = ssh_connection:close(ConnRef, Channel0),
+    receive
+    after ChannelCloseTimeout + 1000 ->
+        {channels, Channels} = ssh:connection_info(ConnRef, channels),
+        ct:log("Channel entries ~p", [Channels]),
+        %% Only one channel entry should be present, the other one should be dropped
+        1 = length(Channels),
+        ssh:close(ConnRef)
+    end.
 %%%----------------------------------------------------------------
 
 %%% For matching peer disconnection
@@ -1249,18 +1695,23 @@
                        Port, {AccP,AccC,AccH}) ->
     ParentHandshakers =
         [{PidW,PidH} ||
-            {{ssh_acceptor_sup,{address,_,Port1,_}}, PidW, worker, [ssh_acceptor]} <-
-                supervisor:which_children(PidS),
+            {{ssh_acceptor_sup,{address,_,Port1,_}}, PidW, worker,
+             [ssh_acceptor]} <- supervisor:which_children(PidS),
             Port1 == Port,
             PidH <- element(2, process_info(PidW,links)),
             is_pid(PidH),
-            process_info(PidH,current_function) == {current_function,{ssh_connection_handler,handshake,3}}],
+            process_info(PidH,current_function) ==
+                {current_function,
+                 {ssh_connection_handler,handshake,4}}],
     {Parents,Handshakers} = lists:unzip(ParentHandshakers),
     find_handshake_parent(T, Port, {AccP++Parents, AccC, AccH++Handshakers});
 
-find_handshake_parent([{_Ref,PidS,supervisor,[ssh_subsystem_sup]}|T], Port, {AccP,AccC,AccH}) ->
+find_handshake_parent([{_Ref,PidS,supervisor,[ssh_connection_sup]}|T],
+                      Port, {AccP,AccC,AccH}) ->
     Connections =
-        [Pid || {connection,Pid,worker,[ssh_connection_handler]} <- supervisor:which_children(PidS)],
+        [Pid ||
+            {connection,Pid,worker,[ssh_connection_handler]} <-
+                supervisor:which_children(PidS)],
     find_handshake_parent(T, Port, {AccP, AccC++Connections, AccH});
 
 find_handshake_parent([_|T], Port, Acc) ->
diff -ruN a/lib/ssh/test/ssh_sftpd_SUITE.erl b/lib/ssh/test/ssh_sftpd_SUITE.erl
--- a/lib/ssh/test/ssh_sftpd_SUITE.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/test/ssh_sftpd_SUITE.erl	2025-12-22 23:16:56.270839035 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2006-2022. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -43,6 +43,7 @@
          open_file_dir_v6/1,
          read_dir/1,
          read_file/1,
+         max_path/1,
          real_path/1,
          relative_path/1,
          relpath/1,
@@ -51,7 +52,6 @@
          retrieve_attributes/1,
          root_with_cwd/1,
          set_attributes/1,
-         sshd_read_file/1,
          ver3_open_flags/1,
          ver3_rename/1,
          ver6_basic/1,
@@ -60,6 +60,7 @@
 
 -include_lib("common_test/include/ct.hrl").
 -include_lib("kernel/include/file.hrl").
+-include_lib("stdlib/include/assert.hrl").
 -include("ssh_xfer.hrl").
 -include("ssh.hrl").
 -include("ssh_test_lib.hrl").
@@ -71,9 +72,9 @@
 -define(SSH_TIMEOUT, 10000).
 -define(REG_ATTERS, <<0,0,0,0,1>>).
 -define(UNIX_EPOCH,  62167219200).
-
--define(is_set(F, Bits),
-	((F) band (Bits)) == (F)).
+-define(MAX_HANDLES, 10).
+-define(MAX_PATH, 200).
+-define(is_set(F, Bits), ((F) band (Bits)) == (F)).
 
 %%--------------------------------------------------------------------
 %% Common Test interface functions -----------------------------------
@@ -86,6 +87,7 @@
     [open_close_file, 
      open_close_dir, 
      read_file, 
+     max_path,
      read_dir,
      write_file, 
      rename_file, 
@@ -97,8 +99,7 @@
      links,
      ver3_rename,
      ver3_open_flags,
-     relpath, 
-     sshd_read_file,
+     relpath,
      ver6_basic,
      access_outside_root,
      root_with_cwd,
@@ -180,7 +181,9 @@
 								  {sftpd_vsn, 6}])],
 			  ssh:daemon(0, [{subsystems, SubSystems}|Options]);
 		      _ ->
-			  SubSystems = [ssh_sftpd:subsystem_spec([])],
+			  SubSystems = [ssh_sftpd:subsystem_spec(
+                                          [{max_handles, ?MAX_HANDLES},
+					  {max_path, ?MAX_PATH}])],
 			  ssh:daemon(0, [{subsystems, SubSystems}|Options])
 		  end,
 
@@ -316,33 +319,62 @@
 read_file(Config) when is_list(Config) ->
     PrivDir =  proplists:get_value(priv_dir, Config),
     FileName = filename:join(PrivDir, "test.txt"),
+         {Cm, Channel} = proplists:get_value(sftp, Config),
+    [begin
+         R1 = req_id(),
+         {ok, <<?SSH_FXP_HANDLE, ?UINT32(R1), Handle/binary>>, _} =
+             open_file(FileName, Cm, Channel, R1, ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES,
+                       ?SSH_FXF_OPEN_EXISTING),
+         R2 = req_id(),
+         {ok, <<?SSH_FXP_DATA, ?UINT32(R2), ?UINT32(_Length), Data/binary>>, _} =
+             read_file(Handle, 100, 0, Cm, Channel, R2),
+         {ok, Data} = file:read_file(FileName)
+     end || _I <- lists:seq(0, ?MAX_HANDLES-1)],
+    ReqId = req_id(),
+    {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(?SSH_FX_FAILURE),
+           ?UINT32(MsgLen), Msg:MsgLen/binary,
+           ?UINT32(LangTagLen), _LangTag:LangTagLen/binary>>, _} =
+        open_file(FileName, Cm, Channel, ReqId, ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES,
+                  ?SSH_FXF_OPEN_EXISTING),
+    ct:log("Message: ~s", [Msg]),
+    ok.
 
-    ReqId = 0,
+%%--------------------------------------------------------------------
+max_path(Config) when is_list(Config) ->
+    PrivDir =  proplists:get_value(priv_dir, Config),
+    FileName = filename:join(PrivDir, "test.txt"),
     {Cm, Channel} = proplists:get_value(sftp, Config),
-
-    {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} =
-	open_file(FileName, Cm, Channel, ReqId,
+    %% verify max_path limit
+    LongFileName =
+        filename:join(PrivDir,
+                      "t" ++ lists:flatten(lists:duplicate(?MAX_PATH, "e")) ++ "st.txt"),
+    {ok, _} = file:copy(FileName, LongFileName),
+    ReqId1 = req_id(),
+    {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId1), ?UINT32(?SSH_FX_NO_SUCH_PATH),
+	  _/binary>>, _} =
+	open_file(LongFileName, Cm, Channel, ReqId1,
 		  ?ACE4_READ_DATA  bor ?ACE4_READ_ATTRIBUTES,
-		  ?SSH_FXF_OPEN_EXISTING),
-
-    NewReqId = 1,
-
-    {ok, <<?SSH_FXP_DATA, ?UINT32(NewReqId), ?UINT32(_Length),
-	  Data/binary>>, _} =
-	read_file(Handle, 100, 0, Cm, Channel, NewReqId),
-
-    {ok, Data} = file:read_file(FileName).
+		  ?SSH_FXF_OPEN_EXISTING).
 
 %%--------------------------------------------------------------------
 read_dir(Config) when is_list(Config) ->
     PrivDir = proplists:get_value(priv_dir, Config),
     {Cm, Channel} = proplists:get_value(sftp, Config),
-    ReqId = 0,
-    {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} =
-	open_dir(PrivDir, Cm, Channel, ReqId),
-    ok = read_dir(Handle, Cm, Channel, ReqId).
+    [begin
+         R1 = req_id(),
+         {ok, <<?SSH_FXP_HANDLE, ?UINT32(R1), Handle/binary>>, _} =
+             open_dir(PrivDir, Cm, Channel, R1),
+         R2 = req_id(),
+         ok = read_dir(Handle, Cm, Channel, R2)
+     end || _I <- lists:seq(0, ?MAX_HANDLES-1)],
+    ReqId = req_id(),
+    {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(?SSH_FX_FAILURE),
+           ?UINT32(MsgLen), Msg:MsgLen/binary,
+           ?UINT32(LangTagLen), _LangTag:LangTagLen/binary>>, _} =
+        open_dir(PrivDir, Cm, Channel, ReqId),
+    ct:log("Message: ~s", [Msg]),
+    ok.
 
-%%--------------------------------------------------------------------
 write_file(Config) when is_list(Config) ->
     PrivDir =  proplists:get_value(priv_dir, Config),
     FileName = filename:join(PrivDir, "test.txt"),
@@ -388,35 +420,33 @@
     PrivDir =  proplists:get_value(priv_dir, Config),
     FileName = filename:join(PrivDir, "test.txt"),
     NewFileName = filename:join(PrivDir, "test1.txt"),
-    ReqId = 0,
+    LongFileName =
+        filename:join(PrivDir,
+                      "t" ++ lists:flatten(lists:duplicate(?MAX_PATH, "e")) ++ "st.txt"),
     {Cm, Channel} = proplists:get_value(sftp, Config),
-
-    {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId),
-	  ?UINT32(?SSH_FX_OK), _/binary>>, _} =
-	rename(FileName, NewFileName, Cm, Channel, ReqId, 6, 0),
-
-    NewReqId = ReqId + 1,
-
-    {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId),
-	  ?UINT32(?SSH_FX_OK), _/binary>>, _} =
-	rename(NewFileName, FileName, Cm, Channel, NewReqId, 6,
-	       ?SSH_FXP_RENAME_OVERWRITE),
-
-    NewReqId1 = NewReqId + 1,
-    file:copy(FileName, NewFileName),
-
-    %% No overwrite
-    {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId1),
-	  ?UINT32(?SSH_FX_FILE_ALREADY_EXISTS), _/binary>>, _} =
-	rename(FileName, NewFileName, Cm, Channel, NewReqId1, 6,
-	       ?SSH_FXP_RENAME_NATIVE),
-
-    NewReqId2 = NewReqId1 + 1,
-
-    {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId2),
-	  ?UINT32(?SSH_FX_OP_UNSUPPORTED), _/binary>>, _} =
-	rename(FileName, NewFileName, Cm, Channel, NewReqId2, 6,
-	       ?SSH_FXP_RENAME_ATOMIC).
+    Version = 6,
+    [begin
+         case Action of
+             {Code, AFile, BFile, Flags} ->
+                 ReqId = req_id(),
+                 ct:log("ReqId = ~p,~nCode = ~p,~nAFile = ~p,~nBFile = ~p,~nFlags = ~p",
+                        [ReqId, Code, AFile, BFile, Flags]),
+                 {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(Code), _/binary>>, _} =
+                     rename(AFile, BFile, Cm, Channel, ReqId, Version, Flags);
+             {file_copy, AFile, BFile} ->
+                 {ok, _} = file:copy(AFile, BFile)
+         end
+     end ||
+        Action <-
+            [{?SSH_FX_OK, FileName, NewFileName, 0},
+             {?SSH_FX_OK, NewFileName, FileName, ?SSH_FXP_RENAME_OVERWRITE},
+             {file_copy, FileName, NewFileName},
+             %% no overwrite
+             {?SSH_FX_FILE_ALREADY_EXISTS, FileName, NewFileName, ?SSH_FXP_RENAME_NATIVE},
+             {?SSH_FX_OP_UNSUPPORTED, FileName, NewFileName, ?SSH_FXP_RENAME_ATOMIC},
+             %% max_path
+             {?SSH_FX_NO_SUCH_PATH, FileName, LongFileName, 0}]],
+    ok.
 
 %%--------------------------------------------------------------------
 mk_rm_dir(Config) when is_list(Config) ->
@@ -644,27 +674,6 @@
 	    Root = Path
     end.
 
-%%--------------------------------------------------------------------
-sshd_read_file(Config) when is_list(Config) ->
-    PrivDir =  proplists:get_value(priv_dir, Config),
-    FileName = filename:join(PrivDir, "test.txt"),
-
-    ReqId = 0,
-    {Cm, Channel} = proplists:get_value(sftp, Config),
-
-    {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} =
-	open_file(FileName, Cm, Channel, ReqId,
-		  ?ACE4_READ_DATA  bor ?ACE4_READ_ATTRIBUTES,
-		  ?SSH_FXF_OPEN_EXISTING),
-
-    NewReqId = 1,
-
-    {ok, <<?SSH_FXP_DATA, ?UINT32(NewReqId), ?UINT32(_Length),
-	  Data/binary>>, _} =
-	read_file(Handle, 100, 0, Cm, Channel, NewReqId),
-
-    {ok, Data} = file:read_file(FileName).
-%%--------------------------------------------------------------------
 ver6_basic(Config) when is_list(Config) ->
     PrivDir =  proplists:get_value(priv_dir, Config),
     %FileName = filename:join(PrivDir, "test.txt"),
@@ -728,25 +737,33 @@
     FileName = "root_with_cwd.txt",
     FilePath = filename:join(CWD, FileName),
     ok = filelib:ensure_dir(FilePath),
-    ok = file:write_file(FilePath ++ "0", <<>>),
-    ok = file:write_file(FilePath ++ "1", <<>>),
-    ok = file:write_file(FilePath ++ "2", <<>>),
     {Cm, Channel} = proplists:get_value(sftp, Config),
-    ReqId0 = 0,
-    {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId0), _Handle0/binary>>, _} =
-	open_file(FileName ++ "0", Cm, Channel, ReqId0,
-		  ?ACE4_READ_DATA  bor ?ACE4_READ_ATTRIBUTES,
-		  ?SSH_FXF_OPEN_EXISTING),
-    ReqId1 = 1,
-    {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId1), _Handle1/binary>>, _} =
-	open_file("./" ++ FileName ++ "1", Cm, Channel, ReqId1,
-		  ?ACE4_READ_DATA  bor ?ACE4_READ_ATTRIBUTES,
-		  ?SSH_FXF_OPEN_EXISTING),
-    ReqId2 = 2,
-    {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId2), _Handle2/binary>>, _} =
-	open_file("/home/" ++ FileName ++ "2", Cm, Channel, ReqId2,
-		  ?ACE4_READ_DATA  bor ?ACE4_READ_ATTRIBUTES,
-		  ?SSH_FXF_OPEN_EXISTING).
+
+    %% repeat procedure to make sure uniq file handles are generated
+    FileHandles =
+        [begin
+             ReqIdStr = integer_to_list(ReqId),
+             ok = file:write_file(FilePath ++ ReqIdStr, <<>>),
+             {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} =
+                 open_file(FileName ++ ReqIdStr, Cm, Channel, ReqId,
+                           ?ACE4_READ_DATA  bor ?ACE4_READ_ATTRIBUTES,
+                           ?SSH_FXF_OPEN_EXISTING),
+             Handle
+         end ||
+            ReqId <- lists:seq(0,2)],
+    ?assertEqual(length(FileHandles),
+                 length(lists:uniq(FileHandles))),
+    %% create a gap in file handles
+    [_, MiddleHandle, _] = FileHandles,
+    close(MiddleHandle, 3, Cm, Channel),
+
+    %% check that gap in file handles is is re-used
+    GapReqId = 4,
+    {ok, <<?SSH_FXP_HANDLE, ?UINT32(GapReqId), MiddleHandle/binary>>, _} =
+        open_file(FileName ++ integer_to_list(1), Cm, Channel, GapReqId,
+                  ?ACE4_READ_DATA  bor ?ACE4_READ_ATTRIBUTES,
+                  ?SSH_FXF_OPEN_EXISTING),
+    ok.
 
 %%--------------------------------------------------------------------
 relative_path(Config) when is_list(Config) ->
@@ -1078,3 +1095,12 @@
 
 not_default_permissions() ->
     8#600. %% User read-write-only
+
+req_id() ->
+    ReqId =
+        case get(req_id) of
+            undefined -> 0;
+            I -> I
+        end,
+    put(req_id, ReqId + 1),
+    ReqId.
diff -ruN a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl
--- a/lib/ssh/test/ssh_sftp_SUITE.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/test/ssh_sftp_SUITE.erl	2025-12-22 23:16:56.270839035 +1030
@@ -1,7 +1,7 @@
 %
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2005-2021. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -55,6 +55,7 @@
          pos_read/1,
          pos_write/1,
          position/1,
+         read_6GB/1,
          read_crypto_tar/1,
          read_dir/1,
          read_file/1,
@@ -81,7 +82,9 @@
 -include_lib("common_test/include/ct.hrl").
 -include_lib("kernel/include/file.hrl").
 -include("ssh_test_lib.hrl").
-						% Default timetrap timeout
+-include_lib("stdlib/include/assert.hrl").
+
+%% Default timetrap timeout
 -define(default_timeout, test_server:minutes(1)).
 
 %%--------------------------------------------------------------------
@@ -119,6 +122,7 @@
 
      {unicode, [], [{group,erlang_server},
 		    {group,openssh_server},
+                    read_6GB,
 		    sftp_nonexistent_subsystem
                    ]},
 
@@ -228,24 +232,7 @@
     [{peer, {fmt_host(HostX),PortX}}, {group, erlang_server}, {sftpd, Sftpd} | Config];
 
 init_per_group(openssh_server, Config) ->
-    ct:comment("Begin ~p",[grps(Config)]),
-    Host = ssh_test_lib:hostname(),
-    case (catch ssh_sftp:start_channel(Host,
-				       [{user_interaction, false},
-					{silently_accept_hosts, true},
-                                        {save_accepted_host, false}
-                                       ])) of
-	{ok, _ChannelPid, Connection} ->
-	    [{peer, {_HostName,{IPx,Portx}}}] = ssh:connection_info(Connection,[peer]),
-	    ssh:close(Connection),
-	    [{w2l, fun w2l/1},
-             {peer, {fmt_host(IPx),Portx}}, {group, openssh_server} | Config];
-	{error,"Key exchange failed"} ->
-	    {skip, "openssh server doesn't support the tested kex algorithm"};
-	Other ->
-            ct:log("No openssh server. Cause:~n~p~n",[Other]),
-	    {skip, "No openssh daemon (see log in testcase)"} 
-    end;
+    verify_openssh(Config);
 
 init_per_group(remote_tar, Config) ->
     ct:comment("Begin ~p",[grps(Config)]),
@@ -287,7 +274,18 @@
     Config.
 
 %%--------------------------------------------------------------------
-
+init_per_testcase(read_6GB, Config) ->
+    case verify_openssh(Config) of
+        Result = {skip, _} ->
+            Result;
+        _ ->
+            case os:type() of
+                {win32, _} ->
+                    {skip, "/dev/zero not available on Windws"};
+                _ ->
+                    init_per_testcase(read_6GB_prepare_openssh_server, Config)
+            end
+    end;
 init_per_testcase(sftp_nonexistent_subsystem, Config) ->
     PrivDir = proplists:get_value(priv_dir, Config),
     SysDir =  proplists:get_value(data_dir, Config),
@@ -300,7 +298,6 @@
 				  [{User, Passwd}]}
 				]),
     [{sftpd, Sftpd} | Config];
-
 init_per_testcase(version_option, Config0) ->
     Config = prepare(Config0),
     TmpConfig0 = lists:keydelete(watchdog, 1, Config),
@@ -320,7 +317,6 @@
                                ]),
     Sftp = {ChannelPid, Connection},
     [{sftp,Sftp}, {watchdog, Dog} | TmpConfig];
-
 init_per_testcase(Case, Config00) ->
     Config0 = prepare(Config00),
     Config1 = lists:keydelete(watchdog, 1, Config0),
@@ -332,11 +328,24 @@
 		   undefined -> [];
 		   Sz -> [{packet_size,Sz}]
 	       end,
+    PrepareOpenSSHServer =
+        fun() ->
+                Host = ssh_test_lib:hostname(),
+        	{ok, ChannelPid, Connection} =
+        	    ssh_sftp:start_channel(Host,
+        				   [{user_interaction, false},
+        				    {silently_accept_hosts, true},
+                                            {save_accepted_host, false}
+                                           | PktSzOpt
+        				   ]),
+        	Sftp = {ChannelPid, Connection},
+        	[{sftp, Sftp}, {watchdog, Dog} | Config2]
+        end,
     Config =
 	case proplists:get_value(group,Config2) of
 	    erlang_server ->
-		{_,Host, Port} =  proplists:get_value(sftpd, Config2),
-		{ok, ChannelPid, Connection}  = 
+		{_,Host, Port} = proplists:get_value(sftpd, Config2),
+		{ok, ChannelPid, Connection} =
 		    ssh_sftp:start_channel(Host, Port,
 					   [{user, User},
 					    {password, Passwd},
@@ -351,18 +360,10 @@
 	    openssh_server when Case == links ->
 		{skip, "known bug in openssh"};
 	    openssh_server ->
-		Host = ssh_test_lib:hostname(),
-		{ok, ChannelPid, Connection} = 
-		    ssh_sftp:start_channel(Host, 
-					   [{user_interaction, false},
-					    {silently_accept_hosts, true},
-                                            {save_accepted_host, false}
-					    | PktSzOpt
-					   ]),
-		Sftp = {ChannelPid, Connection},
-		[{sftp, Sftp}, {watchdog, Dog} | Config2]
+                PrepareOpenSSHServer();
+            _ when Case == read_6GB_prepare_openssh_server ->
+                PrepareOpenSSHServer()
 	end,
-
     case catch proplists:get_value(remote_tar,Config) of
 	%% The 'catch' is for the case of Config={skip,...}
 	true ->
@@ -563,26 +564,62 @@
 retrieve_attributes(Config) when is_list(Config) ->
     FileName = proplists:get_value(filename, Config),
     SftpFileName = w2l(Config, FileName),
-
     {Sftp, _} = proplists:get_value(sftp, Config),
     {ok, FileInfo} = ssh_sftp:read_file_info(Sftp, SftpFileName),
     {ok, NewFileInfo} = file:read_file_info(FileName),
-
-    %% TODO comparison. There are some differences now is that ok?
-    ct:log("SFTP: ~p   FILE: ~p~n", [FileInfo, NewFileInfo]).
+    ct:log("ssh_sftp:read_file_info(~p): ~p~n"
+           "file:read_file_info(~p): ~p",
+           [SftpFileName, FileInfo, FileName, NewFileInfo]),
+    {ExpectedUid, ExpectedGid} =
+        case {os:type(), proplists:get_value(group,Config)} of
+            {{win32, _}, openssh_server} ->
+                %% Windows compiled Erlang is expected will return 0;
+                %% but when Erlang(Windows) client interacts with
+                %% OpenSSH server - value 1000 is received by client
+                %% over SFTP (because OpenSSH is compiled for Linux
+                %% and runs on WSL)
+                {1000, 1000};
+            _ ->
+                {FileInfo#file_info.uid, FileInfo#file_info.gid}
+        end,
+    ?assertEqual(ExpectedUid, NewFileInfo#file_info.uid),
+    ?assertEqual(ExpectedGid, NewFileInfo#file_info.gid),
+    ok.
 
 %%--------------------------------------------------------------------
 set_attributes(Config) when is_list(Config) ->
     FileName = proplists:get_value(testfile, Config),
     SftpFileName = w2l(Config, FileName),
-
     {Sftp, _} = proplists:get_value(sftp, Config),
     {ok,Fd} = file:open(FileName, write),
     io:put_chars(Fd,"foo"),
-    ok = ssh_sftp:write_file_info(Sftp, SftpFileName, #file_info{mode=8#400}),
-    {error, eacces} = file:write_file(FileName, "hello again"),
-    ok = ssh_sftp:write_file_info(Sftp, SftpFileName, #file_info{mode=8#600}),
-    ok = file:write_file(FileName, "hello again").
+    TestWriting =
+        fun(FInfo) ->
+                ok = ssh_sftp:write_file_info(Sftp, SftpFileName,
+                                              FInfo#file_info{mode=8#400}),
+                {error, eacces} = file:write_file(FileName, "hello again"),
+                ok = ssh_sftp:write_file_info(Sftp, SftpFileName,
+                                              FInfo#file_info{mode=8#600}),
+                ok = file:write_file(FileName, "hello again")
+        end,
+    TestWriting(#file_info{}),
+    IsErlangServer =
+        fun() ->
+                TcGroupPath = proplists:get_value(tc_group_path, Config),
+                {_, Path} = lists:unzip(lists:flatten(TcGroupPath)),
+                lists:member(erlang_server, Path)
+        end,
+    case IsErlangServer() of
+        true ->
+            ct:log("Testing with writing a complete #file_info record"),
+            {ok, FileInfo} = file:read_file_info(SftpFileName),
+            TestWriting(FileInfo);
+        _ ->
+            %% with OpenSSH daemon started by other user above instruction end
+            %% up with permission denied
+            ok
+    end,
+    ok.
 
 %%--------------------------------------------------------------------
 file_owner_access(Config) when is_list(Config) ->
@@ -676,6 +713,29 @@
     {ok, 1} = ssh_sftp:position(Sftp, Handle, cur),
     {ok, "2"} = ssh_sftp:read(Sftp, Handle, 1).
 
+read_6GB(Config) when is_list(Config) ->
+    ct:timetrap(16*?default_timeout),
+    FileName = "/dev/zero",
+    SftpFileName = w2l(Config, FileName),
+    {SftpChannel, _ConnectionRef} = proplists:get_value(sftp, Config),
+    ChunkSize = 65535,
+    N = 100000,
+    {ok, Handle} = ssh_sftp:open(SftpChannel, SftpFileName, [read]),
+    ExpectedList = lists:duplicate(ChunkSize, 0),
+    [begin
+         MBTransferred = io_lib:format("~.2f", [I * ChunkSize / 1048576.0]),
+         case ssh_sftp:read(SftpChannel, Handle, ChunkSize, timer:minutes(1)) of
+             {ok, ExpectedList} ->
+                 [ct:log("~n~s MB read~n", [MBTransferred]) || I rem 10000 == 0];
+             Result ->
+                 ct:log("## After reading ~s MB~n## Unexpected result received = ~p",
+                        [MBTransferred, Result]),
+                 ct:fail(unexpected_reason)
+         end
+     end ||
+        I <- lists:seq(0, N)],
+    ok.
+
 %%--------------------------------------------------------------------
 pos_read(Config) when is_list(Config) ->
     FileName = proplists:get_value(testfile, Config),
@@ -1234,4 +1294,22 @@
     W2L = proplists:get_value(w2l, Config, fun(X) -> X end),
     W2L(P).
 
-    
+verify_openssh(Config) ->
+    ct:comment("Begin ~p",[grps(Config)]),
+    Host = ssh_test_lib:hostname(),
+    case (catch ssh_sftp:start_channel(Host,
+				       [{user_interaction, false},
+					{silently_accept_hosts, true},
+                                        {save_accepted_host, false}
+                                       ])) of
+	{ok, _ChannelPid, Connection} ->
+	    [{peer, {_HostName,{IPx,Portx}}}] = ssh:connection_info(Connection,[peer]),
+	    ssh:close(Connection),
+	    [{w2l, fun w2l/1},
+             {peer, {fmt_host(IPx),Portx}}, {group, openssh_server} | Config];
+	{error,"Key exchange failed"} ->
+	    {skip, "openssh server doesn't support the tested kex algorithm"};
+	Other ->
+            ct:log("No openssh server. Cause:~n~p~n",[Other]),
+	    {skip, "No openssh daemon (see log in testcase)"}
+    end.
diff -ruN a/lib/ssh/test/ssh_sup_SUITE.erl b/lib/ssh/test/ssh_sup_SUITE.erl
--- a/lib/ssh/test/ssh_sup_SUITE.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/test/ssh_sup_SUITE.erl	2025-12-22 23:16:56.270839035 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2015-2023. All Rights Reserved.
+%% Copyright Ericsson AB 2015-2024. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -50,7 +50,7 @@
 -define(SSHD_SUP(Pid), {sshd_sup, Pid, supervisor, [supervisor]}).
 -define(SYSTEM_SUP(Pid,Address),
         {{ssh_system_sup, Address}, Pid, supervisor,[ssh_system_sup]}).
--define(SUB_SYSTEM_SUP(Pid), {_,Pid, supervisor,[ssh_subsystem_sup]}).
+-define(CONNECTION_SUP(Pid), {_,Pid, supervisor,[ssh_connection_sup]}).
 -define(ACCEPTOR_SUP(Pid,Address),
         {{ssh_acceptor_sup,Address},Pid,supervisor,[ssh_acceptor_sup]}).
 -define(ACCEPTOR_WORKER(Pid,Address),
@@ -129,25 +129,22 @@
                                              {user, ?USER},
                                              {password, ?PASSWD},
                                              {user_dir, UserDir}]),
-    ?wait_match([?SYSTEM_SUP(SysSup,
-                             #address{address=LocalIP,
-                                      port=LocalPort,
-                                      profile=?DEFAULT_PROFILE})],
+    ?wait_match([?CONNECTION_SUP(ConnectionSup)],
 		supervisor:which_children(sshc_sup),
-                [SysSup, LocalIP, LocalPort]),
-    check_sshc_system_tree(SysSup, Pid1, LocalIP, LocalPort, Config),
+                [ConnectionSup]),
+    check_sshc_system_tree(ConnectionSup, Pid1, Config),
     Pid2 = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
                                              {save_accepted_host, false},
                                              {user_interaction, false},
                                              {user, ?USER},
                                              {password, ?PASSWD},
                                              {user_dir, UserDir}]),
-    ?wait_match([?SYSTEM_SUP(_,_),
-                 ?SYSTEM_SUP(_,_)
+    ?wait_match([?CONNECTION_SUP(_),
+                 ?CONNECTION_SUP(_)
                 ],
 		supervisor:which_children(sshc_sup)),
     ssh:close(Pid1),
-    ?wait_match([?SYSTEM_SUP(_,_)
+    ?wait_match([?CONNECTION_SUP(_)
                 ],
 		supervisor:which_children(sshc_sup)),
     ssh:close(Pid2),
@@ -301,7 +298,7 @@
 						      {user_interaction, true},
 						      {user_dir, UserDir}]),
 
-    [SubSysSup,_ChPid|_] = Sups0 = chk_empty_con_daemon(Daemon),
+    [ConnectionSup,_ChPid|_] = Sups0 = chk_empty_con_daemon(Daemon),
 
     {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
     ok = ssh_connection:shell(ConnectionRef,ChannelId0),
@@ -311,7 +308,7 @@
                  {_,ChSup,supervisor,[ssh_channel_sup]},
                  {connection,_,worker,[ssh_connection_handler]}
                 ],
-		supervisor:which_children(SubSysSup),
+		supervisor:which_children(ConnectionSup),
                 [ChSup]),
     ?wait_match([{_,GroupPid,worker,[ssh_server_channel]}
                 ],
@@ -328,9 +325,9 @@
         {ssh_cm,ConnectionRef, {data, ChannelId0, 0, <<"TimeoutShell started!",Rest/binary>>}} ->
             ct:log("TimeoutShell started. Rest = ~p", [Rest]),
             receive
-                %%---- wait for the subsystem to terminate
+                %%---- wait for the connection to terminate
                 {ssh_cm,ConnectionRef,{closed,ChannelId0}} ->
-                    ct:log("Subsystem terminated",[]),
+                    ct:log("Connection terminated",[]),
                     case {chk_empty_con_daemon(Daemon),
                           process_info(GroupPid),
                           process_info(ShellPid)} of
@@ -361,23 +358,23 @@
     end.
 
 chk_empty_con_daemon(Daemon) ->
-    ?wait_match([?SUB_SYSTEM_SUP(SubSysSup),
+    ?wait_match([?CONNECTION_SUP(ConnectionSup),
 		 ?ACCEPTOR_SUP(AccSup,_)
                 ],
 		supervisor:which_children(Daemon),
-                [SubSysSup,AccSup]),
+                [ConnectionSup,AccSup]),
     ?wait_match([{_,FwdAccSup, supervisor,[ssh_tcpip_forward_acceptor_sup]},
                  {_,ChSup,supervisor,[ssh_channel_sup]},
                  {connection,ServerConnPid,worker,[ssh_connection_handler]}
                 ],
-		supervisor:which_children(SubSysSup),
+		supervisor:which_children(ConnectionSup),
 		[ChSup,FwdAccSup,ServerConnPid]),
     ?wait_match([], supervisor:which_children(FwdAccSup)),
     ?wait_match([], supervisor:which_children(ChSup)),
     ?wait_match([?ACCEPTOR_WORKER(_,_)],
                 supervisor:which_children(AccSup),
                 []),
-    [SubSysSup, ChSup, ServerConnPid, AccSup, FwdAccSup].
+    [ConnectionSup, ChSup, ServerConnPid, AccSup, FwdAccSup].
 
 %%-------------------------------------------------------------------------
 %% Help functions
@@ -389,14 +386,14 @@
                                                    {user, ?USER},
                                                    {password, ?PASSWD},
                                                    {user_dir, UserDir}]),
-    ?wait_match([?SUB_SYSTEM_SUP(SubSysSup),
+    ?wait_match([?CONNECTION_SUP(ConnectionSup),
 		 ?ACCEPTOR_SUP(AccSup,_)],
 		supervisor:which_children(Daemon),
-                [SubSysSup,AccSup]),
+                [ConnectionSup,AccSup]),
     ?wait_match([{_,FwdAccSup, supervisor,[ssh_tcpip_forward_acceptor_sup]},
                  {_,_,supervisor,[ssh_channel_sup]},
                  {connection,ServerConn,worker,[ssh_connection_handler]}],
-		supervisor:which_children(SubSysSup),
+		supervisor:which_children(ConnectionSup),
 		[FwdAccSup,ServerConn]),
     ?wait_match([], supervisor:which_children(FwdAccSup)),
     ?wait_match([?ACCEPTOR_WORKER(_,_)], supervisor:which_children(AccSup)),
@@ -404,7 +401,7 @@
     ?wait_match([{_,FwdAccSup, supervisor,[ssh_tcpip_forward_acceptor_sup]},
                  {_,ChSup,supervisor,[ssh_channel_sup]},
                  {connection,ServerConn,worker,[ssh_connection_handler]}],
-		supervisor:which_children(SubSysSup),
+		supervisor:which_children(ConnectionSup),
 		[ChSup,ServerConn]),
 
     ?wait_match([{_,PidS,worker,[ssh_server_channel]}],
@@ -415,15 +412,15 @@
     ssh:close(ClientConn).
 
 
-check_sshc_system_tree(SysSup, Connection, _LocalIP, _LocalPort, _Config) ->
-    ?wait_match([?SUB_SYSTEM_SUP(SubSysSup)],
-                supervisor:which_children(SysSup),
-                [SubSysSup]),
+check_sshc_system_tree(ConnectionSup, Connection, _Config) ->
+    ?wait_match([?CONNECTION_SUP(ConnectionSup)],
+                supervisor:which_children(sshc_sup),
+                [ConnectionSup]),
     ?wait_match([{_,FwdAccSup, supervisor,[ssh_tcpip_forward_acceptor_sup]},
                  {_,_,supervisor,[ssh_channel_sup]},
                  {connection,Connection,worker,[ssh_connection_handler]}
                 ],
-		supervisor:which_children(SubSysSup),
+		supervisor:which_children(ConnectionSup),
 		[FwdAccSup]),
     ?wait_match([], supervisor:which_children(FwdAccSup)),
 
@@ -432,7 +429,7 @@
                  {_,ChSup,supervisor, [ssh_channel_sup]},
                  {connection,Connection,worker,[ssh_connection_handler]}
                 ],
-		supervisor:which_children(SubSysSup),
+		supervisor:which_children(ConnectionSup),
 		[ChSup,FwdAccSup]),
 
     ?wait_match([{_,ChPid1,worker,[ssh_client_channel]}
@@ -445,7 +442,7 @@
                  {_,ChSup,supervisor, [ssh_channel_sup]},
                  {connection,Connection,worker,[ssh_connection_handler]}
                 ],
-		supervisor:which_children(SubSysSup),
+		supervisor:which_children(ConnectionSup),
 		[ChSup,FwdAccSup]),
 
     ?wait_match([{_,ChPid2,worker,[ssh_client_channel]},
@@ -460,7 +457,7 @@
                  {_,ChSup,supervisor, [ssh_channel_sup]},
                  {connection,Connection,worker,[ssh_connection_handler]}
                 ],
-		supervisor:which_children(SubSysSup),
+		supervisor:which_children(ConnectionSup),
 		[ChSup,FwdAccSup]),
 
     ?wait_match([{_,ChPid2,worker,[ssh_client_channel]}
@@ -474,7 +471,7 @@
                  {_,ChSup,supervisor, [ssh_channel_sup]},
                  {connection,Connection,worker,[ssh_connection_handler]}
                 ],
-		supervisor:which_children(SubSysSup),
+		supervisor:which_children(ConnectionSup),
 		[ChSup,FwdAccSup]),
 
     ?wait_match([], supervisor:which_children(ChSup)),
diff -ruN a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
--- a/lib/ssh/test/ssh_test_lib.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/test/ssh_test_lib.erl	2025-12-17 17:25:02.082182205 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2004-2023. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -64,6 +64,7 @@
 del_dir_contents/1,
 do_del_files/2,
 openssh_sanity_check/1,
+verify_sanity_check/1,
 default_algorithms/1,
 default_algorithms/3,
 default_algorithms/2,
@@ -122,17 +123,26 @@
 setup_known_host/3,
 get_addr_str/0,
 file_base_name/2,
-add_report_handler/0,
-get_reports/1,
 kex_strict_negotiated/2,
 event_logged/3
         ]).
+%% logger callbacks and related helpers
+-export([log/2,
+         get_log_level/0, set_log_level/1, add_log_handler/0,
+         rm_log_handler/0, get_log_events/1]).
 
 -include_lib("common_test/include/ct.hrl").
 -include("ssh_transport.hrl").
 -include_lib("kernel/include/file.hrl").
 -include("ssh_test_lib.hrl").
 
+-define(SANITY_CHECK_NOTE,
+        "For enabling test, make sure following commands work:~n"
+        "ok = ssh:start(), "
+        "{ok, _} = ssh:connect(\"localhost\", 22, "
+        "[{password,\"\"},{silently_accept_hosts, true}, "
+        "{save_accepted_host, false}, {user_interaction, false}]).").
+
 %%%----------------------------------------------------------------
 connect(Port, Options) when is_integer(Port) ->
     connect(hostname(), Port, Options).
@@ -566,7 +576,6 @@
                           end
                   end, Files).
 
-
 openssh_sanity_check(Config) ->
     ssh:start(),
     case ssh:connect("localhost", ?SSH_DEFAULT_PORT,
@@ -578,11 +587,24 @@
 	{ok, Pid} ->
 	    ssh:close(Pid),
 	    ssh:stop(),
-	    Config;
+	    [{sanity_check_result, ok} | Config];
 	Err ->
 	    Str = lists:append(io_lib:format("~p", [Err])),
+            ct:log("Error = ~p", [Err]),
+            ct:log(?SANITY_CHECK_NOTE),
 	    ssh:stop(),
-	    {skip, Str}
+	    [{sanity_check_result, Str} | Config]
+    end.
+
+verify_sanity_check(Config) ->
+    SanityCheckResult = proplists:get_value(sanity_check_result, Config, ok),
+    case SanityCheckResult of
+        ok ->
+            Config;
+        Err ->
+            ct:log("Error = ~p", [Err]),
+            ct:log(?SANITY_CHECK_NOTE),
+            {fail, passwordless_connection_failed}
     end.
 
 %%%--------------------------------------------------------------------
@@ -1271,15 +1293,10 @@
 file_base_name(system_src, Alg) -> file_base_name(system, Alg).
 
 %%%----------------------------------------------------------------
-add_report_handler() ->
-    ssh_eqc_event_handler:add_report_handler().
-
-get_reports(Pid) ->
-    ssh_eqc_event_handler:get_reports(Pid).
-
 -define(SEARCH_FUN(EXP),
         begin
-            fun({info_report, _, {_, std_info, EXP}}) ->
+            fun(#{msg := {string, EXP},
+                  level := debug}) ->
                     true;
                (_) ->
                     false
@@ -1287,19 +1304,20 @@
         end).
 -define(SEARCH_SUFFIX, " will use strict KEX ordering").
 
-kex_strict_negotiated(client, Reports) ->
-    kex_strict_negotiated(?SEARCH_FUN("client" ++ ?SEARCH_SUFFIX), Reports);
-kex_strict_negotiated(server, Reports) ->
-    kex_strict_negotiated(?SEARCH_FUN("server" ++ ?SEARCH_SUFFIX), Reports);
-kex_strict_negotiated(SearchFun, Reports) when is_function(SearchFun) ->
-    case lists:search(SearchFun, Reports) of
+kex_strict_negotiated(client, Events) ->
+    kex_strict_negotiated(?SEARCH_FUN("client" ++ ?SEARCH_SUFFIX), Events);
+kex_strict_negotiated(server, Events) ->
+    kex_strict_negotiated(?SEARCH_FUN("server" ++ ?SEARCH_SUFFIX), Events);
+kex_strict_negotiated(SearchFun, Events) when is_function(SearchFun) ->
+    %% FIXME use event_logged?
+    case lists:search(SearchFun, Events) of
         {value, _} -> true;
         _ -> false
     end.
 
-event_logged(Role, Reports, Reason) ->
+event_logged(Role, Events, Reason) ->
     SearchF =
-        fun({info_msg, _, {_, _Format, Args}}) ->
+        fun(#{msg := {report, #{args := Args}}}) ->
                 AnyF = fun (E) when is_list(E) ->
                                case string:find(E, Reason) of
                                    nomatch -> false;
@@ -1310,10 +1328,48 @@
                        end,
                 lists:member(Role, Args) andalso
                     lists:any(AnyF, Args);
-           (_) ->
+           (_Event) ->
                 false
         end,
-    case lists:search(SearchF, Reports) of
+    case lists:search(SearchF, Events) of
         {value, _} -> true;
         _ -> false
     end.
+
+get_log_level() ->
+    #{level := Level} = logger:get_primary_config(),
+    Level.
+
+set_log_level(Level) ->
+    ok = logger:set_primary_config(level, Level).
+
+add_log_handler() ->
+    logger:remove_handler(?MODULE),
+    TestRef = make_ref(),
+    ok = logger:add_handler(?MODULE, ?MODULE,
+                            #{level => debug,
+                              filter_default => log,
+                              recipient => self(),
+                              test_ref => TestRef}),
+    {ok, TestRef}.
+
+rm_log_handler() ->
+    ok = logger:remove_handler(?MODULE).
+
+get_log_events(TestRef) ->
+    {ok, get_log_events(TestRef, [])}.
+
+get_log_events(TestRef, Acc) ->
+    receive
+        {TestRef, Event} ->
+            get_log_events(TestRef, [Event | Acc])
+    after
+        500 ->
+            Acc
+    end.
+
+%% logger callbacks
+log(LogEvent = #{level:=_Level,msg:=_Msg,meta:=_Meta},
+    #{test_ref := TestRef, recipient := Recipient}) ->
+    Recipient ! {TestRef, LogEvent},
+    ok.
diff -ruN a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl
--- a/lib/ssh/test/ssh_to_openssh_SUITE.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl	2025-12-22 23:16:56.271839046 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2023. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2024. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -23,7 +23,7 @@
 
 -include_lib("common_test/include/ct.hrl").
 -include("ssh_test_lib.hrl").
--include_lib("ssh/src/ssh_transport.hrl").
+-include("ssh_transport.hrl").
 
 -export([
          suite/0,
@@ -131,12 +131,15 @@
 
 init_per_testcase(erlang_server_openssh_client_renegotiate, Config) ->
     case os:type() of
-	{unix,_} -> ssh:start(), Config;
-	Type -> {skip, io_lib:format("Unsupported test on ~p",[Type])}
+	{unix,_} ->
+            ssh:start(),
+            ssh_test_lib:verify_sanity_check(Config);
+	Type ->
+            {skip, io_lib:format("Unsupported test on ~p",[Type])}
     end;
 init_per_testcase(_TestCase, Config) ->
     ssh:start(),
-    Config.
+    ssh_test_lib:verify_sanity_check(Config).
 
 end_per_testcase(_TestCase, _Config) ->
     ssh:stop(),
@@ -146,29 +149,32 @@
 %% Test Cases --------------------------------------------------------
 %%--------------------------------------------------------------------
 erlang_shell_client_openssh_server(Config) when is_list(Config) ->
-    eclient_oserver_helper(Config).
+    eclient_oserver_helper2(eclient_oserver_helper1(), Config).
 
 eclient_oserver_kex_strict(Config) when is_list(Config)->
     case proplists:get_value(kex_strict, Config) of
         true ->
-            {ok, HandlerPid} = ssh_test_lib:add_report_handler(),
-            #{level := Level} = logger:get_primary_config(),
-            logger:set_primary_config(level, notice),
-            Result = eclient_oserver_helper(Config),
-            {ok, Reports} = ssh_test_lib:get_reports(HandlerPid),
-            ct:pal("Reports = ~p", [Reports]),
-            true = ssh_test_lib:kex_strict_negotiated(client, Reports),
-            logger:set_primary_config(Level),
-            Result;
+            {ok, TestRef} = ssh_test_lib:add_log_handler(),
+            Level = ssh_test_lib:get_log_level(),
+            ssh_test_lib:set_log_level(debug),
+            HelperParams = eclient_oserver_helper1(),
+            {ok, Events} = ssh_test_lib:get_log_events(TestRef),
+            true = ssh_test_lib:kex_strict_negotiated(client, Events),
+            ssh_test_lib:set_log_level(Level),
+            ssh_test_lib:rm_log_handler(),
+            eclient_oserver_helper2(HelperParams, Config);
         _ ->
             {skip, "KEX strict not support by local OpenSSH"}
     end.
 
-eclient_oserver_helper(Config) ->
+eclient_oserver_helper1() ->
     process_flag(trap_exit, true),
     IO = ssh_test_lib:start_io_server(),
     Prev = lists:usort(supervisor:which_children(sshc_sup)),
     Shell = ssh_test_lib:start_shell(?SSH_DEFAULT_PORT, IO),
+    {Shell, Prev, IO}.
+
+eclient_oserver_helper2({Shell, Prev, IO}, Config) ->
     IO ! {input, self(), "echo Hej\n"},
     case proplists:get_value(ptty_supported, Config) of
         true ->
@@ -253,25 +259,28 @@
 %%--------------------------------------------------------------------
 %% Test that the Erlang/OTP server can renegotiate with openSSH
 erlang_server_openssh_client_renegotiate(Config) ->
-    eserver_oclient_renegotiate_helper(Config).
+    eserver_oclient_renegotiate_helper2(
+      eserver_oclient_renegotiate_helper1(Config)).
 
 eserver_oclient_kex_strict(Config) ->
     case proplists:get_value(kex_strict, Config) of
         true ->
-            {ok, HandlerPid} = ssh_test_lib:add_report_handler(),
-            #{level := Level} = logger:get_primary_config(),
-            logger:set_primary_config(level, notice),
-            Result = eserver_oclient_renegotiate_helper(Config),
-            {ok, Reports} = ssh_test_lib:get_reports(HandlerPid),
-            ct:log("Reports = ~p", [Reports]),
-            true = ssh_test_lib:kex_strict_negotiated(server, Reports),
-            logger:set_primary_config(Level),
-            Result;
+            {ok, TestRef} = ssh_test_lib:add_log_handler(),
+            Level = ssh_test_lib:get_log_level(),
+            ssh_test_lib:set_log_level(debug),
+
+            HelperParams = eserver_oclient_renegotiate_helper1(Config),
+            {ok, Events} = ssh_test_lib:get_log_events(TestRef),
+            ct:log("Events = ~n~p", [Events]),
+            true = ssh_test_lib:kex_strict_negotiated(server, Events),
+            ssh_test_lib:set_log_level(Level),
+            ssh_test_lib:rm_log_handler(),
+            eserver_oclient_renegotiate_helper2(HelperParams);
         _ ->
             {skip, "KEX strict not support by local OpenSSH"}
     end.
 
-eserver_oclient_renegotiate_helper(Config) ->
+eserver_oclient_renegotiate_helper1(Config) ->
     _PubKeyAlg = ssh_rsa,
     SystemDir = proplists:get_value(data_dir, Config),
     PrivDir = proplists:get_value(priv_dir, Config),
@@ -295,7 +304,9 @@
 
 
     OpenSsh = ssh_test_lib:open_port({spawn, Cmd++" < "++DataFile}),
+    {Data, OpenSsh, Pid}.
 
+eserver_oclient_renegotiate_helper2({Data, OpenSsh, Pid}) ->
     Expect = fun({data,R}) ->
 		     try
 			 NonAlphaChars = [C || C<-lists:seq(1,255),
@@ -623,3 +634,4 @@
             ct:log("KEX strict supported by local OpenSSH"),
             true
     end.
+
diff -ruN a/lib/ssh/test/ssh_trpt_test_lib.erl b/lib/ssh/test/ssh_trpt_test_lib.erl
--- a/lib/ssh/test/ssh_trpt_test_lib.erl	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/test/ssh_trpt_test_lib.erl	2025-12-17 17:25:02.082556494 +1030
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2004-2023. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -24,7 +24,8 @@
 -export([exec/1, exec/2,
 	 instantiate/2,
 	 format_msg/1,
-	 server_host_port/1
+	 server_host_port/1,
+         return_value/1
 	]
        ).
 
@@ -91,7 +92,8 @@
 	    report_trace(throw, Term, S1),
 	    throw({Term,Op});
 
-	error:Error ->
+	error:Error:St ->
+            ct:log("Stacktrace=~n~p", [St]),
 	    report_trace(error, Error, S1),
 	    error({Error,Op});
 
@@ -336,6 +338,17 @@
     Msg = #ssh_msg_ignore{data = "unexpected_ignore_message"},
     send(S0, Msg);
 
+send(S0, ssh_msg_debug) ->
+    Msg = #ssh_msg_debug{
+             always_display = true,
+             message = "some debug message",
+             language = "en"},
+    send(S0, Msg);
+
+send(S0, ssh_msg_unimplemented) ->
+    Msg = #ssh_msg_unimplemented{sequence = 123},
+    send(S0, Msg);
+
 send(S0, ssh_msg_unknown) ->
     Msg = binary:encode_hex(<<"0000000C060900000000000000000000">>),
     send(S0, Msg);
@@ -383,6 +396,26 @@
 	    end),
     send_bytes(NextKexMsgBin, S#s{ssh = C});
 
+send(S0, ssh_msg_kexdh_init_dup) when ?role(S0) == client ->
+    {OwnMsg, PeerMsg} = S0#s.alg_neg,
+    {ok, NextKexMsgBin, C} =
+	try ssh_transport:handle_kexinit_msg(PeerMsg, OwnMsg, S0#s.ssh, init)
+	catch
+	    Class:Exc ->
+		fail("Algorithm negotiation failed!",
+		     {"Algorithm negotiation failed at line ~p:~p~n~p:~s~nPeer: ~s~n Own: ~s",
+		      [?MODULE,?LINE,Class,format_msg(Exc),format_msg(PeerMsg),format_msg(OwnMsg)]},
+		     S0)
+	end,
+    S = opt(print_messages, S0,
+	    fun(X) when X==true;X==detail ->
+		    #ssh{keyex_key = {{_Private, Public}, {_G, _P}}} = C,
+		    Msg = #ssh_msg_kexdh_init{e = Public},
+		    {"Send (reconstructed)~n~s~n",[format_msg(Msg)]}
+	    end),
+    send_bytes(NextKexMsgBin, S#s{ssh = C}),
+    send_bytes(NextKexMsgBin, S#s{ssh = C});
+
 send(S0, ssh_msg_kexdh_reply) ->
     Bytes = proplists:get_value(ssh_msg_kexdh_reply, S0#s.reply),
     S = opt(print_messages, S0,
@@ -532,7 +565,10 @@
            S0#s.ssh)
      of
          {packet_decrypted, DecryptedBytes, EncryptedDataRest, Ssh1} ->
-             S1 = S0#s{ssh = Ssh1#ssh{recv_sequence = ssh_transport:next_seqnum(Ssh1#ssh.recv_sequence)},
+             S1 = S0#s{ssh = Ssh1#ssh{recv_sequence =
+                                          ssh_transport:next_seqnum(undefined,
+                                                                    Ssh1#ssh.recv_sequence,
+                                                                    false)},
                        decrypted_data_buffer = <<>>,
                        undecrypted_packet_length = undefined,
                        aead_data = <<>>,
@@ -779,3 +815,6 @@
 
 save_prints({Fmt,Args}, S) -> 
     S#s{prints = [{Fmt,Args}|S#s.prints]}.
+
+return_value(#s{return_value = ReturnValue}) ->
+    ReturnValue.
diff -ruN a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
--- a/lib/ssh/vsn.mk	2023-12-16 04:53:01.000000000 +1030
+++ b/lib/ssh/vsn.mk	2025-12-22 23:16:56.271839046 +1030
@@ -1,4 +1,4 @@
 #-*-makefile-*-   ; force emacs to enter makefile-mode
 
-SSH_VSN = 5.1.1
+SSH_VSN = 5.1.4.13
 APP_VSN    = "ssh-$(SSH_VSN)"
openSUSE Build Service is sponsored by