[m-rev.] for review: add a system RNG implementation for the C backends on Linux
Julien Fischer
jfischer at opturion.com
Mon Feb 1 15:56:01 AEDT 2021
Hi Peter,
On Mon, 1 Feb 2021, Peter Wang wrote:
>> diff --git a/library/random.system_rng.m b/library/random.system_rng.m
>> index 872058c..affd2c4 100644
>> --- a/library/random.system_rng.m
>> +++ b/library/random.system_rng.m
>> @@ -84,16 +84,19 @@
>>
>> :- import_module bool.
>> :- import_module exception.
>> +:- import_module list.
>> +:- import_module string.
>>
>> %---------------------------------------------------------------------------%
>>
>> -:- pragma foreign_type("Java", system_rng,
>> - "java.security.SecureRandom").
>> +:- pragma foreign_decl("C", "#include \"mercury_random.h\"").
>> +
>
> IMO the C code doesn't need to be in the runtime directory.
Ok, I've shifted into the library.
>> diff --git a/runtime/mercury_random.c b/runtime/mercury_random.c
>> index e69de29..85a1025 100644
>> --- a/runtime/mercury_random.c
>> +++ b/runtime/mercury_random.c
>> @@ -0,0 +1,214 @@
>> +// vim: ts=4 sw=4 expandtab ft=c
>> +
>> +// Copyright (C) 2021 The Mercury team.
>> +// This file is distributed under the terms specified in COPYING.LIB.
>> +
>> +// mercury_random.c
>> +
>
> There are a few stray space characters at EOLs in this file.
Fixed.
>> +#if defined(MR_SYSRAND_IMPL_URANDOM)
>> +
>> +static MR_Bool
>> +read_bytes_from_urandom(int fd, void *buffer, size_t n,
>> + MR_String *err_msg);
>
> It's not specific to urandom to maybe rename it to read_bytes
> or read_bytes_from_fd?
After changes due to comments below it no longer exists.
>> +
>> +MR_SystemRandomHandle
>> +MR_random_open(MR_Bool *succeeded, MR_String *err_msg)
>> +{
>> + int fd;
>> + char errbuf[MR_STRERROR_BUF_SIZE];
>> + MR_SystemRandomHandle handle;
>> +
>> + do {
>> + fd = open("/dev/urandom", O_RDONLY);
>> + } while (fd == -1 && MR_is_eintr(errno));
>> +
>> + if (fd == -1) {
>> + MR_strerror(errno, errbuf, sizeof(errbuf));
>
> You must use the return value of MR_strerror() as it may not
> fill the given buffer.
Fixed.
>> + MR_save_transient_hp();
>> + MR_make_aligned_string_copy(*err_msg, errbuf);
>> + MR_restore_transient_hp();
>> + *succeeded = MR_NO;
>> + handle = NULL;
>> + } else {
>> + handle =
>> + MR_GC_malloc(sizeof(struct MR_SystemRandomHandle_Struct));
>
> Could use MR_GC_NEW.
Done.
>> + handle->MR_srh_fd = fd;
>> + *err_msg = MR_make_string_const("");
>> + *succeeded = MR_YES;
>> + }
>> +
>> + return handle;
>> +}
>> +
>> +MR_Bool
>> +MR_random_close(MR_SystemRandomHandle handle, MR_String *err_msg)
>> +{
>> + char errbuf[MR_STRERROR_BUF_SIZE];
>> +
>> + if (close(handle->MR_srh_fd) == -1) {
>> + MR_strerror(errno, errbuf, sizeof(errbuf));
>
> As above.
Fixed.
>> + MR_save_transient_hp();
>> + MR_make_aligned_string_copy(*err_msg, errbuf);
>> + MR_restore_transient_hp();
>> + return MR_NO;
>> + } else {
>> + handle->MR_srh_fd = -1;
>> + *err_msg = MR_make_string_const("");
>> + return MR_YES;
>> + }
>> +}
>> +
>
>> +uint8_t
>> +MR_random_generate_uint8(MR_SystemRandomHandle handle,
>> + MR_Bool *succeeded, MR_String *err_msg)
>> +{
>> + unsigned char buffer[1];
>> + if (read_bytes_from_urandom(handle->MR_srh_fd, buffer, 1, err_msg)) {
>> + *succeeded = MR_TRUE;
>> + return (uint8_t) buffer[0];
>> + } else {
>> + *succeeded = MR_FALSE;
>> + return 0;
>> + }
>> +}
>
> Why not have a single function which takes a buffer and length?
> The caller can use a union type consisting of the uintN_t and the
> unsigned char buffer:
>
> union {
> uint16_t x;
> unsigned char buf[2];
> } u;
> succeeded =
> MR_random_generate_bytes(handle, u.buf, sizeof(u.buf), &err_msg);
Good idea, I have done so.
>
>> +MR_Bool
>> +read_bytes_from_urandom(int fd, void *buffer, size_t n,
>> + MR_String *err_msg)
>> +{
>> + int i;
>> + char errbuf[MR_STRERROR_BUF_SIZE];
>> +
>> + for (i = 0; i < n; ) {
>> + size_t to_read = n - i;
>> + ssize_t bytes_read = read(fd, buffer, to_read);
>> + if (bytes_read == -1) {
>> + if (MR_is_eintr(errno)) {
>> + continue;
>> + } else {
>> + MR_strerror(errno, errbuf, sizeof(errbuf));
>
> As above.
Fixed.
>> + MR_save_transient_hp();
>> + MR_make_aligned_string_copy(*err_msg, errbuf);
>> + MR_restore_transient_hp();
>> + return MR_FALSE;
>> + }
>> + }
>> + i += bytes_read;
>> + }
>> +
>> + *err_msg = MR_make_string_const("");
>> + return MR_TRUE;
>> +}
>> +
>> +#else // MR_SYSRAND_IMPL_NONE
>
>> diff --git a/runtime/mercury_random.h b/runtime/mercury_random.h
>> index e69de29..8e29c78 100644
>> --- a/runtime/mercury_random.h
>> +++ b/runtime/mercury_random.h
>> @@ -0,0 +1,97 @@
>> +// vim: ts=4 sw=4 expandtab ft=c
>> +
>> +// Copyright (C) 2021 The Mercury team.
>> +// This file is distributed under the terms specified in COPYING.LIB.
>> +
>> +// mercury_random.h - code for interacting with the system RNG.
>> +
>> +#ifndef MR_MERCURY_RANDOM_H
>> +#define MERCURY_RANDOM_H
>> +
>> +#include "mercury_conf_param.h"
>> +#include "mercury_types.h"
>> +#include "mercury_windows.h"
>> +
>> +#include <stdint.h>
>> +
>> +////////////////////////////////////////////////////////////////////////////
>> +//
>> +// The following macros define if the system random number exists on this
>> +// system and, if so, how it is accessed.
>> +//
>> +// Only one of the following must be defined.
>> +//
>> +// MR_SYSRAND_IMPL_ARC4RANDOM (NYI)
>> +// the system RNG is implemented by calling the arc4random() family of
>> +// functions. Note: this for when arc4random() is provided by libc (as on
>> +// macOS and the BSDs), not for when it is provided as a separate library
>> +// (e.g. libbsd on Linux).
>> +//
>> +// MR_SYSRAND_IMPL_RAND_S (NYI)
>> +// the system RNG is implemented by calling the rand_s() function
>> +// (Windows only).
>> +//
>> +// MR_SYSRAND_IMPL_GETRANDOM (NYI)
>> +// the system RNG is implemented by calling getrandom() (sufficiently
>> +// recent Linux kernels only).
>> +//
>> +// MR_SYSRAND_IMPL_URANDOM
>> +// the system RNG is implemented by reading from /dev/urandom.
>> +// (XXX currently only tested, and enabled, on Linux.)
>> +//
>> +// MR_SYSRAND_IMPL_NONE
>> +// there is no system RNG is not available on this platform.
>> +
>> +#if defined(__linux__)
>> + #define MR_SYSRAND_IMPL_URANDOM
>> +#else
>> + #define MR_SYSRAND_IMPL_NONE
>> +#endif
>
> I've used /dev/urandom on Linux, FreeBSD, OpenBSD, MacOS X and AIX,
> and Solaris and NetBSD also have it, so you can enable
> MR_SYSRAND_IMPL_URANDOM on anything but Windows.
/dev/urandom should only be used when nothing better is available;
macOS and the BSDs should all use arc4random().
I don't have access to a Solaris or AIX system to test on.
Modified log message and diff below.
Thanks for the review.
Julien.
------------------------------
Add a system RNG implementation for the C backends on Linux.
Add a system RNG implementation for the C backends on Linux that works by
reading random bits from /dev/urandom. We will eventually provide a version
that reads from getrandom() on Linux systems that provide that; this
implementation is a fallback for those system that do not. (It may also
be what we use on other Unix-like system that do not provide anything
better.)
library/random.system_rng.m:
Add two implementations of the system RNG for the C backends, one that
reads from /dev/urandom and one that just aborts. The former is used
on Linux and the latter is used everywhere else (for now).
diff --git a/library/random.system_rng.m b/library/random.system_rng.m
index c14aea8..95c6b3c 100644
--- a/library/random.system_rng.m
+++ b/library/random.system_rng.m
@@ -84,16 +84,112 @@
:- import_module bool.
:- import_module exception.
+:- import_module list.
+:- import_module string.
%---------------------------------------------------------------------------%
-:- pragma foreign_type("Java", system_rng,
- "java.security.SecureRandom").
+
+:- pragma foreign_decl("C", "
+
+#include \"mercury_conf_param.h\"
+#include \"mercury_memory.h\"
+#include \"mercury_misc.h\"
+#include \"mercury_runtime_util.h\"
+#include \"mercury_std.h\"
+#include \"mercury_string.h\"
+#include \"mercury_types.h\"
+#include \"mercury_windows.h\"
+
+#if defined(MR_HAVE_UNISTD_H)
+ #include <unistd.h>
+#endif
+#if defined(MR_HAVE_FCNTL_H)
+ #include <fcntl.h>
+#endif
+#if defined(MR_HAVE_SYS_STAT_H)
+ #include <sys/stat.h>
+#endif
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+// The following macros define if the system random number exists on this
+// system and, if so, how it is accessed.
+//
+// Only one of the following must be defined.
+//
+// ML_SYSRAND_IMPL_ARC4RANDOM (NYI)
+// the system RNG is implemented by calling the arc4random() family of
+// functions. Note: this for when arc4random() is provided by libc (as on
+// macOS and the BSDs), not for when it is provided as a separate library
+// (e.g. libbsd on Linux).
+//
+// ML_SYSRAND_IMPL_RAND_S (NYI)
+// the system RNG is implemented by calling the rand_s() function
+// (Windows only).
+//
+// ML_SYSRAND_IMPL_GETRANDOM (NYI)
+// the system RNG is implemented by calling getrandom() (sufficiently
+// recent Linux kernels only).
+//
+// ML_SYSRAND_IMPL_URANDOM
+// the system RNG is implemented by reading from /dev/urandom.
+// (XXX currently only tested, and enabled, on Linux.)
+//
+// ML_SYSRAND_IMPL_NONE
+// there is no system RNG is not available on this platform.
+
+#if defined(__linux__)
+ #define ML_SYSRAND_IMPL_URANDOM
+#else
+ #define ML_SYSRAND_IMPL_NONE
+#endif
+
+#if defined(ML_SYSRAND_IMPL_URANDOM)
+ struct ML_SystemRandomHandle_Struct {
+ int ML_srh_fd;
+ };
+ typedef struct ML_SystemRandomHandle_Struct *ML_SystemRandomHandle;
+#else
+ typedef MR_Unsigned ML_SystemRandomHandle;
+#endif
+
+// When succeeded is MR_TRUE, returns a handle through which the system
+// RNG can be accessed; err_msg will point to the empty string in this case.
+// When succeeded is MR_FALSE, the value return is not a valid handle and
+// err_msg will point ot a string (on the Mercury heap) describing why a handle
+// for the system RNG could not be acquired.
+//
+extern ML_SystemRandomHandle ML_random_open(MR_Bool *succeeded,
+ MR_String *err_msg);
+
+// Attempt to close the handle to the system RNG.
+// Returns MR_TRUE on success with err_msg pointing to the empty string.
+// Returns MR_FALSE on failure with err_msg pointing to a string (on the
+// Mercury heap) that describes whey the handle could not be closed.
+//
+extern MR_Bool ML_random_close(ML_SystemRandomHandle handle, MR_String *err_msg);
+
+
+// Fill buffer with len random bytes generated by the system RNG.
+// Returns MR_TRUE if len bytes were generated; err_msg will point to the empty
+// string.
+// Returns MR_FALSE if the system RNG has been unable to generate len bytes.
+// In this case the contents of buffer are indeterminate and err_msg will point
+// to a string (on the Mercury heap) that describes the problem.
+//
+extern MR_Bool ML_random_generate_bytes(ML_SystemRandomHandle handle,
+ unsigned char *buffer, size_t len, MR_String *err_msg);
+").
+
+:- pragma foreign_type("C", system_rng,
+ "ML_SystemRandomHandle", [can_pass_as_mercury_type]).
:- pragma foreign_type("C#", system_rng,
"System.Security.Cryptography.RandomNumberGenerator").
-
-:- type system_rng
- ---> system_rng.
+:- pragma foreign_type("Java", system_rng,
+ "java.security.SecureRandom").
%---------------------------------------------------------------------------%
@@ -114,6 +210,17 @@
%---------------------------------------------------------------------------%
+:- pragma foreign_proc("C",
+ have_system_rng,
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+#if defined(ML_SYSRAND_IMPL_NONE)
+ SUCCESS_INDICATOR = MR_FALSE;
+#else
+ SUCCESS_INDICATOR = MR_TRUE;
+#endif
+").
+
:- pragma foreign_proc("C#",
have_system_rng,
[will_not_call_mercury, promise_pure, thread_safe],
@@ -128,9 +235,6 @@
SUCCESS_INDICATOR = true;
").
-have_system_rng :-
- semidet_false.
-
%---------------------------------------------------------------------------%
open_system_rng(Result, !IO) :-
@@ -146,6 +250,14 @@ open_system_rng(Result, !IO) :-
:- pred do_open_system_rng(system_rng::out, bool::out, string::out,
io::di, io::uo) is det.
+:- pragma foreign_proc("C",
+ do_open_system_rng(Handle::out, IsOk::out, ErrorMsg::out,
+ _IO0::di, _IO::uo),
+ [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
+"
+ Handle = ML_random_open(&IsOk, &ErrorMsg);
+").
+
:- pragma foreign_proc("C#",
do_open_system_rng(Handle::out, IsOk::out, ErrorMsg::out,
_IO0::di, _IO::uo),
@@ -167,11 +279,6 @@ open_system_rng(Result, !IO) :-
ErrorMsg = \"\";
").
-do_open_system_rng(Handle, IsOk, ErrorMsg, !IO) :-
- Handle = system_rng,
- IsOk = no,
- ErrorMsg = "No system RNG available".
-
%---------------------------------------------------------------------------%
close_system_rng(Handle, !IO) :-
@@ -180,12 +287,20 @@ close_system_rng(Handle, !IO) :-
IsOk = yes
;
IsOk = no,
- throw(software_error(ErrorMsg))
+ throw_system_rng_error($pred, ErrorMsg)
).
:- pred do_close_system_rng(system_rng::in, bool::out, string::out,
io::di, io::uo) is det.
+:- pragma foreign_proc("C",
+ do_close_system_rng(Handle::in, IsOk::out, ErrorMsg::out,
+ _IO0::di, _IO::uo),
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+ IsOk = ML_random_close(Handle, &ErrorMsg);
+").
+
:- pragma foreign_proc("C#",
do_close_system_rng(Handle::in, IsOk::out, ErrorMsg::out,
_IO0::di, _IO::uo),
@@ -206,80 +321,191 @@ close_system_rng(Handle, !IO) :-
ErrorMsg = \"\";
").
-do_close_system_rng(_, _, _, _, _) :-
- private_builtin.sorry("No system RNG available").
-
%---------------------------------------------------------------------------%
+generate_uint8(Handle, U8, !IO) :-
+ do_generate_uint8(Handle, U8, IsOk, ErrorMsg, !IO),
+ (
+ IsOk = yes
+ ;
+ IsOk = no,
+ throw_system_rng_error($pred, ErrorMsg)
+ ).
+
+:- pred do_generate_uint8(system_rng::in, uint8::out, bool::out,
+ string::out, io::di, io::uo) is det.
+
+:- pragma foreign_proc("C",
+ do_generate_uint8(Handle::in, U8::out, IsOk::out, ErrorMsg::out,
+ _IO0::di, _IO::uo),
+ [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
+"
+ union {
+ uint8_t n;
+ unsigned char buffer[1];
+ } u;
+ IsOk = ML_random_generate_bytes(Handle, u.buffer, sizeof(u.buffer),
+ &ErrorMsg);
+ U8 = u.n;
+").
+
:- pragma foreign_proc("C#",
- generate_uint8(Handle::in, U8::out, _IO0::di, _IO::uo),
+ do_generate_uint8(Handle::in, U8::out, IsOk::out, ErrorMsg::out,
+ _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe],
"
byte[] bytes = new byte[1];
Handle.GetBytes(bytes);
U8 = bytes[0];
+ IsOk = mr_bool.YES;
+ ErrorMsg = \"\";
").
:- pragma foreign_proc("Java",
- generate_uint8(Handle::in, U8::out, _IO0::di, _IO::uo),
+ do_generate_uint8(Handle::in, U8::out, IsOk::out, ErrorMsg::out,
+ _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe],
"
byte[] bytes = new byte[1];
Handle.nextBytes(bytes);
U8 = bytes[0];
+ IsOk = bool.YES;
+ ErrorMsg = \"\";
").
-generate_uint8(_, _, !IO) :-
- private_builtin.sorry("No system RNG available").
-
%---------------------------------------------------------------------------%
+generate_uint16(Handle, U16, !IO) :-
+ do_generate_uint16(Handle, U16, IsOk, ErrorMsg, !IO),
+ (
+ IsOk = yes
+ ;
+ IsOk = no,
+ throw_system_rng_error($pred, ErrorMsg)
+ ).
+
+:- pred do_generate_uint16(system_rng::in, uint16::out, bool::out,
+ string::out, io::di, io::uo) is det.
+
+:- pragma foreign_proc("C",
+ do_generate_uint16(Handle::in, U16::out, IsOk::out, ErrorMsg::out,
+ _IO0::di, _IO::uo),
+ [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
+"
+ union {
+ uint16_t n;
+ unsigned char buffer[2];
+ } u;
+ IsOk = ML_random_generate_bytes(Handle, u.buffer, sizeof(u.buffer),
+ &ErrorMsg);
+ U16 = u.n;
+").
+
:- pragma foreign_proc("C#",
- generate_uint16(Handle::in, U16::out, _IO0::di, _IO::uo),
+ do_generate_uint16(Handle::in, U16::out, IsOk::out, ErrorMsg::out,
+ _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, not_thread_safe],
"
byte[] bytes = new byte[2];
Handle.GetBytes(bytes);
U16 = (ushort) (bytes[1] << 8 | (bytes[0] & 0x00ff));
+ IsOk = mr_bool.YES;
+ ErrorMsg = \"\";
").
:- pragma foreign_proc("Java",
- generate_uint16(Handle::in, U16::out, _IO0::di, _IO::uo),
+ do_generate_uint16(Handle::in, U16::out, IsOk::out, ErrorMsg::out,
+ _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe],
"
byte[] bytes = new byte[2];
Handle.nextBytes(bytes);
U16 = (short) (bytes[0] << java.lang.Byte.SIZE | (bytes[1] & 0x00ff));
+ IsOk = bool.YES;
+ ErrorMsg = \"\";
").
-generate_uint16(_, _, !IO) :-
- private_builtin.sorry("No system RNG available").
-
%---------------------------------------------------------------------------%
+generate_uint32(Handle, U32, !IO) :-
+ do_generate_uint32(Handle, U32, IsOk, ErrorMsg, !IO),
+ (
+ IsOk = yes
+ ;
+ IsOk = no,
+ throw_system_rng_error($pred, ErrorMsg)
+ ).
+
+:- pred do_generate_uint32(system_rng::in, uint32::out, bool::out,
+ string::out, io::di, io::uo) is det.
+
+:- pragma foreign_proc("C",
+ do_generate_uint32(Handle::in, U32::out, IsOk::out, ErrorMsg::out,
+ _IO0::di, _IO::uo),
+ [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
+"
+ union {
+ uint32_t n;
+ unsigned char buffer[4];
+ } u;
+ IsOk = ML_random_generate_bytes(Handle, u.buffer, sizeof(u.buffer),
+ &ErrorMsg);
+ U32 = u.n;
+").
+
:- pragma foreign_proc("C#",
- generate_uint32(Handle::in, U32::out, _IO0::di, _IO::uo),
+ do_generate_uint32(Handle::in, U32::out, IsOk::out, ErrorMsg::out,
+ _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, not_thread_safe],
"
byte[] bytes = new byte[4];
Handle.GetBytes(bytes);
U32 = (uint) (bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0]);
+ IsOk = mr_bool.YES;
+ ErrorMsg = \"\";
").
:- pragma foreign_proc("Java",
- generate_uint32(Handle::in, U32::out, _IO0::di, _IO::uo),
+ do_generate_uint32(Handle::in, U32::out, IsOk::out, ErrorMsg::out,
+ _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe],
"
U32 = Handle.nextInt();
+ IsOk = bool.YES;
+ ErrorMsg = \"\";
").
-generate_uint32(_, _, !IO) :-
- private_builtin.sorry("No system RNG available").
-
%---------------------------------------------------------------------------%
+generate_uint64(Handle, U64, !IO) :-
+ do_generate_uint64(Handle, U64, IsOk, ErrorMsg, !IO),
+ (
+ IsOk = yes
+ ;
+ IsOk = no,
+ throw_system_rng_error($pred, ErrorMsg)
+ ).
+
+:- pred do_generate_uint64(system_rng::in, uint64::out, bool::out,
+ string::out, io::di, io::uo) is det.
+
+:- pragma foreign_proc("C",
+ do_generate_uint64(Handle::in, U64::out, IsOk::out, ErrorMsg::out,
+ _IO0::di, _IO::uo),
+ [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
+"
+ union {
+ uint64_t n;
+ unsigned char buffer[8];
+ } u;
+ IsOk = ML_random_generate_bytes(Handle, u.buffer, sizeof(u.buffer),
+ &ErrorMsg);
+ U64 = u.n;
+").
+
:- pragma foreign_proc("C#",
- generate_uint64(Handle::in, U64::out, _IO0::di, _IO::uo),
+ do_generate_uint64(Handle::in, U64::out, IsOk::out, ErrorMsg::out,
+ _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, not_thread_safe],
"
byte[] bytes = new byte[8];
@@ -293,17 +519,136 @@ generate_uint32(_, _, !IO) :-
(ulong) bytes[2] << 16 |
(ulong) bytes[1] << 8 |
(ulong) bytes[0]);
+ IsOk = mr_bool.YES;
+ ErrorMsg = \"\";
").
:- pragma foreign_proc("Java",
- generate_uint64(Handle::in, U64::out, _IO0::di, _IO::uo),
+ do_generate_uint64(Handle::in, U64::out, IsOk::out, ErrorMsg::out,
+ _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe],
"
U64 = Handle.nextLong();
+ IsOk = bool.YES;
+ ErrorMsg = \"\";
").
-generate_uint64(_, _, !IO) :-
- private_builtin.sorry("No system RNG available").
+%---------------------------------------------------------------------------%
+
+:- pred throw_system_rng_error(string::in, string::in) is erroneous.
+
+throw_system_rng_error(Pred, Msg) :-
+ string.format("%s: %s", [s(Pred), s(Msg)], Error),
+ throw(software_error(Error)).
+
+%---------------------------------------------------------------------------%
+
+:- pragma foreign_code("C", "
+
+#if defined(ML_SYSRAND_IMPL_URANDOM)
+
+ML_SystemRandomHandle
+ML_random_open(MR_Bool *succeeded, MR_String *err_msg)
+{
+ int fd;
+ char errbuf[MR_STRERROR_BUF_SIZE];
+ const char *errno_msg;
+ ML_SystemRandomHandle handle;
+
+ do {
+ fd = open(\"/dev/urandom\", O_RDONLY);
+ } while (fd == -1 && MR_is_eintr(errno));
+
+ if (fd == -1) {
+ errno_msg = MR_strerror(errno, errbuf, sizeof(errbuf));
+ MR_save_transient_hp();
+ MR_make_aligned_string_copy(*err_msg, errno_msg);
+ MR_restore_transient_hp();
+ *succeeded = MR_NO;
+ handle = NULL;
+ } else {
+ handle = MR_GC_NEW(struct ML_SystemRandomHandle_Struct);
+ handle->ML_srh_fd = fd;
+ *err_msg = MR_make_string_const(\"\");
+ *succeeded = MR_YES;
+ }
+
+ return handle;
+}
+
+MR_Bool
+ML_random_close(ML_SystemRandomHandle handle, MR_String *err_msg)
+{
+ char errbuf[MR_STRERROR_BUF_SIZE];
+
+ if (close(handle->ML_srh_fd) == -1) {
+ MR_strerror(errno, errbuf, sizeof(errbuf));
+ MR_save_transient_hp();
+ MR_make_aligned_string_copy(*err_msg, errbuf);
+ MR_restore_transient_hp();
+ return MR_NO;
+ } else {
+ handle->ML_srh_fd = -1;
+ *err_msg = MR_make_string_const(\"\");
+ return MR_YES;
+ }
+}
+
+MR_Bool
+ML_random_generate_bytes(ML_SystemRandomHandle handle,
+ unsigned char *buffer, size_t len, MR_String *err_msg)
+{
+ int i;
+ char errbuf[MR_STRERROR_BUF_SIZE];
+ const char *errno_msg;
+
+ for (i = 0; i < len; ) {
+ size_t to_read = len - i;
+ ssize_t bytes_read = read(handle->ML_srh_fd, buffer, to_read);
+ if (bytes_read == -1) {
+ if (MR_is_eintr(errno)) {
+ continue;
+ } else {
+ errno_msg = MR_strerror(errno, errbuf, sizeof(errbuf));
+ MR_save_transient_hp();
+ MR_make_aligned_string_copy(*err_msg, errno_msg);
+ MR_restore_transient_hp();
+ return MR_FALSE;
+ }
+ }
+ i += bytes_read;
+ }
+
+ *err_msg = MR_make_string_const(\"\");
+ return MR_TRUE;
+}
+
+#else // ML_SYSRAND_IMPL_NONE
+
+ML_SystemRandomHandle
+ML_random_open(MR_Bool *succeeded, MR_String *err_msg)
+{
+ succeeded = MR_FALSE;
+ *err_msg = MR_make_string_const(\"No system RNG available\");
+ return 0; // Dummy value.
+}
+
+MR_Bool
+ML_random_close(MR_SystemRandomHandle handle, MR_String *err_msg)
+{
+ MR_fatal_error(\"ML_random_close - no system RNG available.\");
+}
+
+MR_Bool
+ML_random_generate_bytes(MR_SystemRandomHandle handle,
+ unsigned char *buffer, MR_String *err_msg)
+{
+ MR_fatal_error(\"ML_random_generate_bytes - no system RNG available.\");
+}
+
+#endif
+
+").
%---------------------------------------------------------------------------%
:- end_module random.system_rng.
More information about the reviews
mailing list