@@ -28,8 +28,9 @@ use std::io;
28
28
use std:: io:: Read ;
29
29
use std:: fs:: { File , OpenOptions } ;
30
30
use std:: os:: unix:: fs:: OpenOptionsExt ;
31
- use std:: sync:: atomic:: { AtomicBool , ATOMIC_BOOL_INIT , Ordering } ;
31
+ use std:: sync:: atomic:: { AtomicBool , ATOMIC_BOOL_INIT , Ordering , AtomicUsize } ;
32
32
use std:: cmp;
33
+ use std:: mem;
33
34
34
35
#[ derive( Clone , Debug ) ]
35
36
pub struct OsRng {
@@ -97,9 +98,10 @@ impl OsRngImpl for OsRng {
97
98
}
98
99
99
100
fn max_chunk_size ( & self ) -> usize {
100
- // The documentation says 1024 is the maximum for getrandom, but
101
- // 1040 for /dev/random.
102
- 1024
101
+ // This is the largest size that's guaranteed to not block across
102
+ // all the Solarish platforms, though some may allow for larger
103
+ // sizes.
104
+ 256
103
105
}
104
106
105
107
fn method_str ( & self ) -> & ' static str {
@@ -110,18 +112,48 @@ impl OsRngImpl for OsRng {
110
112
}
111
113
}
112
114
113
- fn getrandom ( buf : & mut [ u8 ] , blocking : bool ) -> libc:: c_long {
114
- extern "C" {
115
- fn syscall ( number : libc:: c_long , ...) -> libc:: c_long ;
115
+ #[ cfg( target_os = "illumos" ) ]
116
+ type GetRandomFn = unsafe extern fn ( * mut u8 , libc:: size_t , libc:: c_uint )
117
+ -> libc:: ssize_t ;
118
+ #[ cfg( target_os = "solaris" ) ]
119
+ type GetRandomFn = unsafe extern fn ( * mut u8 , libc:: size_t , libc:: c_uint )
120
+ -> libc:: c_int ;
121
+
122
+ // Use dlsym to determine if getrandom(2) is present in libc. On Solarish
123
+ // systems, the syscall interface is not stable and can change between
124
+ // updates. Even worse, issuing unsupported syscalls will cause the system
125
+ // to generate a SIGSYS signal (usually terminating the program).
126
+ // Instead the stable APIs are exposed via libc. Cache the result of the
127
+ // lookup for future calls. This is loosely modeled after the
128
+ // libstd::sys::unix::weak macro which unfortunately is not exported.
129
+ fn fetch ( ) -> Option < GetRandomFn > {
130
+ static FPTR : AtomicUsize = AtomicUsize :: new ( 1 ) ;
131
+
132
+ if FPTR . load ( Ordering :: SeqCst ) == 1 {
133
+ let name = "getrandom\0 " ;
134
+ let addr = unsafe {
135
+ libc:: dlsym ( libc:: RTLD_DEFAULT , name. as_ptr ( ) as * const _ ) as usize
136
+ } ;
137
+ FPTR . store ( addr, Ordering :: SeqCst ) ;
116
138
}
117
139
118
- const SYS_GETRANDOM : libc:: c_long = 143 ;
140
+ let ptr = FPTR . load ( Ordering :: SeqCst ) ;
141
+ unsafe {
142
+ mem:: transmute :: < usize , Option < GetRandomFn > > ( ptr)
143
+ }
144
+ }
145
+
146
+ fn getrandom ( buf : & mut [ u8 ] , blocking : bool ) -> libc:: ssize_t {
119
147
const GRND_NONBLOCK : libc:: c_uint = 0x0001 ;
120
148
const GRND_RANDOM : libc:: c_uint = 0x0002 ;
121
149
122
- unsafe {
123
- syscall ( SYS_GETRANDOM , buf. as_mut_ptr ( ) , buf. len ( ) ,
124
- if blocking { 0 } else { GRND_NONBLOCK } | GRND_RANDOM )
150
+ if let Some ( rand) = fetch ( ) {
151
+ let flag = if blocking { 0 } else { GRND_NONBLOCK } | GRND_RANDOM ;
152
+ unsafe {
153
+ rand ( buf. as_mut_ptr ( ) , buf. len ( ) , flag) as libc:: ssize_t
154
+ }
155
+ } else {
156
+ -1
125
157
}
126
158
}
127
159
@@ -143,33 +175,18 @@ fn getrandom_try_fill(dest: &mut [u8], blocking: bool) -> Result<(), Error> {
143
175
err,
144
176
) ) ;
145
177
}
146
- } else if result != dest. len ( ) as i64 {
178
+ } else if result != dest. len ( ) as libc :: ssize_t {
147
179
return Err ( Error :: new ( ErrorKind :: Unavailable ,
148
180
"unexpected getrandom error" ) ) ;
149
181
}
150
182
Ok ( ( ) )
151
183
}
152
184
153
185
fn is_getrandom_available ( ) -> bool {
154
- use std:: sync:: atomic:: { AtomicBool , ATOMIC_BOOL_INIT , Ordering } ;
155
- use std:: sync:: { Once , ONCE_INIT } ;
156
-
157
- static CHECKER : Once = ONCE_INIT ;
158
- static AVAILABLE : AtomicBool = ATOMIC_BOOL_INIT ;
159
-
160
- CHECKER . call_once ( || {
161
- debug ! ( "OsRng: testing getrandom" ) ;
162
- let mut buf: [ u8 ; 0 ] = [ ] ;
163
- let result = getrandom ( & mut buf, false ) ;
164
- let available = if result == -1 {
165
- let err = io:: Error :: last_os_error ( ) . raw_os_error ( ) ;
166
- err != Some ( libc:: ENOSYS )
167
- } else {
168
- true
169
- } ;
170
- AVAILABLE . store ( available, Ordering :: Relaxed ) ;
171
- info ! ( "OsRng: using {}" , if available { "getrandom" } else { "/dev/random" } ) ;
172
- } ) ;
173
-
174
- AVAILABLE . load ( Ordering :: Relaxed )
186
+ let available = match fetch ( ) {
187
+ Some ( _) => true ,
188
+ None => false ,
189
+ } ;
190
+ info ! ( "OsRng: using {}" , if available { "getrandom" } else { "/dev/random" } ) ;
191
+ available
175
192
}
0 commit comments