Commit dc20d355b5 for openssl.org

commit dc20d355b553f1dd3c99e468e2cfa0d696a74bab
Author: Uni <pedroluiscolmenares722@gmail.com>
Date:   Fri Mar 6 18:19:31 2026 +0100

    Fix intermittent hang in 82-test_ech_client_server.t

    Reviewed-by: Neil Horman <nhorman@openssl.org>
    Reviewed-by: Matt Caswell <matt@openssl.foundation>
    MergeDate: Tue Mar 10 18:22:17 2026
    (Merged from https://github.com/openssl/openssl/pull/30289)

diff --git a/test/recipes/82-test_ech_client_server.t b/test/recipes/82-test_ech_client_server.t
index 480744491c..446323120c 100644
--- a/test/recipes/82-test_ech_client_server.t
+++ b/test/recipes/82-test_ech_client_server.t
@@ -12,7 +12,6 @@ use warnings;
 use IPC::Open3;
 use OpenSSL::Test qw/:DEFAULT srctop_file srctop_dir bldtop_file/;
 use OpenSSL::Test::Utils;
-use Symbol 'gensym';

 # servers randomly pick a port, then set this for clients to use
 # we also record the pid so we can kill it later if needed
@@ -71,193 +70,200 @@ my $good_b64 = extract_ecl();
 sub start_ech_client_server
 {
     my ( $test_type, $winpattern ) = @_;
+    my $timeout = 60;

-    # start an s_server listening on some random port, with ECH enabled
-    # and willing to accept one request
-
-    # openssl s_server -accept 0 -naccept 1
-    #                  -key $server_key -cert $server_cert
-    #                  -key2 $server_key -cert2 $server_cert
-    #                  -ech_key $echconfig_pem
-    #                  -servername example.com
-    #                  -tls1_3
-    my @s_server_cmd;
-    if ($test_type eq "cid-free" ) {
-        # turn on trial-decrypt, so client can use random CID
-        @s_server_cmd = ("s_server", "-accept", "0", "-naccept", "1",
-                         "-cert", $server_pem, "-key", $server_key,
-                         "-cert2", $server_pem, "-key2", $server_key,
-                         "-ech_key", $echconfig_pem,
-                         "-servername", "example.com",
-                         "-ech_trialdecrypt",
-                         "-tls1_3");
-     } elsif ($test_type eq "keydir" ) {
-        # load keys from key dir (some will fail)
-        @s_server_cmd = ("s_server", "-accept", "0", "-naccept", "1",
-                         "-cert", $server_pem, "-key", $server_key,
-                         "-cert2", $server_pem, "-key2", $server_key,
-                         "-ech_dir", $ech_dir,
-                         "-ech_noretry_dir", $ech_dir,
-                         "-servername", "example.com",
-                         "-tls1_3");
-    } else {
-        # default for all other tests (for now)
-        @s_server_cmd = ("s_server", "-accept", "0", "-naccept", "1",
-                         "-cert", $server_pem, "-key", $server_key,
-                         "-cert2", $server_pem, "-key2", $server_key,
-                         "-ech_key", $echconfig_pem,
-                         "-servername", "example.com",
-                         "-ech_greaseretries",
-                         "-tls1_3");
-    }
-    print("@s_server_cmd\n");
-    $s_server_pid = open3(my $s_server_i, my $s_server_o,
-                             my $s_server_e = gensym,
-                             $shlib_wrap, $apps_openssl, @s_server_cmd);
-    # we're looking for...
-    # ACCEPT 0.0.0.0:45921
-    # ACCEPT [::]:45921
-    $s_server_port = "0";
-    while (<$s_server_o>) {
-        print($_);
-        chomp;
-        if (/^ACCEPT 0.0.0.0:(\d+)/) {
-            $s_server_port = $1;
-            last;
-        } elsif (/^ACCEPT \[::\]:(\d+)/) {
-            $s_server_port = $1;
-            last;
-        } elsif (/^Using default/) {
-            ;
-        } elsif (/^Added (\d+) ECH/) {
-            ;
-        } elsif (/^Added ECH key pair/) {
-            ;
-        } elsif (/^Loaded/) {
-            ;
-        } elsif (/^Setting secondary/) {
-            ;
+    eval {
+        local $SIG{ALRM} = sub { die "timeout\n" };
+        alarm $timeout;
+        my @s_server_cmd;
+        if ($test_type eq "cid-free" ) {
+            # turn on trial-decrypt, so client can use random CID
+            @s_server_cmd = ("s_server", "-accept", "0", "-naccept", "1",
+                             "-cert", $server_pem, "-key", $server_key,
+                             "-cert2", $server_pem, "-key2", $server_key,
+                             "-ech_key", $echconfig_pem,
+                             "-servername", "example.com",
+                             "-ech_trialdecrypt",
+                             "-tls1_3");
+        } elsif ($test_type eq "keydir" ) {
+            # load keys from key dir (some will fail)
+            @s_server_cmd = ("s_server", "-accept", "0", "-naccept", "1",
+                             "-cert", $server_pem, "-key", $server_key,
+                             "-cert2", $server_pem, "-key2", $server_key,
+                             "-ech_dir", $ech_dir,
+                             "-ech_noretry_dir", $ech_dir,
+                             "-servername", "example.com",
+                             "-tls1_3");
         } else {
-            last;
+            # default for all other tests (for now)
+            @s_server_cmd = ("s_server", "-accept", "0", "-naccept", "1",
+                             "-cert", $server_pem, "-key", $server_key,
+                             "-cert2", $server_pem, "-key2", $server_key,
+                             "-ech_key", $echconfig_pem,
+                             "-servername", "example.com",
+                             "-ech_greaseretries",
+                             "-tls1_3");
         }
-    }
-    # openssl s_client -connect localhost:NNNNN
-    #                  -servername server.example
-    #                  -CAfile test/certs/rootcert.pem
-    #                  -ech_config_list "ADn+...AA="
-    #                  -prexit
-    my @s_client_cmd;
-    if ($test_type eq "GREASE-suite" ) {
-        # GREASE with suite
-        @s_client_cmd = ("s_client",
-                         "-connect", "localhost:$s_server_port",
-                         "-servername", "server.example",
-                         "-CAfile", $root_pem,
-                         "-ech_grease_suite", "0x21,2,3",
-                         "-prexit");
-     } elsif ($test_type eq "bad-GREASE-suite" ) {
-        # bad GREASE suite
-        @s_client_cmd = ("s_client",
-                         "-connect", "localhost:$s_server_port",
-                         "-servername", "server.example",
-                         "-CAfile", $root_pem,
-                         "-ech_grease_suite", "thisisnotagoodone",
-                         "-prexit");
-    } elsif ($test_type eq "lots-of-options" ) {
-        # real ECH with lots of options
-        @s_client_cmd = ("s_client",
-                         "-connect", "localhost:$s_server_port",
-                         "-servername", "server.example",
-                         "-CAfile", $root_pem,
-                         "-ech_config_list", $good_b64,
-                         "-ech_outer_sni", "foodle.doodle",
-                         "-ech_select", "0",
-                         "-alpn", "http/1.1",
-                         "-ech_outer_alpn", "http451",
-                         "-prexit");
-    } elsif ($test_type eq "GREASE-type" ) {
-        # GREASE with type
-        @s_client_cmd = ("s_client",
-                         "-connect", "localhost:$s_server_port",
-                         "-servername", "server.example",
-                         "-CAfile", $root_pem,
-                         "-ech_grease_type", "12345",
-                         "-prexit");
-    } elsif ($test_type eq "GREASE" ) {
-        # GREASE with suite
-        @s_client_cmd = ("s_client",
-                         "-connect", "localhost:$s_server_port",
-                         "-servername", "server.example",
-                         "-CAfile", $root_pem,
-                         "-ech_grease",
-                         "-prexit");
-    } elsif ($test_type eq "no-outer" ) {
-        # Real ECH, no outer SNI
-        @s_client_cmd = ("s_client",
-                         "-connect", "localhost:$s_server_port",
-                         "-servername", "server.example",
-                         "-CAfile", $root_pem,
-                         "-ech_config_list", $good_b64,
-                         "-ech_no_outer_sni",
-                         "-prexit");
-    } elsif ($test_type eq "bad-ech" ) {
-        # bad ECH
-        @s_client_cmd = ("s_client",
-                         "-connect", "localhost:$s_server_port",
-                         "-servername", "server.example",
-                         "-CAfile", $root_pem,
-                         "-ech_config_list", "AEH+DQA91wAgACCBdNrnZxqNrUXSyimqqnfmNG4lHtVsbmaaIeRoUoFWFQAEAAEAAQAOc2VydmVyLmV4YW1wbGUAAA==",
-                         "-prexit");
-    } elsif ($test_type eq "cid-free" ) {
-        # Real ECH, ignore CID
-        @s_client_cmd = ("s_client",
-                         "-connect", "localhost:$s_server_port",
-                         "-servername", "server.example",
-                         "-CAfile", $root_pem,
-                         "-ech_config_list", $good_b64,
-                         "-ech_ignore_cid",
-                         "-prexit");
-    } elsif ($test_type eq "cid-wrong" ) {
-        # Real ECH, ignore CID, no trial decrypt
-        @s_client_cmd = ("s_client",
-                         "-connect", "localhost:$s_server_port",
-                         "-servername", "server.example",
-                         "-CAfile", $root_pem,
-                         "-ech_config_list", $good_b64,
-                         "-ech_ignore_cid",
-                         "-prexit");
-    } else {
-        # Real ECH, and default
-        @s_client_cmd = ("s_client",
-                         "-connect", "localhost:$s_server_port",
-                         "-servername", "server.example",
-                         "-CAfile", $root_pem,
-                         "-ech_config_list", $good_b64,
-                         "-prexit");
-    }
-    print("@s_client_cmd\n");
-    local (*sc_input);
-    my $s_client_pid = open3(*sc_input, my $s_client_o,
-                             my $s_client_e = gensym,
-                             $shlib_wrap, $apps_openssl, @s_client_cmd);
-    print sc_input "Q\n";
-    close(sc_input);
-    waitpid($s_client_pid, 0);
-    # the output from s_client that we want to check is written to its
-    # stdout, e.g: "^ECH: success, yay!"
-    $s_client_match = 0;
-    while (<$s_client_o>) {
-        print($_);
-        chomp;
-        if (/$winpattern/) {
-            $s_client_match = 1;
-            last;
+        print("@s_server_cmd\n");
+        $s_server_pid = open3(my $s_server_i, my $s_server_o,
+                                 my $s_server_e,
+                                 $shlib_wrap, $apps_openssl, @s_server_cmd);
+        # we're looking for...
+        # ACCEPT 0.0.0.0:45921
+        # ACCEPT [::]:45921
+        $s_server_port = "0";
+        while (<$s_server_o>) {
+            print($_);
+            chomp;
+            if (/^ACCEPT 0.0.0.0:(\d+)/) {
+                $s_server_port = $1;
+                last;
+            } elsif (/^ACCEPT \[::\]:(\d+)/) {
+                $s_server_port = $1;
+                last;
+            } elsif (/^Using default/) {
+                ;
+            } elsif (/^Added (\d+) ECH/) {
+                ;
+            } elsif (/^Added ECH key pair/) {
+                ;
+            } elsif (/^Loaded/) {
+                ;
+            } elsif (/^Setting secondary/) {
+                ;
+            } else {
+                last;
+            }
+        }
+        # openssl s_client -connect localhost:NNNNN
+        #                  -servername server.example
+        #                  -CAfile test/certs/rootcert.pem
+        #                  -ech_config_list "ADn+...AA="
+        #                  -prexit
+        my @s_client_cmd;
+        if ($test_type eq "GREASE-suite" ) {
+            # GREASE with suite
+            @s_client_cmd = ("s_client",
+                             "-connect", "localhost:$s_server_port",
+                             "-servername", "server.example",
+                             "-CAfile", $root_pem,
+                             "-ech_grease_suite", "0x21,2,3",
+                             "-prexit");
+         } elsif ($test_type eq "bad-GREASE-suite" ) {
+            # bad GREASE suite
+            @s_client_cmd = ("s_client",
+                             "-connect", "localhost:$s_server_port",
+                             "-servername", "server.example",
+                             "-CAfile", $root_pem,
+                             "-ech_grease_suite", "thisisnotagoodone",
+                             "-prexit");
+        } elsif ($test_type eq "lots-of-options" ) {
+            # real ECH with lots of options
+            @s_client_cmd = ("s_client",
+                             "-connect", "localhost:$s_server_port",
+                             "-servername", "server.example",
+                             "-CAfile", $root_pem,
+                             "-ech_config_list", $good_b64,
+                             "-ech_outer_sni", "foodle.doodle",
+                             "-ech_select", "0",
+                             "-alpn", "http/1.1",
+                             "-ech_outer_alpn", "http451",
+                             "-prexit");
+        } elsif ($test_type eq "GREASE-type" ) {
+            # GREASE with type
+            @s_client_cmd = ("s_client",
+                             "-connect", "localhost:$s_server_port",
+                             "-servername", "server.example",
+                             "-CAfile", $root_pem,
+                             "-ech_grease_type", "12345",
+                             "-prexit");
+        } elsif ($test_type eq "GREASE" ) {
+            # GREASE with suite
+            @s_client_cmd = ("s_client",
+                             "-connect", "localhost:$s_server_port",
+                             "-servername", "server.example",
+                             "-CAfile", $root_pem,
+                             "-ech_grease",
+                             "-prexit");
+        } elsif ($test_type eq "no-outer" ) {
+            # Real ECH, no outer SNI
+            @s_client_cmd = ("s_client",
+                             "-connect", "localhost:$s_server_port",
+                             "-servername", "server.example",
+                             "-CAfile", $root_pem,
+                             "-ech_config_list", $good_b64,
+                             "-ech_no_outer_sni",
+                             "-prexit");
+        } elsif ($test_type eq "bad-ech" ) {
+            # bad ECH
+            @s_client_cmd = ("s_client",
+                             "-connect", "localhost:$s_server_port",
+                             "-servername", "server.example",
+                             "-CAfile", $root_pem,
+                             "-ech_config_list", "AEH+DQA91wAgACCBdNrnZxqNrUXSyimqqnfmNG4lHtVsbmaaIeRoUoFWFQAEAAEAAQAOc2VydmVyLmV4YW1wbGUAAA==",
+                             "-prexit");
+        } elsif ($test_type eq "cid-free" ) {
+            # Real ECH, ignore CID
+            @s_client_cmd = ("s_client",
+                             "-connect", "localhost:$s_server_port",
+                             "-servername", "server.example",
+                             "-CAfile", $root_pem,
+                             "-ech_config_list", $good_b64,
+                             "-ech_ignore_cid",
+                             "-prexit");
+        } elsif ($test_type eq "cid-wrong" ) {
+            # Real ECH, ignore CID, no trial decrypt
+            @s_client_cmd = ("s_client",
+                             "-connect", "localhost:$s_server_port",
+                             "-servername", "server.example",
+                             "-CAfile", $root_pem,
+                             "-ech_config_list", $good_b64,
+                             "-ech_ignore_cid",
+                             "-prexit");
+        } else {
+            # Real ECH, and default
+            @s_client_cmd = ("s_client",
+                             "-connect", "localhost:$s_server_port",
+                             "-servername", "server.example",
+                             "-CAfile", $root_pem,
+                             "-ech_config_list", $good_b64,
+                             "-prexit");
+        }
+        print("@s_client_cmd\n");
+        local (*sc_input);
+        my $s_client_pid = open3(*sc_input, my $s_client_o,
+                                 my $s_client_e,
+                                 $shlib_wrap, $apps_openssl, @s_client_cmd);
+        print sc_input "Q\n";
+        close(sc_input);
+        waitpid($s_client_pid, 0);
+        my $stillthere = kill 0, $s_server_pid;
+        if ($stillthere) {
+           print("s_server process ($s_server_pid) is not dead yet.\n");
+           kill 'HUP', $s_server_pid;
+        }
+        # the output from s_client that we want to check is written to its
+        # stdout, e.g: "^ECH: success, yay!"
+        $s_client_match = 0;
+        while (<$s_client_o>) {
+            print($_);
+            chomp;
+            if (/$winpattern/) {
+                $s_client_match = 1;
+                last;
+            }
+        }
+
+        alarm 0;
+    };
+    if ($@) {
+        if ($@ eq "timeout\n") {
+            print("TIMEOUT: test timed out after ${timeout}s\n");
+            kill 'KILL', $s_server_pid
+                if $s_server_pid && kill(0, $s_server_pid);
+        } else {
+            die $@;
         }
-    }
-    my $stillthere = kill 0, $s_server_pid;
-    if ($stillthere) {
-       print("s_server process ($s_server_pid) is not dead yet.\n");
-       kill 'HUP', $s_server_pid;
     }
 }