[m-rev.] for review: add a system RNG implementation for the C backends on Linux
Peter Wang
novalazy at gmail.com
Mon Feb 1 12:07:44 AEDT 2021
On Sun, 31 Jan 2021 15:15:34 +1100 Julien Fischer <jfischer at opturion.com> wrote:
>
> For review by anyone.
>
> ------------------------
>
> 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.)
>
> runtime/mercury_random.h:
> runtime/mercury_random.c:
> Add new runtime module that provides the interface to the system
> RNG for the C backends.
>
> Add two implementations of the system RNG, 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).
>
> runtime/Mmakefile:
> Add the new files.
>
> library/random.system_rng.m:
> Add C foreign procs for the predicates in this module that
> forward all their work to the new runtime module.
>
> Julien.
>
> 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.
> 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.
> +#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?
> +
> +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.
> + 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.
> + 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.
> + 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);
> +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.
> + 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.
Peter
More information about the reviews
mailing list