Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ CFLAGS ?= -g -O2 -std=gnu99 \
-Wformat-security -Werror=format-security -Wstrict-prototypes \
-D_FORTIFY_SOURCE=2 -fPIC -fno-strict-overflow

TEST_WRAPS := -Wl,-wrap=ioctl,-wrap=syscall
TEST_WRAPS := -Wl,-wrap=ioctl,-wrap=getrandom,-wrap=syscall

ifeq ($(shell uname -o), GNU/Linux)
TEST_LDFLAGS := $(TEST_WRAPS)
Expand Down
24 changes: 20 additions & 4 deletions randombytes.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
# include <stdint.h>
# include <stdio.h>
# include <sys/ioctl.h>
# if defined(__linux__) && defined(__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC_MINOR__ > 24))
# define USE_GLIBC
# include <sys/random.h>
# endif /* defined(__linux__) && defined(__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC_MINOR__ > 24)) */
# include <sys/stat.h>
# include <sys/syscall.h>
# include <sys/types.h>
Expand Down Expand Up @@ -89,7 +93,15 @@ static int randombytes_wasi_randombytes(void *buf, size_t n) {
}
#endif /* defined(__wasi__) */

#if defined(__linux__) && defined(SYS_getrandom)
#if defined(__linux__) && (defined(USE_GLIBC) || defined(SYS_getrandom))
# if defined(USE_GLIBC)
// getrandom is declared in glibc.
# elif defined(SYS_getrandom)
static ssize_t getrandom(void *buf, size_t buflen, unsigned int flags) {
return syscall(SYS_getrandom, buf, buflen, flags);
}
# endif

static int randombytes_linux_randombytes_getrandom(void *buf, size_t n)
{
/* I have thought about using a separate PRF, seeded by getrandom, but
Expand All @@ -102,7 +114,7 @@ static int randombytes_linux_randombytes_getrandom(void *buf, size_t n)
/* getrandom does not allow chunks larger than 33554431 */
chunk = n <= 33554431 ? n : 33554431;
do {
ret = syscall(SYS_getrandom, (char *)buf + offset, chunk, 0);
ret = getrandom((char *)buf + offset, chunk, 0);
} while (ret == -1 && errno == EINTR);
if (ret < 0) return ret;
offset += ret;
Expand All @@ -111,7 +123,7 @@ static int randombytes_linux_randombytes_getrandom(void *buf, size_t n)
assert(n == 0);
return 0;
}
#endif /* defined(__linux__) && defined(SYS_getrandom) */
#endif // defined(__linux__) && (defined(USE_GLIBC) || defined(SYS_getrandom))


#if defined(__linux__) && !defined(SYS_getrandom)
Expand Down Expand Up @@ -296,7 +308,11 @@ int randombytes(void *buf, size_t n)
# pragma message("Using crypto api from NodeJS")
return randombytes_js_randombytes_nodejs(buf, n);
#elif defined(__linux__)
# if defined(SYS_getrandom)
# if defined(USE_GLIBC)
# pragma message("Using getrandom function call")
/* Use getrandom system call */
return randombytes_linux_randombytes_getrandom(buf, n);
# elif defined(SYS_getrandom)
# pragma message("Using getrandom system call")
/* Use getrandom system call */
return randombytes_linux_randombytes_getrandom(buf, n);
Expand Down
73 changes: 61 additions & 12 deletions randombytes_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

static void *current_test = NULL;
static int syscall_called = 0;
static int glib_getrandom_called = 0;

// ======== Helper macros and functions ========

Expand Down Expand Up @@ -45,6 +46,9 @@ static void test_functional(void) {
uint8_t buf1[20] = {}, buf2[sizeof(buf1)] = {};
const int ret1 = randombytes(buf1, sizeof(buf1));
const int ret2 = randombytes(buf2, sizeof(buf2));
if (ret1 != 0 || ret2 != 0) {
printf("error: %s\n", strerror(errno));
}
assert(ret1 == 0);
assert(ret2 == 0);
assert(memcmp(buf1, buf2, sizeof(buf1)) != 0);
Expand All @@ -58,7 +62,7 @@ static void test_empty(void) {
assert(memcmp(buf, zero, sizeof(zero)) == 0);
}

static void test_getrandom_partial(void) {
static void test_getrandom_syscall_partial(void) {
syscall_called = 0;
uint8_t buf[100] = {};
const int ret = randombytes(buf, sizeof(buf));
Expand All @@ -69,7 +73,7 @@ static void test_getrandom_partial(void) {
}
}

static void test_getrandom_interrupted(void) {
static void test_getrandom_syscall_interrupted(void) {
syscall_called = 0;
uint8_t zero[20] = {};
uint8_t buf[sizeof(zero)] = {};
Expand All @@ -78,6 +82,26 @@ static void test_getrandom_interrupted(void) {
assert(memcmp(buf, zero, 20) != 0);
}

static void test_getrandom_glib_partial(void) {
glib_getrandom_called = 0;
uint8_t buf[100] = {};
const int ret = randombytes(buf, sizeof(buf));
assert(ret == 0);
assert(glib_getrandom_called >= 5);
for (int i = 1; i < 5; i++) {
assert(memcmp(&buf[0], &buf[20*i], 20) != 0);
}
}

static void test_getrandom_glib_interrupted(void) {
glib_getrandom_called = 0;
uint8_t zero[20] = {};
uint8_t buf[sizeof(zero)] = {};
const int ret = randombytes(buf, sizeof(buf));
assert(ret == 0);
assert(memcmp(buf, zero, 20) != 0);
}

static void test_issue_17(void) {
uint8_t buf1[20] = {}, buf2[sizeof(buf1)] = {};
const int ret1 = randombytes(buf1, sizeof(buf1));
Expand Down Expand Up @@ -109,22 +133,38 @@ static void test_issue_33(void) {

// ======== Mock OS functions to simulate uncommon behavior ========

#if defined(__linux__) && defined(SYS_getrandom)
#if defined(__linux__) && defined(__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC_MINOR__ > 24))
int __wrap_getrandom(char *buf, size_t buflen, int flags) {
glib_getrandom_called++;
if (current_test == test_getrandom_glib_partial) {
// Fill only 16 bytes, the caller should retry
const size_t current_buflen = buflen <= 16 ? buflen : 16;
return __real_getrandom(buf, current_buflen, flags);
} else if (current_test == test_getrandom_glib_interrupted) {
if (glib_getrandom_called < 5) {
errno = EINTR;
return -1;
}
}
return __real_getrandom(buf, buflen, flags);
}

#elif defined(__linux__) && defined(SYS_getrandom)
int __wrap_syscall(int n, char *buf, size_t buflen, int flags) {
syscall_called++;
if (current_test == test_getrandom_partial) {
if (current_test == test_getrandom_syscall_partial) {
// Fill only 16 bytes, the caller should retry
const size_t current_buflen = buflen <= 16 ? buflen : 16;
return __real_syscall(n, buf, current_buflen, flags);
} else if (current_test == test_getrandom_interrupted) {
} else if (current_test == test_getrandom_syscall_interrupted) {
if (syscall_called < 5) {
errno = EINTR;
return -1;
}
}
return __real_syscall(n, buf, buflen, flags);
}
#endif /* defined(__linux__) && defined(SYS_getrandom) */
#endif /* defined(__linux__) && (defined(SYS_getrandom) or glibc version > 2.24) */

#if defined(__linux__) && !defined(SYS_getrandom)
int __wrap_ioctl(int fd, int code, int* ret) {
Expand All @@ -148,13 +188,22 @@ int main(void) {

RUN_TEST(test_functional)
RUN_TEST(test_empty)
#if defined(__linux__) && defined(SYS_getrandom)
RUN_TEST(test_getrandom_partial)
RUN_TEST(test_getrandom_interrupted)
#if defined(__linux__) && defined(USE_GLIBC)
RUN_TEST(test_getrandom_glib_partial)
RUN_TEST(test_getrandom_glib_interrupted)
SKIP_TEST(test_getrandom_syscall_partial)
SKIP_TEST(test_getrandom_syscall_interrupted)
#elif defined(__linux__) && defined(SYS_getrandom)
SKIP_TEST(test_getrandom_glib_partial)
SKIP_TEST(test_getrandom_glib_interrupted)
RUN_TEST(test_getrandom_syscall_partial)
RUN_TEST(test_getrandom_syscall_interrupted)
#else
SKIP_TEST(test_getrandom_partial)
SKIP_TEST(test_getrandom_interrupted)
#endif /* defined(__linux__) && defined(SYS_getrandom) */
SKIP_TEST(test_getrandom_glib_partial)
SKIP_TEST(test_getrandom_glib_interrupted)
SKIP_TEST(test_getrandom_syscall_partial)
SKIP_TEST(test_getrandom_syscall_interrupted)
#endif /* defined(__linux__) && (defined(SYS_getrandom) or glibc version > 2.24) */
#if defined(__linux__) && !defined(SYS_getrandom)
RUN_TEST(test_issue_17)
RUN_TEST(test_issue_22)
Expand Down