1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
|
use Test::More;
use FileHandle();
use POSIX();
use Encode();
use Config;
use strict;
use warnings;
my %optional;
if ($^O eq 'MSWin32') {
} else {
eval `cat ./check_random.inc`;
if ($optional{DEFINE}) {
diag("check_random.inc produced the flag of $optional{DEFINE}");
}
}
SKIP: {
if ($^O eq 'linux') { # LD_PRELOAD trick works here
require Crypt::URandom;
my $can_load_getrandom = !!(eval 'defined Crypt::URandom::getrandom(1)');
if (!$can_load_getrandom) {
chomp $@;
skip("Cannot load getrandom in $^O':$@", 1);
} elsif (($optional{DEFINE}) && ($optional{DEFINE} eq '-DHAVE_CRYPT_URANDOM_NATIVE_GETRANDOM')) {
my $is_covering = !!(eval 'Devel::Cover::get_coverage()');
my @extra_args = $is_covering ? ('-MDevel::Cover') : ();
my $correct_length = 27;
my $failed_number_of_bytes = 13;
my $error_number = POSIX::EINTR() + 0;
my $c_path = 'getrandom.c';
unlink $c_path or ($! == POSIX::ENOENT()) or die "Failed to unlink $c_path:$!";
my $c_handle = FileHandle->new($c_path, Fcntl::O_CREAT() | Fcntl::O_WRONLY() | Fcntl::O_EXCL()) or die "Failed to open $c_path for writing:$!";
print $c_handle <<"_OUT_";
#include <stddef.h>
#include <sys/types.h>
#include <errno.h>
int count = 0;
ssize_t getrandom(void *buf, size_t buflen, unsigned int flags) {
count = count + 1;
if (count <= 2) {
errno = $error_number;
return $failed_number_of_bytes;
} else {
errno = 0;
return buflen;
}
}
_OUT_
my $binary_path = './getrandom.so';
my $result = system { $Config{cc} } $Config{cc}, $Config{cccdlflags}, '-shared', '-o', $binary_path, $c_path;
ok($result == 0, "Compiled a LD_PRELOAD binary at $binary_path:$?");
my $handle = FileHandle->new();
if (my $pid = $handle->open(q[-|])) {
my $result = $handle->read(my $line, $correct_length + 5);
chomp $line;
my $actual_length = length $line;
ok($actual_length == $correct_length, "getrandom hit with INT signal after $failed_number_of_bytes bytes recovers to produce correct length of $correct_length bytes:$actual_length");
waitpid $pid, 0;
ok($? == 0, "Successfully processed getrandom");
} elsif (defined $pid) {
local $ENV{LD_PRELOAD} = $binary_path;
eval {
exec { $^X } $^X, (map { "-I$_" } @INC), @extra_args, '-MCrypt::URandom', '-e', 'my $data = Crypt::URandom::getrandom(' . $correct_length . '); print "$data\n"; exit 0;' or die "Failed to exec $^X:$!";
} or do {
warn "$@";
};
exit 1;
} else {
die "Failed to fork:$!";
}
unlink $c_path or die "Failed to unlink $c_path:$!";
unlink $binary_path or die "Failed to unlink $binary_path:$!";;
$failed_number_of_bytes = -1;
$error_number = POSIX::EAGAIN() + 0;
$c_handle = FileHandle->new($c_path, Fcntl::O_CREAT() | Fcntl::O_WRONLY() | Fcntl::O_EXCL()) or die "Failed to open $c_path for writing:$!";
print $c_handle <<"_OUT_";
#include <stddef.h>
#include <sys/types.h>
#include <errno.h>
ssize_t getrandom(void *buf, size_t buflen, unsigned int flags) {
errno = $error_number;
return $failed_number_of_bytes;
}
_OUT_
$result = system { $Config{cc} } $Config{cc}, $Config{cccdlflags}, '-shared', '-o', $binary_path, $c_path;
ok($result == 0, "Compiled a LD_PRELOAD binary at $binary_path:$!");
$handle = FileHandle->new();
if (my $pid = $handle->open(q[-|])) {
my $result = $handle->read(my $line, 4000);
chomp $line;
my ($actual_error, $entire_message) = split /\t/smx, $line;
chomp $entire_message;
$! = POSIX::EAGAIN();
my $correct_error = "$!";
ok($actual_error eq $correct_error, "Correct error caught:'$actual_error' vs '$correct_error'");
diag("EAGAIN looks like '$entire_message'");
waitpid $pid, 0;
ok($? == 0, "Successfully caught exception for broken getrandom:$?");
} elsif (defined $pid) {
local $ENV{LD_PRELOAD} = $binary_path;
eval {
exec { $^X } $^X, (map { "-I$_" } @INC), @extra_args, '-MEncode', '-MCrypt::URandom', '-e', 'eval { Crypt::URandom::getrandom(28); } or do { print Encode::encode("UTF-8", "$!\t$@\n"); exit 0 }; exit 1;' or die "Failed to exec $^X:$!";
} or do {
warn "$@";
};
exit 1;
} else {
die "Failed to fork:$!";
}
unlink $c_path or die "Failed to unlink $c_path:$!";
unlink $binary_path or die "Failed to unlink $binary_path:$!";;
} else {
skip("Not sure about alternative function signatures for $optional{DEFINE}", 1);
}
} else {
skip("Not sure about LD_PRELOAD support in $^O", 1);
}
}
done_testing();
|