Initial import of libdpf.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ryan Henry 2026-09-24 14:08:32 -06:00
commit e4e666f459
4563 changed files with 1690372 additions and 0 deletions

View file

@ -0,0 +1,84 @@
# Random Numbers
This provides methods for obtaining random, or pseudo-random, numbers.
There is generally only on function to worry about:
```c
int
psnip_random_bytes(enum PSnipRandomSource source,
size_t length,
psnip_uint8_t data[length]);
```
This will fill `data` with `length` bytes of randomness from `source`.
It returns 0 on success or, -1 if the source is not available (only
possible with the secure source), -2 if you supplied an invalid source
argument, or another negative number for other unexpected errors.
## Secure
`PSNIP_RANDOM_SOURCE_SECURE` tries to obtain cryptographically secure
random numbers. This may or may not be available, depending on the
platform; if no source can be found `psnip_random_get_bytes` will
return -1 (you can pass 0 for the length if you just want to query for
support without actually generating anything).
This will try various methods. Currently, this means:
* [`RtlGenRandom()`](http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694%28v=vs.85%29.aspx) on Windows
* `getrandom()` on Solaris >= 11.3 (**TODO**), Linux >= 3.17
* `arc4random()` on some BSDs (**TODO**)
* [AES-NI](https://software.intel.com/en-us/articles/intel-advanced-encryption-standard-aes-instructions-set/) on CPUs which support it
* Reading from `/dev/urandom` or `/dev/random` (if they exist)
If your platform isn't supported, please get in touch to discuss
adding a back-end.
## Reproducible
`PSNIP_RANDOM_SOURCE_REPRODUCIBLE` generates a *reproducible* stream
of pseudo-random numbers. Currently we use a 32-bit variant of PCG;
this was chosen because it provides pretty good quality random
numbers, and the state is small enough that we can use atomic
operations instead of a mutex.
To deal with seeding, there are two additional functions:
```c
psnip_uint32_t psnip_random_get_seed (void);
void psnip_random_set_seed (const psnip_uint32_t seed);
```
You can use the `psnip_random_set_seed` function to seed this
generator. If you do not provide a seed we'll try to generate a
decent seed for you, but we make no promises about the quality.
Potential sources for the seed include libc's `rand()`, system time,
`lrand48()`, etc.
Even with a high quality seed, **this is not cryptographically secure**.
## Fast
`PSNIP_RANDOM_SOURCE_FAST` just tries to provide a random number
quickly. It probably isn't cryptographically secure, but may be; for
example, if you have a hardware-based RNG which we believe would be
faster than the reproducible PRNG, we'll use it.
Note that, even if the fast source is implemented using the same
algorithm as the reproducible source (i.e., PGC with a 32-bit state),
the fast source will *not* alter the state of the reproducible PRNG.
## Dependencies
This module requires the following portable-snippet modules:
* exact-int
* atomic — for thread-safety
* clock — for seeding
* once — for thread-safety
* cpu — to detect CPU-based PRNGs (*i.e.*, RdRand on Intel)
The code currently assumes the same directory structure as is used in
the portable-snippets repository. Patches to allow other structures
will be seriously considered.

View file

@ -0,0 +1,400 @@
#define _GNU_SOURCE
#include "random.h"
#if !defined(psnip_uint64_t)
# include "../exact-int/exact-int.h"
#endif
#include "../atomic/atomic.h"
#include "../clock/clock.h"
#include "../once/once.h"
#include "../cpu/cpu.h"
#include <assert.h>
#include <string.h>
#include <stdio.h>
#if !defined(_WIN32)
# include <sys/types.h>
# include <unistd.h>
#endif
#if defined(PSNIP_CPU_ARCH_X86_64) || defined(PSNIP_CPU_ARCH_X86)
# if defined(__has_builtin)
# if defined(__clang__) && (__clang_major__ == 3 && __clang_minor__ == 5)
# elif __has_builtin(__builtin_ia32_rdrand64_step) || __has_builtin(__builtin_ia32_rdrand32_step)
# define PSNIP_RANDOM__SECURE_ALLOW_RDRAND
# endif
# elif (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
# define PSNIP_RANDOM__SECURE_ALLOW_RDRAND
# endif
#endif
static int (* psnip_random_secure_generate)(size_t length, psnip_uint8_t data[PSNIP_RANDOM_ARRAY_PARAM(length)]) = NULL;
static psnip_once psnip_random_secure_once = PSNIP_ONCE_INIT;
#if defined(__linux)
# include <unistd.h>
# include <sys/syscall.h>
# if defined(SYS_getrandom)
# include <linux/version.h>
# include <sys/utsname.h>
static int
psnip_random__have_getrandom(void) {
struct utsname buf;
int major, minor, revision, r;
uname(&buf);
r = sscanf(buf.release, "%d.%d.%d", &major, &minor, &revision);
if (r != 3)
return 0;
return KERNEL_VERSION(major, minor, revision) >= KERNEL_VERSION(3, 17, 0);
}
# if defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 26))
# include <sys/random.h>
# define psnip_random__getrandom(buf, buflen, flags) getrandom(buf, buflen, flags)
# else
static int
psnip_random__getrandom(void* buf, size_t buflen, unsigned int flags) {
return (int) syscall(SYS_getrandom, buf, buflen, flags);
}
# endif
static int
psnip_random_secure_generate_getrandom(size_t length, psnip_uint8_t data[PSNIP_RANDOM_ARRAY_PARAM(length)]) {
size_t bytes_read = 0;
int r;
while (bytes_read < length) {
r = psnip_random__getrandom(&(data[bytes_read]), length - bytes_read, 0);
if (r == -1)
return -4;
bytes_read += r;
}
return 0;
}
# endif
#endif
#if defined(PSNIP_RANDOM__SECURE_ALLOW_RDRAND)
# if defined(__GNUC__)
# include <x86intrin.h>
# else
# include <immintrin.h>
# endif
#if defined(__GNUC__) && !defined(__INTEL_COMPILER)
__attribute__((__target__("rdrnd")))
#endif
static int
psnip_random__rdrand (size_t length, psnip_uint8_t data[PSNIP_RANDOM_ARRAY_PARAM(length)]) {
size_t remaining = length;
unsigned int r;
#if defined(PSNIP_CPU_ARCH_X86_64)
unsigned long long int v;
#else
unsigned int v;
#endif
while (remaining > 0) {
do {
#if defined(PSNIP_CPU_ARCH_X86_64)
# if defined(__GNUC__)
r = __builtin_ia32_rdrand64_step(&v);
# else
r = _rdrand64_step(&v);
# endif
#else
# if defined(__GNUC__)
r = __builtin_ia32_rdrand32_step(&v);
# else
r = _rdrand32_step(&v);
# endif
#endif
} while (r == 0);
if (remaining >= sizeof(v)) {
memcpy(&(data[length - remaining]), &v, sizeof(v));
remaining -= sizeof(v);
} else {
memcpy(&(data[length - remaining]), &v, remaining);
remaining = 0;
}
}
return 0;
}
#endif
#if defined(_WIN32)
static HMODULE psnip_rand_secure__advapi32_dll = NULL;
static BOOLEAN (APIENTRY *psnip_rand_secure__RtlGenRandom)(void*, ULONG);
static int psnip_random_secure_generate_RtlGenRandom(size_t length, psnip_uint8_t data[PSNIP_RANDOM_ARRAY_PARAM(length)]) {
assert(psnip_rand_secure__RtlGenRandom != NULL);
return psnip_rand_secure__RtlGenRandom(data, (ULONG) length) ? 0 : -3;
}
static void
psnip_random_secure_init(void) {
psnip_rand_secure__advapi32_dll = LoadLibrary("ADVAPI32.DLL");
if (psnip_rand_secure__advapi32_dll == NULL)
return;
psnip_rand_secure__RtlGenRandom = (BOOLEAN (APIENTRY *)(void*,ULONG))
GetProcAddress(psnip_rand_secure__advapi32_dll, "SystemFunction036");
if (psnip_rand_secure__RtlGenRandom == NULL)
return;
psnip_random_secure_generate = &psnip_random_secure_generate_RtlGenRandom;
}
# define PSNIP_RANDOM_SECURE_FOUND
#endif /* defined(_WIN32) */
#if !defined(PSNIP_RANDOM_SECURE_FOUND)
static int
psnip_random_secure_generate_dev_random(size_t length, psnip_uint8_t data[PSNIP_RANDOM_ARRAY_PARAM(length)]) {
static FILE* dev_random = NULL;
size_t bytes_read = 0;
if (dev_random == NULL) {
dev_random = fopen("/dev/random", "rb");
if (dev_random == NULL)
return -1;
}
if (length > 0) {
bytes_read = fread(data, 1, length, dev_random);
if (bytes_read != length)
return -5;
}
return 0;
}
static int
psnip_random_secure_generate_dev_urandom(size_t length, psnip_uint8_t data[PSNIP_RANDOM_ARRAY_PARAM(length)]) {
static FILE* dev_urandom = NULL;
size_t bytes_read = 0;
if (dev_urandom == NULL) {
dev_urandom = fopen("/dev/random", "rb");
if (dev_urandom == NULL)
return -1;
}
if (length > 0) {
bytes_read = fread(data, 1, length, dev_urandom);
if (bytes_read != length)
return -5;
}
return 0;
}
static void
psnip_random_secure_init(void) {
#if defined(__linux) && defined(SYS_getrandom)
if (psnip_random__have_getrandom()) {
psnip_random_secure_generate = &psnip_random_secure_generate_getrandom;
return;
}
#endif
if (psnip_random_secure_generate_dev_urandom(0, NULL) == 0) {
psnip_random_secure_generate = &psnip_random_secure_generate_dev_urandom;
return;
}
if (psnip_random_secure_generate_dev_random(0, NULL) == 0) {
psnip_random_secure_generate = &psnip_random_secure_generate_dev_random;
return;
}
#if defined(PSNIP_RANDOM__SECURE_ALLOW_RDRAND)
if (psnip_cpu_feature_check(PSNIP_CPU_FEATURE_X86_RDRND)) {
psnip_random_secure_generate = &psnip_random__rdrand;
return;
}
#endif
}
#endif
/* http://burtleburtle.net/bob/hash/integer.html */
static psnip_uint32_t psnip_random__seed_hash(psnip_uint32_t a) {
a = (a ^ 61) ^ (a >> 16);
a += (a << 3);
a ^= (a >> 4);
a *= 0x27d4eb2dU;
a ^= (a >> 15);
return a;
}
static psnip_uint32_t
psnip_random__pcg_gen_seed(void) {
psnip_uint32_t res = 0;
struct PsnipClockTimespec cur_time = { 0, };
int precision;
precision = psnip_clock_get_precision(PSNIP_CLOCK_TYPE_WALL);
if (precision != 0) {
if (psnip_clock_get_time(PSNIP_CLOCK_TYPE_WALL, &cur_time) == 0) {
goto complete;
}
}
precision = psnip_clock_get_precision(PSNIP_CLOCK_TYPE_MONOTONIC);
if (precision != 0) {
if (psnip_clock_get_time(PSNIP_CLOCK_TYPE_MONOTONIC, &cur_time) == 0) {
goto complete;
}
}
precision = psnip_clock_get_precision(PSNIP_CLOCK_TYPE_CPU);
if (precision != 0) {
if (psnip_clock_get_time(PSNIP_CLOCK_TYPE_CPU, &cur_time) == 0) {
goto complete;
}
}
complete:
res ^= psnip_random__seed_hash((psnip_uint32_t) cur_time.nanoseconds);
res ^= psnip_random__seed_hash((psnip_uint32_t) cur_time.seconds);
res ^= psnip_random__seed_hash((psnip_uint32_t) rand());
#if !defined(_WIN32)
res ^= psnip_random__seed_hash((psnip_uint32_t) getpid());
res ^= psnip_random__seed_hash((psnip_uint32_t) getppid());
#endif
return res;
}
/* PCG */
#define PSNIP_RANDOM__PCG_MULTIPLIER (747796405U)
#define PSNIP_RANDOM__PCG_INCREMENT (1729U)
static psnip_uint32_t
psnip_random__pcg_next_state(psnip_uint32_t state) {
return state * PSNIP_RANDOM__PCG_MULTIPLIER + PSNIP_RANDOM__PCG_INCREMENT;
}
static psnip_uint32_t
psnip_random__pcg_from_state(psnip_uint32_t state) {
psnip_uint32_t res = ((state >> ((state >> 28) + 4)) ^ state) * (277803737U);
res ^= res >> 22;
return res;
}
static int
psnip_random__pgc_generate(psnip_atomic_int32* state, size_t length, psnip_uint8_t data[PSNIP_RANDOM_ARRAY_PARAM(length)]) {
psnip_int32_t old_state;
psnip_uint32_t new_state, v;
size_t remaining;
do {
old_state = psnip_atomic_int32_load(state);
new_state = (psnip_uint32_t) old_state;
remaining = length;
while (remaining > 0) {
v = psnip_random__pcg_from_state(new_state);
new_state = psnip_random__pcg_next_state(new_state);
if (remaining >= sizeof(psnip_uint32_t)) {
memcpy(&(data[length - remaining]), &v, sizeof(psnip_uint32_t));
remaining -= sizeof(psnip_uint32_t);
} else {
memcpy(&(data[length - remaining]), &v, remaining);
remaining = 0;
}
}
} while (!psnip_atomic_int32_compare_exchange(state, &old_state, (psnip_int32_t) new_state));
return 0;
}
/* Reproducible */
static psnip_atomic_int32 psnip_random__reproducible_seed = 0;
static psnip_atomic_int32 psnip_random__reproducible_state = 0;
static psnip_once psnip_random_reproducible_once = PSNIP_ONCE_INIT;
static void
psnip_random_reproducible_init(void) {
psnip_int32_t seed = (psnip_uint32_t) psnip_random__pcg_gen_seed();
psnip_atomic_int32_store(&psnip_random__reproducible_seed, seed);
psnip_atomic_int32_store(&psnip_random__reproducible_state, seed);
}
void
psnip_random_set_seed (psnip_uint32_t seed) {
const psnip_int32_t sseed = (psnip_int32_t) seed;
psnip_once_call(&psnip_random_reproducible_once, &psnip_random_reproducible_init);
psnip_atomic_int32_store(&psnip_random__reproducible_seed, sseed);
psnip_atomic_int32_store(&psnip_random__reproducible_state, sseed);
}
psnip_uint32_t
psnip_random_get_seed (void) {
psnip_once_call(&psnip_random_reproducible_once, &psnip_random_reproducible_init);
return (psnip_uint32_t) psnip_atomic_int32_load(&psnip_random__reproducible_seed);
}
/* Fast */
static psnip_atomic_int32 psnip_random__fast_state = 0;
static psnip_once psnip_random_fast_once = PSNIP_ONCE_INIT;
static void
psnip_random_fast_init(void) {
psnip_int32_t seed = (psnip_int32_t) psnip_random__pcg_gen_seed();
psnip_atomic_int32_store((psnip_atomic_int32*) &psnip_random__fast_state, seed);
}
int
psnip_random_bytes(enum PSnipRandomSource source,
size_t length,
psnip_uint8_t data[PSNIP_RANDOM_ARRAY_PARAM(length)]) {
switch (source) {
case PSNIP_RANDOM_SOURCE_SECURE:
#if !defined(PSNIP_RANDOM_SECURE_NO_INIT)
psnip_once_call(&psnip_random_secure_once, &psnip_random_secure_init);
if (psnip_random_secure_generate == NULL)
return -1;
#endif
return psnip_random_secure_generate(length, data);
case PSNIP_RANDOM_SOURCE_REPRODUCIBLE:
#if !defined(PSNIP_RANDOM_REPRODUCIBLE_NO_INIT)
psnip_once_call(&psnip_random_reproducible_once, &psnip_random_reproducible_init);
#endif
return psnip_random__pgc_generate(&psnip_random__reproducible_state, length, data);
case PSNIP_RANDOM_SOURCE_FAST:
#if !defined(PSNIP_RANDOM_FAST_NO_INIT)
psnip_once_call(&psnip_random_fast_once, &psnip_random_fast_init);
#endif
return psnip_random__pgc_generate(&psnip_random__fast_state, length, data);
}
return -2;
}

View file

@ -0,0 +1,38 @@
#if !defined(PSNIP_RANDOM__H)
#define PSNIP_RANDOM__H
#include <stdlib.h>
#if !defined(psnip_uint8_t)
# include "../exact-int/exact-int.h"
#endif
#if defined(HEDLEY_ARRAY_PARAM)
# define PSNIP_RANDOM_ARRAY_PARAM(expr) HEDLEY_ARRAY_PARAM(expr)
#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) && !defined(__PGI)
# define PSNIP_RANDOM_ARRAY_PARAM(expr) (expr)
#else
# define PSNIP_RANDOM_ARRAY_PARAM(expr)
#endif
#if defined(__cplusplus)
extern "C" {
#endif
enum PSnipRandomSource {
PSNIP_RANDOM_SOURCE_SECURE,
PSNIP_RANDOM_SOURCE_REPRODUCIBLE,
PSNIP_RANDOM_SOURCE_FAST,
};
int psnip_random_bytes (enum PSnipRandomSource source,
size_t length,
psnip_uint8_t data[PSNIP_RANDOM_ARRAY_PARAM(length)]);
psnip_uint32_t psnip_random_get_seed (void);
void psnip_random_set_seed (psnip_uint32_t seed);
#if defined(__cplusplus)
}
#endif
#endif /* defined(PSNIP_RANDOM__H) */