[Git][debian-proftpd-team/proftpd][trixie] deb13u4: #1151962

Hilmar Preuße (@hilmar) gitlab at salsa.debian.org
Sun Oct 25 21:17:54 GMT 2026



Hilmar Preuße pushed to branch trixie at Debian ProFTPD Team / proftpd


Commits:
248716dc by Hilmar Preuße at 2026-10-25T22:17:18+01:00
deb13u4: #1151962

- - - - -


5 changed files:

- debian/changelog
- + debian/patches/0b5a1611312ee70ffd38693bb4374cb8668a6e6e.diff
- + debian/patches/508eb4d1b8c550b5f3059ad5b33d60c6b6c76bc7.diff
- + debian/patches/c1607a2a219d56443809de326e673b4e38b3bc58.diff
- debian/patches/series


Changes:

=====================================
debian/changelog
=====================================
@@ -1,3 +1,9 @@
+proftpd-dfsg (1.3.8.c+dfsg-4+deb13u4) trixie; urgency=medium
+
+  * Add patches for mod_tls spins at 100% CPU (...) (Closes: #1151962).
+
+ -- Hilmar Preuße <hille42 at debian.org>  Sun, 25 Oct 2026 22:16:31 +0100
+
 proftpd-dfsg (1.3.8.c+dfsg-4+deb13u3) trixie; urgency=medium
 
   * Add patch for CVE-2026-44331 (Closes: #1135840).


=====================================
debian/patches/0b5a1611312ee70ffd38693bb4374cb8668a6e6e.diff
=====================================
@@ -0,0 +1,379 @@
+From 0b5a1611312ee70ffd38693bb4374cb8668a6e6e Mon Sep 17 00:00:00 2001
+From: TJ Saunders <tj at castaglia.org>
+Date: Wed, 16 Jul 2025 09:26:28 -0700
+Subject: [PATCH] Issue #1963: When shutting down our TLS connections, check
+ for the case where the same `SSL_SESSION *` is used by different `SSL *`, and
+ manually avoid a potential double-free.
+
+---
+ contrib/mod_tls.c                            |  61 +++++--
+ tests/t/lib/ProFTPD/Tests/Modules/mod_tls.pm | 174 +++++++++++++------
+ 2 files changed, 172 insertions(+), 63 deletions(-)
+
+--- proftpd.orig/contrib/mod_tls.c
++++ proftpd/contrib/mod_tls.c
+@@ -8874,8 +8874,8 @@
+ 
+     /* 'close_notify' not already sent; send it now. */
+     pr_trace_msg(trace_channel, 17,
+-      "shutting down TLS session, 'close_notify' not already sent; "
+-      "sending now");
++      "shutting down %s TLS session, 'close_notify' not already sent; "
++      "sending now", ssl == ctrl_ssl ? "control" : "data");
+     lineno = __LINE__ + 1;
+     res = SSL_shutdown(ssl);
+   }
+@@ -8891,8 +8891,8 @@
+         int is_ssl_data = FALSE, xerrno;
+ 
+         pr_trace_msg(trace_channel, 17,
+-          "shutting down TLS session, 'close_notify' not received; "
+-          "peeking at next data");
++          "shutting down %s TLS session, 'close_notify' not received; "
++          "peeking at next data", ssl == ctrl_ssl ? "control" : "data");
+ 
+         /* This where we need to peek at the next data, to see whether we
+          * dealing with a well-behaved FTPS client, which will be sending
+@@ -8929,8 +8929,9 @@
+         xerrno = errno;
+ 
+         pr_trace_msg(trace_channel, 17,
+-          "shutting down TLS session, 'close_notify' not received; "
+-          "SSL_shutdown() returned %d", res);
++          "shutting down %s TLS session, 'close_notify' not received; "
++          "SSL_shutdown() returned %d", ssl == ctrl_ssl ? "control" : "data",
++          res);
+ 
+         errno = xerrno;
+       }
+@@ -9071,10 +9072,39 @@
+     session.total_raw_out += bwritten;
+   }
+ 
++  if (ssl != ctrl_ssl &&
++      SSL_get_session(ssl) == SSL_get_session(ctrl_ssl)) {
++    /* Uh-oh; our two SSL objects are pointing at the same SSL_SESSION object.
++     * This can happen when the SSL_SESSION is resumed, enabled by session
++     * caching.  Which is normally a Good Thing.
++     *
++     * But Issue#1963 happens when that SSL_SESSION object is freed twice:
++     * once via SSL_free() on this SSL, and again on SSL_free() for the
++     * other SSL.  Yuck.
++     *
++     * So we manually handle the situation by clearing the duplicate
++     * SSL_SESSION pointer from the data SSL.  Why the data SSL, and not the
++     * control SSL?  We could be handling multiple concurrent data transfers,
++     * and/or the client may reuse the session again on a subsequent data
++     * transfer.
++     *
++     * Note that older OpenSSL versions used two different SSL_SESSION objects,
++     * hence why we have not seen this occur frequently in the past.  (Or it
++     * was not reported.)  And for TLSv1.3 resumed sessions, the SSL_SESSION
++     * objects are different, hence no double-free.
++     */
++    pr_trace_msg(trace_channel, 29,
++      "data SSL %p being ended has same SSL_SESSION %p as control SSL, "
++      "clearing the data SSL pointer manually (Issue #1963)", ssl,
++      SSL_get_session(ssl));
++    SSL_set_session(ssl, NULL);
++  }
++
+   SSL_free(ssl);
+ 
+   if (res >= 0) {
+-    pr_trace_msg(trace_channel, 17, "TLS session cleanly shut down");
++    pr_trace_msg(trace_channel, 17, "%s TLS session cleanly shut down",
++      ssl == ctrl_ssl ? "control" : "data");
+   }
+ }
+ 
+@@ -15805,13 +15835,13 @@
+    * and thus we have a read-only copy.
+    */
+ 
+-  if (tls_ctrl_netio) {
++  if (tls_ctrl_netio != NULL) {
+     pr_unregister_netio(PR_NETIO_STRM_CTRL);
+     destroy_pool(tls_ctrl_netio->pool);
+     tls_ctrl_netio = NULL;
+   }
+ 
+-  if (tls_data_netio) {
++  if (tls_data_netio != NULL) {
+     pr_unregister_netio(PR_NETIO_STRM_DATA);
+     destroy_pool(tls_data_netio->pool);
+     tls_data_netio = NULL;
+@@ -15822,7 +15852,6 @@
+   }
+ 
+   tls_closelog();
+-  return;
+ }
+ 
+ static void tls_timeout_ev(const void *event_data, void *user_data) {
+@@ -17150,12 +17179,18 @@
+ 
+ static char *get_sess_id_text(BIO *bio, const unsigned char *id,
+     unsigned int idsz) {
+-  register unsigned int i;
+   char *data = NULL;
+   long datalen;
+ 
+-  for (i = 0; i < idsz; i++) {
+-    BIO_printf(bio, "%02x", id[i]);
++  if (idsz > 0) {
++    register unsigned int i;
++
++    for (i = 0; i < idsz; i++) {
++      BIO_printf(bio, "%02x", id[i]);
++    }
++
++  } else {
++    BIO_printf(bio, "%s", "NONE");
+   }
+ 
+   datalen = BIO_get_mem_data(bio, &data);
+--- proftpd.orig/tests/t/lib/ProFTPD/Tests/Modules/mod_tls.pm
++++ proftpd/tests/t/lib/ProFTPD/Tests/Modules/mod_tls.pm
+@@ -135,6 +135,11 @@
+     test_class => [qw(forking)],
+   },
+ 
++  tls_list_with_session_reuse => {
++    order => ++$order,
++    test_class => [qw(forking)],
++  },
++
+   tls_list_with_no_session_reuse_required_opt => {
+     order => ++$order,
+     test_class => [qw(forking)],
+@@ -3800,49 +3805,19 @@
+ sub tls_list_no_session_reuse {
+   my $self = shift;
+   my $tmpdir = $self->{tmpdir};
+-
+-  my $config_file = "$tmpdir/tls.conf";
+-  my $pid_file = File::Spec->rel2abs("$tmpdir/tls.pid");
+-  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/tls.scoreboard");
+-
+-  my $log_file = test_get_logfile();
+-
+-  my $auth_user_file = File::Spec->rel2abs("$tmpdir/tls.passwd");
+-  my $auth_group_file = File::Spec->rel2abs("$tmpdir/tls.group");
+-
+-  my $user = 'proftpd';
+-  my $passwd = 'test';
+-  my $group = 'ftpd';
+-  my $home_dir = File::Spec->rel2abs($tmpdir);
+-  my $uid = 500;
+-  my $gid = 500;
+-
+-  # Make sure that, if we're running as root, that the home directory has
+-  # permissions/privs set for the account we create
+-  if ($< == 0) {
+-    unless (chmod(0755, $home_dir)) {
+-      die("Can't set perms on $home_dir to 0755: $!");
+-    }
+-
+-    unless (chown($uid, $gid, $home_dir)) {
+-      die("Can't set owner of $home_dir to $uid/$gid: $!");
+-    }
+-  }
+-
+-  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
+-    '/bin/bash');
+-  auth_group_write($auth_group_file, $group, $gid, $user);
++  my $setup = test_setup($tmpdir, 'tls');
+ 
+   my $cert_file = File::Spec->rel2abs('t/etc/modules/mod_tls/server-cert.pem');
+   my $ca_file = File::Spec->rel2abs('t/etc/modules/mod_tls/ca-cert.pem');
+ 
+   my $config = {
+-    PidFile => $pid_file,
+-    ScoreboardFile => $scoreboard_file,
+-    SystemLog => $log_file,
++    PidFile => $setup->{pid_file},
++    ScoreboardFile => $setup->{scoreboard_file},
++    SystemLog => $setup->{log_file},
+ 
+-    AuthUserFile => $auth_user_file,
+-    AuthGroupFile => $auth_group_file,
++    AuthUserFile => $setup->{auth_user_file},
++    AuthGroupFile => $setup->{auth_group_file},
++    AuthOrder => 'mod_auth_file.c',
+ 
+     IfModules => {
+       'mod_delay.c' => {
+@@ -3851,7 +3826,7 @@
+ 
+       'mod_tls.c' => {
+         TLSEngine => 'on',
+-        TLSLog => $log_file,
++        TLSLog => $setup->{log_file},
+         TLSRequired => 'on',
+         TLSRSACertificateFile => $cert_file,
+         TLSCACertificateFile => $ca_file,
+@@ -3859,7 +3834,8 @@
+     },
+   };
+ 
+-  my ($port, $config_user, $config_group) = config_write($config_file, $config);
++  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
++    $config);
+ 
+   # Open pipes, for use between the parent and child processes.  Specifically,
+   # the child will indicate when it's done with its test by writing a message
+@@ -3887,7 +3863,7 @@
+         Croak => 1,
+       );
+ 
+-      unless ($client->login($user, $passwd)) {
++      unless ($client->login($setup->{user}, $setup->{passwd})) {
+         die("Can't login: " . $client->last_message());
+       }
+ 
+@@ -3908,9 +3884,8 @@
+       my $resp = $client->message();
+       my $expected = '425 Unable to build data connection: Operation not permitted';
+       $self->assert($expected eq $resp,
+-        test_msg("Expected '$expected', got '$resp'"));
++        test_msg("Expected response '$expected', got '$resp'"));
+     };
+-
+     if ($@) {
+       $ex = $@;
+     }
+@@ -3919,7 +3894,7 @@
+     $wfh->flush();
+ 
+   } else {
+-    eval { server_wait($config_file, $rfh) };
++    eval { server_wait($setup->{config_file}, $rfh) };
+     if ($@) {
+       warn($@);
+       exit 1;
+@@ -3929,18 +3904,117 @@
+   }
+ 
+   # Stop server
+-  server_stop($pid_file);
+-
++  server_stop($setup->{pid_file});
+   $self->assert_child_ok($pid);
+ 
+-  if ($ex) {
+-    test_append_logfile($log_file, $ex);
+-    unlink($log_file);
++  test_cleanup($setup->{log_file}, $ex);
++}
+ 
+-    die($ex);
++sub tls_list_with_session_reuse {
++  my $self = shift;
++  my $tmpdir = $self->{tmpdir};
++  my $setup = test_setup($tmpdir, 'tls');
++
++  my $cert_file = File::Spec->rel2abs('t/etc/modules/mod_tls/server-cert.pem');
++  my $ca_file = File::Spec->rel2abs('t/etc/modules/mod_tls/ca-cert.pem');
++
++  my $config = {
++    PidFile => $setup->{pid_file},
++    ScoreboardFile => $setup->{scoreboard_file},
++    SystemLog => $setup->{log_file},
++
++    AuthUserFile => $setup->{auth_user_file},
++    AuthGroupFile => $setup->{auth_group_file},
++    AuthOrder => 'mod_auth_file.c',
++
++    IfModules => {
++      'mod_delay.c' => {
++        DelayEngine => 'off',
++      },
++
++      'mod_tls.c' => {
++        TLSEngine => 'on',
++        TLSLog => $setup->{log_file},
++        TLSRequired => 'on',
++        TLSRSACertificateFile => $cert_file,
++        TLSCACertificateFile => $ca_file,
++      },
++    },
++  };
++
++  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
++    $config);
++
++  # Open pipes, for use between the parent and child processes.  Specifically,
++  # the child will indicate when it's done with its test by writing a message
++  # to the parent.
++  my ($rfh, $wfh);
++  unless (pipe($rfh, $wfh)) {
++    die("Can't open pipe: $!");
+   }
+ 
+-  unlink($log_file);
++  require Net::FTPSSL;
++
++  my $ex;
++
++  # Fork child
++  $self->handle_sigchld();
++  defined(my $pid = fork()) or die("Can't fork: $!");
++  if ($pid) {
++    eval {
++      # Give the server a chance to start up
++      sleep(2);
++
++      my $client = Net::FTPSSL->new('127.0.0.1',
++        Encryption => 'E',
++        Port => $port,
++        Croak => 1,
++        ReuseSession => 1,
++      );
++
++      unless ($client->login($setup->{user}, $setup->{passwd})) {
++        die("Can't login: " . $client->last_message());
++      }
++
++      # Since we are requiring SSL session reuse for data transfers,
++      # and this client IS using SSL session resumption, I expect
++      # these data transfers to succeed.
++      for (my $i = 0; $i < 3; $i++) {
++        my $res = $client->list('.');
++        unless ($res) {
++          die("LIST failed unexpectedly");
++        }
++
++        my $resp = $client->message();
++        my $expected = '226 Transfer complete';
++        $self->assert($expected eq $resp,
++          test_msg("Expected response '$expected', got '$resp'"));
++      }
++
++      $client->quit();
++    };
++    if ($@) {
++      $ex = $@;
++    }
++
++    $wfh->print("done\n");
++    $wfh->flush();
++
++  } else {
++    eval { server_wait($setup->{config_file}, $rfh) };
++    if ($@) {
++      warn($@);
++      exit 1;
++    }
++
++    exit 0;
++  }
++
++  # Stop server
++  server_stop($setup->{pid_file});
++  $self->assert_child_ok($pid);
++
++  test_cleanup($setup->{log_file}, $ex);
+ }
+ 
+ sub tls_list_with_no_session_reuse_required_opt {


=====================================
debian/patches/508eb4d1b8c550b5f3059ad5b33d60c6b6c76bc7.diff
=====================================
@@ -0,0 +1,41 @@
+From 508eb4d1b8c550b5f3059ad5b33d60c6b6c76bc7 Mon Sep 17 00:00:00 2001
+From: TJ Saunders <tj at castaglia.org>
+Date: Sun, 10 May 2026 09:37:17 -0700
+Subject: [PATCH] Issue #2056: Initial testing with OpenSSL 3.5.5 shows that
+ the prior changes for Issue #1963 were insufficient; adding a manual refcount
+ increment works.
+
+---
+ contrib/mod_tls.c | 18 ++++++++++++++++--
+ 1 file changed, 16 insertions(+), 2 deletions(-)
+
+diff --git a/contrib/mod_tls.c b/contrib/mod_tls.c
+index e78e4c17be..fede5b8cd1 100644
+--- a/contrib/mod_tls.c
++++ b/contrib/mod_tls.c
+@@ -9095,9 +9095,23 @@ static void tls_end_sess(SSL *ssl, conn_t *conn, int flags) {
+      */
+     pr_trace_msg(trace_channel, 29,
+       "data SSL %p being ended has same SSL_SESSION %p as control SSL, "
+-      "clearing the data SSL pointer manually (Issue #1963)", ssl,
++      "clearing the data SSL pointer manually (see Issue #1963)", ssl,
+       SSL_get_session(ssl));
+-    SSL_set_session(ssl, NULL);
++    if (SSL_set_session(ssl, NULL) != 1) {
++      pr_trace_msg(trace_channel, 29,
++        "error setting NULL session on SSL %p: %s", ssl, tls_get_errors());
++    }
++
++    /* Note that, per findings in Issue #2056, we also need to manually
++     * increment the refcount of the ctrl_ssl session, lest we still
++     * inadvertently corrupt the OpenSSL internal session cache state.
++     * Sigh.
++     */
++    if (SSL_SESSION_up_ref(SSL_get_session(ctrl_ssl)) != 1) {
++      pr_trace_msg(trace_channel, 29,
++        "error incrementing session refcount on SSL %p: %s", ctrl_ssl,
++        tls_get_errors());
++    }
+   }
+ 
+   SSL_free(ssl);


=====================================
debian/patches/c1607a2a219d56443809de326e673b4e38b3bc58.diff
=====================================
@@ -0,0 +1,24 @@
+From c1607a2a219d56443809de326e673b4e38b3bc58 Mon Sep 17 00:00:00 2001
+From: TJ Saunders <tj at castaglia.org>
+Date: Tue, 19 May 2026 14:46:56 -0700
+Subject: [PATCH] Issue #2093: Check for a null `ctrl_ssl` pointer, as is
+ already done in the master branch, before calling OpenSSL APIs on it and
+ triggering a null pointer dereference crash.
+
+Note that this is a regression in the 1.3.9 branch, caused by changes for addressing Issue #1963.
+---
+ contrib/mod_tls.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/contrib/mod_tls.c b/contrib/mod_tls.c
+index fede5b8cd1..ad476bb210 100644
+--- a/contrib/mod_tls.c
++++ b/contrib/mod_tls.c
+@@ -9073,6 +9073,7 @@ static void tls_end_sess(SSL *ssl, conn_t *conn, int flags) {
+   }
+ 
+   if (ssl != ctrl_ssl &&
++      ctrl_ssl != NULL &&
+       SSL_get_session(ssl) == SSL_get_session(ctrl_ssl)) {
+     /* Uh-oh; our two SSL objects are pointing at the same SSL_SESSION object.
+      * This can happen when the SSL_SESSION is resumed, enabled by session


=====================================
debian/patches/series
=====================================
@@ -29,3 +29,9 @@ mod_sftp-1.3.8d-issue2115.diff
 baf4b7929758c72cdb6cf16325fa25f435d23db6.diff
 # issue 2190, CVE-2026-63090
 mod_sftp-1.3.8d-issue2190.diff
+# Debian #1151962 #1 Issue #1963 (PR #1970)
+0b5a1611312ee70ffd38693bb4374cb8668a6e6e.diff
+# Debian #1151962 #2 Issue #2056 (PR #2076)
+508eb4d1b8c550b5f3059ad5b33d60c6b6c76bc7.diff
+# Debian #1151962 #3 Issue #2093 (PR #2094)
+c1607a2a219d56443809de326e673b4e38b3bc58.diff



View it on GitLab: https://salsa.debian.org/debian-proftpd-team/proftpd/-/commit/248716dcbc356771ae61c4b25f286a0664dcba10

-- 
View it on GitLab: https://salsa.debian.org/debian-proftpd-team/proftpd/-/commit/248716dcbc356771ae61c4b25f286a0664dcba10
You're receiving this email because of your account on salsa.debian.org. Manage all notifications: https://salsa.debian.org/-/profile/notifications | Help: https://salsa.debian.org/help




More information about the Pkg-proftpd-maintainers mailing list