From 337415b7eeb56241a84336f896488ccfbba67d4c Mon Sep 17 00:00:00 2001 From: Maria Schopp Date: Tue, 25 May 2021 16:42:55 -0400 Subject: [PATCH 1/4] Use getrandom API --- randombytes.c | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/randombytes.c b/randombytes.c index 6e7f81d..e86c5ea 100644 --- a/randombytes.c +++ b/randombytes.c @@ -33,6 +33,7 @@ # include # include # include +# include # include # include # include @@ -89,7 +90,31 @@ static int randombytes_wasi_randombytes(void *buf, size_t n) { } #endif /* defined(__wasi__) */ -#if defined(__linux__) && defined(SYS_getrandom) +#if defined(__linux__) && defined(__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC_MINOR__ > 24)) +static int randombytes_linux_randombytes_getrandom_function(void *buf, size_t n) +{ + /* I have thought about using a separate PRF, seeded by getrandom, but + * it turns out that the performance of getrandom is good enough + * (250 MB/s on my laptop). + */ + size_t offset = 0, chunk; + int ret; + while (n > 0) { + /* getrandom does not allow chunks larger than 33554431 */ + chunk = n <= 33554431 ? n : 33554431; + do { + ret = getrandom ((char *)buf + offset, chunk, 0); + } while (ret == -1 && errno == EINTR); + if (ret < 0) return ret; + offset += ret; + n -= ret; + } + assert(n == 0); + return 0; +} + + +#elif defined(__linux__) && defined(SYS_getrandom) static int randombytes_linux_randombytes_getrandom(void *buf, size_t n) { /* I have thought about using a separate PRF, seeded by getrandom, but @@ -111,7 +136,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(SYS_getrandom) or glibc version > 2.24) */ #if defined(__linux__) && !defined(SYS_getrandom) @@ -296,7 +321,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(__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC_MINOR__ > 24)) +# pragma message("Using getrandom function call") + /* Use getrandom system call */ + return randombytes_linux_randombytes_getrandom_function(buf, n); +# elif defined(SYS_getrandom) # pragma message("Using getrandom system call") /* Use getrandom system call */ return randombytes_linux_randombytes_getrandom(buf, n); From d37c070742412d9be1729d81b424cb43d118055b Mon Sep 17 00:00:00 2001 From: Daan Sprenkels Date: Tue, 8 Jun 2021 20:26:46 +0200 Subject: [PATCH 2/4] Skip syscall tests when using glib getrandom --- randombytes.c | 6 ++++-- randombytes_test.c | 23 +++++++++++++---------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/randombytes.c b/randombytes.c index e86c5ea..d549783 100644 --- a/randombytes.c +++ b/randombytes.c @@ -33,7 +33,9 @@ # include # include # include -# include +# if defined(__linux__) && defined(__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC_MINOR__ > 24)) +# include +# endif /* defined(__linux__) && defined(__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC_MINOR__ > 24)) */ # include # include # include @@ -103,7 +105,7 @@ static int randombytes_linux_randombytes_getrandom_function(void *buf, size_t n) /* getrandom does not allow chunks larger than 33554431 */ chunk = n <= 33554431 ? n : 33554431; do { - ret = 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; diff --git a/randombytes_test.c b/randombytes_test.c index 72c80fc..5dd0291 100644 --- a/randombytes_test.c +++ b/randombytes_test.c @@ -58,7 +58,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 +69,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)] = {}; @@ -112,11 +112,11 @@ static void test_issue_33(void) { #if 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; @@ -148,13 +148,16 @@ 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(__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC_MINOR__ > 24)) + SKIP_TEST(test_getrandom_syscall_partial) + SKIP_TEST(test_getrandom_syscall_interrupted) +#elif defined(__linux__) && defined(SYS_getrandom) + 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_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) From 02ec8e6bee88fdd1f86a12c79f84327f381cdaeb Mon Sep 17 00:00:00 2001 From: Daan Sprenkels Date: Mon, 6 Sep 2021 21:57:48 +0200 Subject: [PATCH 3/4] Refactor #30 --- randombytes.c | 39 ++++++++++++--------------------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/randombytes.c b/randombytes.c index d549783..fedd1b7 100644 --- a/randombytes.c +++ b/randombytes.c @@ -34,6 +34,7 @@ # 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 @@ -92,31 +93,15 @@ static int randombytes_wasi_randombytes(void *buf, size_t n) { } #endif /* defined(__wasi__) */ -#if defined(__linux__) && defined(__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC_MINOR__ > 24)) -static int randombytes_linux_randombytes_getrandom_function(void *buf, size_t n) -{ - /* I have thought about using a separate PRF, seeded by getrandom, but - * it turns out that the performance of getrandom is good enough - * (250 MB/s on my laptop). - */ - size_t offset = 0, chunk; - int ret; - while (n > 0) { - /* getrandom does not allow chunks larger than 33554431 */ - chunk = n <= 33554431 ? n : 33554431; - do { - ret = getrandom((char *)buf + offset, chunk, 0); - } while (ret == -1 && errno == EINTR); - if (ret < 0) return ret; - offset += ret; - n -= ret; - } - assert(n == 0); - return 0; +#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 - -#elif defined(__linux__) && defined(SYS_getrandom) static int randombytes_linux_randombytes_getrandom(void *buf, size_t n) { /* I have thought about using a separate PRF, seeded by getrandom, but @@ -129,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; @@ -138,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) or glibc version > 2.24) */ +#endif // defined(__linux__) && (defined(USE_GLIBC) || defined(SYS_getrandom)) #if defined(__linux__) && !defined(SYS_getrandom) @@ -323,10 +308,10 @@ 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(__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC_MINOR__ > 24)) +# if defined(USE_GLIBC) # pragma message("Using getrandom function call") /* Use getrandom system call */ - return randombytes_linux_randombytes_getrandom_function(buf, n); + return randombytes_linux_randombytes_getrandom(buf, n); # elif defined(SYS_getrandom) # pragma message("Using getrandom system call") /* Use getrandom system call */ From 74f98202fbde4413bef111dd0a106e0d7b1e0104 Mon Sep 17 00:00:00 2001 From: Daan Sprenkels Date: Mon, 6 Sep 2021 21:58:07 +0200 Subject: [PATCH 4/4] Add tests for #30 Fixes #30 --- Makefile | 2 +- randombytes_test.c | 52 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 4 deletions(-) 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_test.c b/randombytes_test.c index 5dd0291..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); @@ -78,6 +82,26 @@ static void test_getrandom_syscall_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,7 +133,23 @@ 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_syscall_partial) { @@ -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,19 @@ int main(void) { RUN_TEST(test_functional) RUN_TEST(test_empty) -#if defined(__linux__) && defined(__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC_MINOR__ > 24)) +#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_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) */