diff --git a/Makefile b/Makefile index 332fdec..749288a 100644 --- a/Makefile +++ b/Makefile @@ -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) diff --git a/randombytes.c b/randombytes.c index 6e7f81d..fedd1b7 100644 --- a/randombytes.c +++ b/randombytes.c @@ -33,6 +33,10 @@ # include # include # include +# if defined(__linux__) && defined(__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC_MINOR__ > 24)) +# define USE_GLIBC +# include +# endif /* defined(__linux__) && defined(__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC_MINOR__ > 24)) */ # include # include # include @@ -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 @@ -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; @@ -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) @@ -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); diff --git a/randombytes_test.c b/randombytes_test.c index 72c80fc..cd476ca 100644 --- a/randombytes_test.c +++ b/randombytes_test.c @@ -7,6 +7,7 @@ static void *current_test = NULL; static int syscall_called = 0; +static int glib_getrandom_called = 0; // ======== Helper macros and functions ======== @@ -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); @@ -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)); @@ -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)] = {}; @@ -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)); @@ -109,14 +133,30 @@ 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; @@ -124,7 +164,7 @@ int __wrap_syscall(int n, char *buf, size_t buflen, int flags) { } 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) { @@ -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)