02-test_errstr.t 5.5 KB

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