Replace the rand() which reads from /dev/random with a Mersenne PRNG.

Much better now.

Also, it is seeded by reading from /dev/random at startup. Not sure if that's the best idea, but we'll see.
This commit is contained in:
apio 2022-10-15 16:46:54 +02:00
parent 18f1f8b7ca
commit 45afd3e243
4 changed files with 78 additions and 40 deletions

View File

@ -117,25 +117,7 @@ int main()
if (fclose(config) < 0) { perror("fclose"); } if (fclose(config) < 0) { perror("fclose"); }
FILE* fp = fopen("/dev/random", "rw"); printf("\n\nGot random number %d\n\n", rand());
if (!fp)
{
perror("fopen");
return 1;
}
long int random = 0;
fread(&random, sizeof(random), 1, fp);
if (ferror(fp))
{
perror("fread");
return 1;
}
fclose(fp);
printf("\n\nGot random number %ld\n\n", random);
sleep(2); sleep(2);

View File

@ -53,7 +53,7 @@ extern "C"
/* Returns a random number. */ /* Returns a random number. */
int rand(void); int rand(void);
/* Does nothing. */ /* Seeds the random number generator with the specified seed. */
void srand(unsigned int seed); void srand(unsigned int seed);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -10,6 +10,33 @@ static void terminate_libc()
fclose(stderr); fclose(stderr);
} }
static void initialize_random()
{
unsigned int seed = 3735928559U;
FILE* fp = fopen("/dev/random", "rw");
if (!fp)
{
errno = 0;
goto failed_to_read_dev_random;
}
fread(&seed, sizeof(seed), 1, fp);
if (ferror(fp))
{
errno = 0;
fclose(fp);
goto failed_to_read_dev_random;
}
fclose(fp);
failed_to_read_dev_random:
srand(seed); // If we failed, this will be seeded with a known value. Else, it will be seeded with a random value
// from the kernel.
return;
}
static void check_for_file(int fd, FILE** target_stream, const char* path, const char* mode) static void check_for_file(int fd, FILE** target_stream, const char* path, const char* mode)
{ {
if (lseek(fd, 0, SEEK_CUR) < 0) if (lseek(fd, 0, SEEK_CUR) < 0)
@ -27,5 +54,7 @@ extern "C" void initialize_libc()
{ {
check_for_file(STDOUT_FILENO, &stdout, "/dev/console", "rw"); check_for_file(STDOUT_FILENO, &stdout, "/dev/console", "rw");
check_for_file(STDERR_FILENO, &stderr, "/dev/console", "rw"); check_for_file(STDERR_FILENO, &stderr, "/dev/console", "rw");
initialize_random();
atexit(terminate_libc); atexit(terminate_libc);
} }

View File

@ -1,37 +1,64 @@
#include <assert.h> #include <assert.h>
#include <stdio.h> #include <stddef.h>
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
static FILE* randfp = nullptr; typedef uint32_t word_t;
#define STATE_SIZE 624
#define MIDDLE 397
#define INIT_SHIFT 30
#define INIT_FACT 1812433253
#define TWIST_MASK 0x9908b0df
#define SHIFT1 11
#define MASK1 0xffffffff
#define SHIFT2 7
#define MASK2 0x9d2c5680
#define SHIFT3 15
#define MASK3 0xefc60000
#define SHIFT4 18
void close_randfp() #define LOWER_MASK 0x7fffffff
#define UPPER_MASK (~(word_t)LOWER_MASK)
static word_t state[STATE_SIZE];
static size_t index = STATE_SIZE + 1;
static void twist()
{ {
fclose(randfp); for (size_t i = 0; i < STATE_SIZE; i++)
{
word_t x = (state[i] & UPPER_MASK) | (state[(i + 1) % STATE_SIZE] & LOWER_MASK);
x = (x >> 1) ^ (x & 1 ? TWIST_MASK : 0);
state[i] = state[(i + MIDDLE) % STATE_SIZE] ^ x;
}
index = 0;
} }
extern "C" extern "C"
{ {
int rand() // FIXME: This function is a dirty and extremely hacky way to get going, /dev/random should only be used int rand()
// for initial seeding, userspace should do their own random calculations.
{ {
if (!randfp) if (index >= STATE_SIZE)
{ {
randfp = fopen("/dev/random", "rw"); assert(index == STATE_SIZE && "Mersenne generator was never seeded");
assert(randfp != nullptr); twist();
atexit(close_randfp);
} }
int result;
fread(&result, sizeof(result), 1, randfp); word_t y = state[index];
if (ferror(randfp)) y ^= (y >> SHIFT1) & MASK1;
{ y ^= (y << SHIFT2) & MASK2;
perror("read(randfp)"); y ^= (y << SHIFT3) & MASK3;
abort(); y ^= y >> SHIFT4;
}
return result; index++;
return y;
} }
void srand(unsigned int) void srand(unsigned int seed)
{ {
return; index = STATE_SIZE;
state[0] = seed;
for (size_t i = 1; i < STATE_SIZE; i++)
state[i] = (word_t)((INIT_FACT * (state[i - 1] ^ (state[i - 1] >> INIT_SHIFT))) + i);
} }
} }