1 1.1 christos #! /usr/bin/env perl 2 1.1.1.2 christos # Copyright 2018-2021 The OpenSSL Project Authors. All Rights Reserved. 3 1.1 christos # 4 1.1.1.2 christos # Licensed under the Apache License 2.0 (the "License"). You may not use 5 1.1 christos # this file except in compliance with the License. You can obtain a copy 6 1.1 christos # in the file LICENSE in the source distribution or at 7 1.1 christos # https://www.openssl.org/source/license.html 8 1.1 christos 9 1.1 christos use strict; 10 1.1 christos no strict 'refs'; # To be able to use strings as function refs 11 1.1 christos use OpenSSL::Test; 12 1.1 christos use OpenSSL::Test::Utils; 13 1.1 christos use Errno qw(:POSIX); 14 1.1.1.2 christos use POSIX qw(:limits_h strerror); 15 1.1 christos 16 1.1.1.2 christos use Data::Dumper; 17 1.1 christos 18 1.1 christos setup('test_errstr'); 19 1.1 christos 20 1.1 christos # In a cross compiled situation, there are chances that our 21 1.1 christos # application is linked against different C libraries than 22 1.1 christos # perl, and may thereby get different error messages for the 23 1.1 christos # same error. 24 1.1 christos # The safest is not to test under such circumstances. 25 1.1 christos plan skip_all => 'This is unsupported for cross compiled configurations' 26 1.1 christos if config('CROSS_COMPILE'); 27 1.1 christos 28 1.1 christos # The same can be said when compiling OpenSSL with mingw configuration 29 1.1 christos # on Windows when built with msys perl. Similar problems are also observed 30 1.1 christos # in MSVC builds, depending on the perl implementation used. 31 1.1 christos plan skip_all => 'This is unsupported on MSYS/MinGW or MSWin32' 32 1.1 christos if $^O eq 'msys' or $^O eq 'MSWin32'; 33 1.1 christos 34 1.1 christos plan skip_all => 'OpenSSL is configured "no-autoerrinit" or "no-err"' 35 1.1 christos if disabled('autoerrinit') || disabled('err'); 36 1.1 christos 37 1.1.1.2 christos # OpenSSL constants found in <openssl/err.h> 38 1.1.1.2 christos use constant ERR_SYSTEM_FLAG => INT_MAX + 1; 39 1.1.1.2 christos use constant ERR_LIB_OFFSET => 23; # Offset of the "library" errcode section 40 1.1.1.2 christos 41 1.1.1.2 christos # OpenSSL "library" numbers 42 1.1.1.2 christos use constant ERR_LIB_NONE => 1; 43 1.1.1.2 christos 44 1.1.1.2 christos # We use Errno::EXPORT_OK as a list of known errno values on the current 45 1.1.1.2 christos # system. libcrypto's ERR should either use the same string as perl, or if 46 1.1.1.2 christos # it was outside the range that ERR looks at, ERR gives the reason string 47 1.1.1.2 christos # "reason(nnn)", where nnn is the errno number. 48 1.1 christos 49 1.1.1.2 christos plan tests => scalar @Errno::EXPORT_OK 50 1.1 christos +1 # Checking that error 128 gives 'reason(128)' 51 1.1 christos +1 # Checking that error 0 gives the library name 52 1.1.1.2 christos +1; # Check trailing whitespace is removed. 53 1.1.1.2 christos 54 1.1.1.2 christos # Test::More:ok() has a sub prototype, which means we need to use the '&ok' 55 1.1.1.2 christos # syntax to force it to accept a list as a series of arguments. 56 1.1.1.2 christos 57 1.1.1.2 christos foreach my $errname (@Errno::EXPORT_OK) { 58 1.1.1.2 christos # The error names are perl constants, which are implemented as functions 59 1.1.1.2 christos # returning the numeric value of that name. 60 1.1.1.2 christos my $errcode = "Errno::$errname"->(); 61 1.1.1.2 christos 62 1.1.1.2 christos SKIP: { 63 1.1.1.2 christos # On most systems, there is no E macro for errcode zero in <errno.h>, 64 1.1.1.2 christos # which means that it seldom comes up here. However, reports indicate 65 1.1.1.2 christos # that some platforms do have an E macro for errcode zero. 66 1.1.1.2 christos # With perl, errcode zero is a bit special. Perl consistently gives 67 1.1.1.2 christos # the empty string for that one, while the C strerror() may give back 68 1.1.1.2 christos # something else. The easiest way to deal with that possible mismatch 69 1.1.1.2 christos # is to skip this errcode. 70 1.1.1.2 christos skip "perl error strings and ssystem error strings for errcode 0 differ", 1 71 1.1.1.2 christos if $errcode == 0; 72 1.1.1.2 christos # On some systems (for example Hurd), there are negative error codes. 73 1.1.1.2 christos # These are currently unsupported in OpenSSL error reports. 74 1.1.1.2 christos skip "negative error codes are not supported in OpenSSL", 1 75 1.1.1.2 christos if $errcode < 0; 76 1.1.1.2 christos 77 1.1.1.2 christos &ok(match_syserr_reason($errcode)); 78 1.1.1.2 christos } 79 1.1.1.2 christos } 80 1.1.1.2 christos 81 1.1.1.2 christos # OpenSSL library 1 is the "unknown" library 82 1.1.1.2 christos &ok(match_opensslerr_reason(ERR_LIB_NONE << ERR_LIB_OFFSET | 256, 83 1.1.1.2 christos "reason(256)")); 84 1.1.1.2 christos # Reason code 0 of any library gives the library name as reason 85 1.1.1.2 christos &ok(match_opensslerr_reason(ERR_LIB_NONE << ERR_LIB_OFFSET | 0, 86 1.1.1.2 christos "unknown library")); 87 1.1.1.2 christos &ok(match_any("Trailing whitespace \n\t", "?", ( "Trailing whitespace" ))); 88 1.1.1.2 christos 89 1.1.1.2 christos exit 0; 90 1.1.1.2 christos 91 1.1.1.2 christos # For an error string "error:xxxxxxxx:lib:func:reason", this returns 92 1.1.1.2 christos # the following array: 93 1.1.1.2 christos # 94 1.1.1.2 christos # ( "xxxxxxxx", "lib", "func", "reason" ) 95 1.1.1.2 christos sub split_error { 96 1.1.1.2 christos # Limit to 5 items, in case the reason contains a colon 97 1.1.1.2 christos my @erritems = split /:/, $_[0], 5; 98 1.1 christos 99 1.1.1.2 christos # Remove the first item, which is always "error" 100 1.1.1.2 christos shift @erritems; 101 1.1 christos 102 1.1.1.2 christos return @erritems; 103 1.1.1.2 christos } 104 1.1 christos 105 1.1.1.2 christos # Compares the first argument as string to each of the arguments 3 and on, 106 1.1.1.2 christos # and returns an array of two elements: 107 1.1.1.2 christos # 0: True if the first argument matched any of the others, otherwise false 108 1.1.1.2 christos # 1: A string describing the test 109 1.1.1.2 christos # The returned array can be used as the arguments to Test::More::ok() 110 1.1.1.2 christos sub match_any { 111 1.1.1.2 christos my $first = shift; 112 1.1.1.2 christos my $desc = shift; 113 1.1.1.2 christos my @strings = @_; 114 1.1.1.2 christos 115 1.1.1.2 christos # ignore trailing whitespace 116 1.1.1.2 christos $first =~ s/\s+$//; 117 1.1.1.2 christos 118 1.1.1.2 christos if (scalar @strings > 1) { 119 1.1.1.2 christos $desc = "match '$first' ($desc) with one of ( '" 120 1.1.1.2 christos . join("', '", @strings) . "' )"; 121 1.1.1.2 christos } else { 122 1.1.1.2 christos $desc = "match '$first' ($desc) with '$strings[0]'"; 123 1.1 christos } 124 1.1.1.2 christos 125 1.1.1.2 christos return ( scalar( 126 1.1.1.2 christos grep { ref $_ eq 'Regexp' ? $first =~ $_ : $first eq $_ } 127 1.1.1.2 christos @strings 128 1.1.1.2 christos ) > 0, 129 1.1.1.2 christos $desc ); 130 1.1 christos } 131 1.1 christos 132 1.1.1.2 christos sub match_opensslerr_reason { 133 1.1.1.2 christos my $errcode = shift; 134 1.1.1.2 christos my @strings = @_; 135 1.1.1.2 christos 136 1.1.1.2 christos my $errcode_hex = sprintf "%x", $errcode; 137 1.1.1.2 christos my $reason = 138 1.1.1.2 christos ( run(app([ qw(openssl errstr), $errcode_hex ]), capture => 1) )[0]; 139 1.1.1.2 christos $reason =~ s|\R$||; 140 1.1.1.2 christos $reason = ( split_error($reason) )[3]; 141 1.1.1.2 christos 142 1.1.1.2 christos return match_any($reason, $errcode_hex, @strings); 143 1.1.1.2 christos } 144 1.1.1.2 christos 145 1.1.1.2 christos sub match_syserr_reason { 146 1.1.1.2 christos my $errcode = shift; 147 1.1.1.2 christos 148 1.1.1.2 christos my @strings = (); 149 1.1.1.2 christos # The POSIX reason string 150 1.1.1.2 christos push @strings, eval { 151 1.1.1.2 christos # Set $! to the error number... 152 1.1.1.2 christos local $! = $errcode; 153 1.1.1.2 christos # ... and $! will give you the error string back 154 1.1.1.2 christos $! 155 1.1.1.2 christos }; 156 1.1.1.2 christos # Occasionally, we get an error code that is simply not translatable 157 1.1.1.2 christos # to POSIX semantics on VMS, and we get an error string saying so. 158 1.1.1.2 christos push @strings, qr/^non-translatable vms error code:/ if $^O eq 'VMS'; 159 1.1.1.2 christos # The OpenSSL fallback string 160 1.1.1.2 christos push @strings, "reason($errcode)"; 161 1.1.1.2 christos 162 1.1.1.2 christos return match_opensslerr_reason(ERR_SYSTEM_FLAG | $errcode, @strings); 163 1.1.1.2 christos } 164