File: getrandom.t

package info (click to toggle)
libcrypt-urandom-perl 0.54-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 208 kB
  • sloc: perl: 518; makefile: 3
file content (126 lines) | stat: -rw-r--r-- 4,519 bytes parent folder | download
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();