From 6c2dd8c27cc7438f0c9d82cb63c96e61e81c392f Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sat, 8 May 2021 23:52:33 +0900 Subject: [PATCH 1/7] libc: minimal: Add rand() and srand() rand() and srand() are pseudo-random number generator functions defined in ISO C. This implementation uses the Linear Congruential Generator (LCG) algorithm with the following parameters, which are the same as used in GNU Libc "TYPE_0" algorithm. Modulus 2^31 Multiplier 1103515245 Increment 12345 Output Bits 30..0 Note that the default algorithm used by GNU Libc is not TYPE_0, and TYPE_0 should be selected first by an initstate() call as shown below. All global variables in a C library must be routed to a memory partition in order to be used by user-mode applications when CONFIG_USERSPACE is enabled. Thus, srand_seed is marked as such. z_libc_partition is originally used by the Newlib C library but it's generic enough to be used by either the minimal libc or the newlib. All other functions in the Minimal C library, however, don't require global variables/states. Unconditionally using z_libc_partition with the minimal libc might be a problem for applications utilizing many custom memory partitions on platforms with a limited number of MPU regions (eg. Cortex M0/M3). This commit introduces a kconfig option CONFIG_MINIMAL_LIBC_RAND so that applications can enable the functions if needed. The option is disabled by default. Because this commit _does_ implement rand() and srand(), our coding guideline check on GitHub Action finds it as a violation. Error: lib/libc/minimal/include/stdlib.h:45:WARNING: Violation to rule 21.2 (Should not used a reserved identifier) - srand But this is false positive. The following is a simple test program for LCG with GNU Libc. #include #include int main() { static char state[8]; /* Switch GLIBC to use LCG/TYPE_0 generator type. */ initstate(0, state, sizeof(state)); srand(1); /* Or any other value. */ printf("%d\n", rand()); printf("%d\n", rand()); return 0; } See initstate(3p) for more detail about how to use LCG in GLIBC. Signed-off-by: Yasushi SHOJI --- include/sys/libc-hooks.h | 13 +++++++----- lib/libc/Kconfig | 13 ++++++++++++ lib/libc/minimal/CMakeLists.txt | 1 + lib/libc/minimal/include/stdlib.h | 6 ++++++ lib/libc/minimal/source/stdlib/rand.c | 29 +++++++++++++++++++++++++++ 5 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 lib/libc/minimal/source/stdlib/rand.c diff --git a/include/sys/libc-hooks.h b/include/sys/libc-hooks.h index 3122fe06db348..aee95811671c0 100644 --- a/include/sys/libc-hooks.h +++ b/include/sys/libc-hooks.h @@ -69,11 +69,14 @@ extern struct k_mem_partition z_malloc_partition; #if defined(CONFIG_NEWLIB_LIBC) || defined(CONFIG_STACK_CANARIES) || \ defined(CONFIG_NEED_LIBC_MEM_PARTITION) -/* Minimal libc has no globals. We do put the stack canary global in the - * libc partition since it is not worth placing in a partition of its own. - * - * Some architectures require a global pointer for thread local storage, - * which is placed inside the libc partition. +/* - All newlib globals will be placed into z_libc_partition. + * - Minimal C library globals, if any, will be placed into + * z_libc_partition. + * - Stack canary globals will be placed into z_libc_partition since + * it is not worth placing in its own partition. + * - Some architectures may place the global pointer to the thread local + * storage in z_libc_partition since it is not worth placing in its + * own partition. */ #define Z_LIBC_PARTITION_EXISTS 1 diff --git a/lib/libc/Kconfig b/lib/libc/Kconfig index b0b22f4f1b848..6cfb03fd4d220 100644 --- a/lib/libc/Kconfig +++ b/lib/libc/Kconfig @@ -145,6 +145,19 @@ config MINIMAL_LIBC_OPTIMIZE_STRING_FOR_SIZE Enable smaller but potentially slower implementations of memcpy and memset. On the Cortex-M0+ this reduces the total code size by 120 bytes. +config MINIMAL_LIBC_RAND + bool "Enables rand and srand functions" + select NEED_LIBC_MEM_PARTITION + help + Enable rand() and srand() for the minimal libc. The + functions implicitly access global/static data. Such data + must be put into a memory partition if CONFIG_USERSPACE=y, + and disabling this option may save an entry for application + defining many custom partitions. + + Say 'Y' here if you need rand() and srand(). This might require + an additional memory partition. + endif # MINIMAL_LIBC config STDOUT_CONSOLE diff --git a/lib/libc/minimal/CMakeLists.txt b/lib/libc/minimal/CMakeLists.txt index b8f9475ef96c3..b7f9e5577444b 100644 --- a/lib/libc/minimal/CMakeLists.txt +++ b/lib/libc/minimal/CMakeLists.txt @@ -22,3 +22,4 @@ zephyr_library_sources( ) zephyr_library_sources_ifdef(CONFIG_POSIX_CLOCK source/time/time.c) +zephyr_library_sources_ifdef(CONFIG_MINIMAL_LIBC_RAND source/stdlib/rand.c) diff --git a/lib/libc/minimal/include/stdlib.h b/lib/libc/minimal/include/stdlib.h index de54facea8c10..d2486efb0d947 100644 --- a/lib/libc/minimal/include/stdlib.h +++ b/lib/libc/minimal/include/stdlib.h @@ -10,6 +10,8 @@ #define ZEPHYR_LIB_LIBC_MINIMAL_INCLUDE_STDLIB_H_ #include +#include + #ifdef __cplusplus extern "C" { #endif @@ -37,7 +39,11 @@ static inline void exit(int status) } void abort(void); +#ifdef CONFIG_MINIMAL_LIBC_RAND +#define RAND_MAX INT_MAX int rand(void); +void srand(unsigned int seed); +#endif /* CONFIG_MINIMAL_LIBC_RAND */ static inline int abs(int __n) { diff --git a/lib/libc/minimal/source/stdlib/rand.c b/lib/libc/minimal/source/stdlib/rand.c new file mode 100644 index 0000000000000..71459c9b0454f --- /dev/null +++ b/lib/libc/minimal/source/stdlib/rand.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021 Space Cubics, LLC. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define LIBC_DATA K_APP_DMEM(z_libc_partition) + +#define OUTPUT_BITS (0x7fffffffU) +#define MULTIPLIER (1103515245U) +#define INCREMENT (12345U) + +static LIBC_DATA unsigned long srand_seed = 1; + +void srand(unsigned int s) +{ + srand_seed = s; +} + +int rand(void) +{ + srand_seed = (MULTIPLIER * srand_seed + INCREMENT) & OUTPUT_BITS; + + return srand_seed; +} From 09b479e72a0a86dca72dce4c8dc7b486fb9f4ed4 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Fri, 7 May 2021 18:39:25 +0900 Subject: [PATCH 2/7] tests: crypto: mbedtls: Remove local implementation of rand() When MBEDTLS_RSA_C is defined, mbedtls define its local version of rand() function. Since we already have rand() in our minimal libc, we can safely remove this. Signed-off-by: Yasushi SHOJI --- tests/crypto/mbedtls/prj.conf | 1 + tests/crypto/mbedtls/src/mbedtls.c | 14 -------------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/tests/crypto/mbedtls/prj.conf b/tests/crypto/mbedtls/prj.conf index ed8ee9816435b..a572aece6e75b 100644 --- a/tests/crypto/mbedtls/prj.conf +++ b/tests/crypto/mbedtls/prj.conf @@ -5,3 +5,4 @@ CONFIG_MBEDTLS_CFG_FILE="config-tls-generic.h" CONFIG_MBEDTLS_TEST=y CONFIG_ZTEST=y CONFIG_TEST_USERSPACE=y +CONFIG_MINIMAL_LIBC_RAND=y diff --git a/tests/crypto/mbedtls/src/mbedtls.c b/tests/crypto/mbedtls/src/mbedtls.c index 0d7f0c8cd9f25..06d6f0c06546f 100644 --- a/tests/crypto/mbedtls/src/mbedtls.c +++ b/tests/crypto/mbedtls/src/mbedtls.c @@ -72,20 +72,6 @@ #include "mbedtls/memory_buffer_alloc.h" #endif -#if defined(MBEDTLS_RSA_C) -int rand(void) -{ - static ZTEST_DMEM uint32_t seed = 7U; - - seed ^= seed << 13; - seed ^= seed >> 17; - seed ^= seed << 5; - - return seed; -} - -#endif - static int test_snprintf(size_t n, const char ref_buf[10], int ref_ret) { int ret; From 0cf4eb0fd9527b236c77f62c2147e5ec26160db7 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Thu, 6 May 2021 12:03:33 +0900 Subject: [PATCH 3/7] tests: c_lib: Add tests for rand() and srand() - simple tests for each function - a reproducibility test for rand with the same seed values Signed-off-by: Yasushi SHOJI --- tests/lib/c_lib/prj.conf | 1 + tests/lib/c_lib/src/main.c | 109 +++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/tests/lib/c_lib/prj.conf b/tests/lib/c_lib/prj.conf index 869fd338e6afd..775239be95bb8 100644 --- a/tests/lib/c_lib/prj.conf +++ b/tests/lib/c_lib/prj.conf @@ -1,3 +1,4 @@ CONFIG_ZTEST=y CONFIG_TEST_USERSPACE=y CONFIG_ZTEST_FATAL_HOOK=y +CONFIG_MINIMAL_LIBC_RAND=y diff --git a/tests/lib/c_lib/src/main.c b/tests/lib/c_lib/src/main.c index 35fc13b3d14bf..0a4af7706a3dd 100644 --- a/tests/lib/c_lib/src/main.c +++ b/tests/lib/c_lib/src/main.c @@ -899,6 +899,112 @@ void test_time(void) zassert_not_null(gmtime_r(&tests4, &tp), "gmtime_r failed"); } +/** + * + * @brief Test rand function + * + */ +void test_rand(void) +{ + int a; + + a = rand(); + /* The default seed is 1 */ + zassert_equal(a, 1103527590, "rand failed"); +} + +/** + * + * @brief Test srand function + * + */ +void test_srand(void) +{ + int a; + + srand(0); + a = rand(); + zassert_equal(a, 12345, "srand with seed 0 failed"); + + srand(1); + a = rand(); + zassert_equal(a, 1103527590, "srand with seed 1 failed"); + + srand(10); + a = rand(); + zassert_equal(a, 297746555, "srand with seed 10 failed"); + + srand(UINT_MAX - 1); + a = rand(); + zassert_equal(a, 2087949151, "srand with seed UINT_MAX - 1 failed"); + + srand(UINT_MAX); + a = rand(); + zassert_equal(a, 1043980748, "srand with seed UINT_MAX failed"); +} + +/** + * + * @brief Test rand function for reproducibility + * + */ +void test_rand_reproducibility(void) +{ + int a; + int b; + int c; + + srand(0); + a = rand(); + zassert_equal(a, 12345, "srand with seed 0 failed"); + srand(0); + b = rand(); + zassert_equal(b, 12345, "srand with seed 0 failed (2nd)"); + srand(0); + c = rand(); + zassert_equal(c, 12345, "srand with seed 0 failed (3rd)"); + + srand(1); + a = rand(); + zassert_equal(a, 1103527590, "srand with seed 1 failed"); + srand(1); + b = rand(); + zassert_equal(b, 1103527590, "srand with seed 1 failed (2nd)"); + srand(1); + c = rand(); + zassert_equal(c, 1103527590, "srand with seed 1 failed (3rd)"); + + srand(10); + a = rand(); + zassert_equal(a, 297746555, "srand with seed 10 failed"); + srand(10); + b = rand(); + zassert_equal(b, 297746555, "srand with seed 10 failed (2nd)"); + srand(10); + c = rand(); + zassert_equal(c, 297746555, "srand with seed 10 failed (3rd)"); + + srand(UINT_MAX - 1); + a = rand(); + zassert_equal(a, 2087949151, "srand with seed UINT_MAX - 1 failed"); + srand(UINT_MAX - 1); + b = rand(); + zassert_equal(b, 2087949151, "srand with seed UINT_MAX - 1 failed (2nd)"); + srand(UINT_MAX - 1); + c = rand(); + zassert_equal(c, 2087949151, "srand with seed UINT_MAX - 1 failed (3rd)"); + + srand(UINT_MAX); + a = rand(); + zassert_equal(a, 1043980748, "srand with seed UINT_MAX failed"); + srand(UINT_MAX); + b = rand(); + zassert_equal(b, 1043980748, "srand with seed UINT_MAX failed (2nd)"); + srand(UINT_MAX); + c = rand(); + zassert_equal(c, 1043980748, "srand with seed UINT_MAX failed (3rd)"); +} + /** * * @brief test abort functions @@ -966,6 +1072,9 @@ void test_main(void) ztest_unit_test(test_memcpy), ztest_unit_test(test_memmove), ztest_unit_test(test_time), + ztest_unit_test(test_rand), + ztest_unit_test(test_srand), + ztest_unit_test(test_rand_reproducibility), ztest_unit_test(test_abort), ztest_unit_test(test_exit), ztest_unit_test(test_str_operate), From e21d66e32ae8240620bfcb122c7db9de4f933061 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Fri, 7 May 2021 15:48:15 +0900 Subject: [PATCH 4/7] doc: guides: Add srand() Under the "OS Abstraction / POSIX Support" section in the "User and Developer Guides", we somehow had rand() function already listed and marked supported, but not srand(). Add srand() to the list. Signed-off-by: Yasushi SHOJI --- doc/guides/portability/posix.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/guides/portability/posix.rst b/doc/guides/portability/posix.rst index b3edcf2d544ac..391a3a8cb6c43 100644 --- a/doc/guides/portability/posix.rst +++ b/doc/guides/portability/posix.rst @@ -282,7 +282,7 @@ This is implemented as part of the minimal C library available in Zephyr. setlocale(), snprintf(),+ sprintf(),+ - srand(), + srand(),+ sscanf(), strcat(),+ strchr(),+ From bd13898c695f4b77b3cd9365b7aa396dd24a21e4 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sat, 8 May 2021 22:23:44 +0900 Subject: [PATCH 5/7] doc: Markup kconfig options with :kconfig: role Some Kconfig options are left marked as inline literals. But in Zephyr document, we use the "kconfig" role provided by Sphinx. Signed-off-by: Yasushi SHOJI --- doc/guides/porting/arch.rst | 2 +- doc/reference/kernel/other/interrupts.rst | 11 ++++++----- doc/reference/kernel/threads/index.rst | 4 ++-- doc/reference/usermode/memory_domain.rst | 3 ++- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/doc/guides/porting/arch.rst b/doc/guides/porting/arch.rst index 7badb90208904..f6eb16689a91b 100644 --- a/doc/guides/porting/arch.rst +++ b/doc/guides/porting/arch.rst @@ -795,7 +795,7 @@ details: Some architectures may need to update software memory management structures or modify hardware registers on another CPU when memory domain APIs are invoked. -If so, CONFIG_ARCH_MEM_DOMAIN_SYNCHRONOUS_API must be selected by the +If so, :kconfig:`CONFIG_ARCH_MEM_DOMAIN_SYNCHRONOUS_API` must be selected by the architecture and some additional APIs must be implemented. This is common on MMU systems and uncommon on MPU systems: diff --git a/doc/reference/kernel/other/interrupts.rst b/doc/reference/kernel/other/interrupts.rst index 9c076665d8c0c..42c003bcd2c2c 100644 --- a/doc/reference/kernel/other/interrupts.rst +++ b/doc/reference/kernel/other/interrupts.rst @@ -377,10 +377,10 @@ argument is ignored. Vector Table ------------ -A vector table is generated when CONFIG_GEN_IRQ_VECTOR_TABLE is enabled. This -data structure is used natively by the CPU and is simply an array of function -pointers, where each element n corresponds to the IRQ handler for IRQ line n, -and the function pointers are: +A vector table is generated when :kconfig:`CONFIG_GEN_IRQ_VECTOR_TABLE` is +enabled. This data structure is used natively by the CPU and is simply an +array of function pointers, where each element n corresponds to the IRQ handler +for IRQ line n, and the function pointers are: #. For 'direct' interrupts declared with :c:macro:`IRQ_DIRECT_CONNECT`, the handler function will be placed here. @@ -394,7 +394,8 @@ and the function pointers are: Some architectures (such as the Nios II internal interrupt controller) have a common entry point for all interrupts and do not support a vector table, in -which case the CONFIG_GEN_IRQ_VECTOR_TABLE option should be disabled. +which case the :kconfig:`CONFIG_GEN_IRQ_VECTOR_TABLE` option should be +disabled. Some architectures may reserve some initial vectors for system exceptions and declare this in a table elsewhere, in which case diff --git a/doc/reference/kernel/threads/index.rst b/doc/reference/kernel/threads/index.rst index 7bc865186df97..4fb96613758aa 100644 --- a/doc/reference/kernel/threads/index.rst +++ b/doc/reference/kernel/threads/index.rst @@ -485,8 +485,8 @@ The following code illustrates the ways a thread can terminate. /* thread terminates at end of entry point function */ } -If CONFIG_USERSPACE is enabled, aborting a thread will additionally mark the -thread and stack objects as uninitialized so that they may be re-used. +If :kconfig:`CONFIG_USERSPACE` is enabled, aborting a thread will additionally +mark the thread and stack objects as uninitialized so that they may be re-used. Runtime Statistics ****************** diff --git a/doc/reference/usermode/memory_domain.rst b/doc/reference/usermode/memory_domain.rst index 1fd478967c790..7cb341cc0f864 100644 --- a/doc/reference/usermode/memory_domain.rst +++ b/doc/reference/usermode/memory_domain.rst @@ -49,7 +49,8 @@ Hardware Stack Overflow ``CONFIG_HW_STACK_PROTECTION`` is an optional feature which detects stack buffer overflows when the system is running in supervisor mode. This catches issues when the entire stack buffer has overflowed, and not -individual stack frames, use compiler-assisted CONFIG_STACK_CANARIES for that. +individual stack frames, use compiler-assisted :kconfig:`CONFIG_STACK_CANARIES` +for that. Like any crash in supervisor mode, no guarantees can be made about the overall health of the system after a supervisor mode stack overflow, and any instances From 5055edcc2b53bd5722020b515167b7315cc5956f Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sat, 8 May 2021 23:04:21 +0900 Subject: [PATCH 6/7] doc: reference: memory_domain: Markup variables in sentences Mark variables up if used in plain sentences for easier reading. Signed-off-by: Yasushi SHOJI --- doc/reference/usermode/memory_domain.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/reference/usermode/memory_domain.rst b/doc/reference/usermode/memory_domain.rst index 7cb341cc0f864..e9de9f58a1ba9 100644 --- a/doc/reference/usermode/memory_domain.rst +++ b/doc/reference/usermode/memory_domain.rst @@ -200,7 +200,7 @@ automatically. Manual Memory Partitions ------------------------ -The following code declares a global array buf, and then declares +The following code declares a global array ``buf``, and then declares a read-write partition for it which may be added to a domain: .. code-block:: c @@ -255,7 +255,7 @@ BSS. */ K_APP_BMEM(my_partition) int var2; -The build system will ensure that the base address of my_partition will +The build system will ensure that the base address of ``my_partition`` will be properly aligned, and the total size of the region conforms to the memory management hardware requirements, adding padding if necessary. From fe01786a3437fc073b14dbbcc0ae6053300f7d73 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sun, 9 May 2021 00:13:34 +0900 Subject: [PATCH 7/7] doc: reference: memory_domain: Update minimal libc The global variables in the Minimal C library are now placed in z_libc_partition, update the document accordingly. Signed-off-by: Yasushi SHOJI --- doc/reference/usermode/memory_domain.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/reference/usermode/memory_domain.rst b/doc/reference/usermode/memory_domain.rst index e9de9f58a1ba9..6bdafee7a13f5 100644 --- a/doc/reference/usermode/memory_domain.rst +++ b/doc/reference/usermode/memory_domain.rst @@ -302,8 +302,8 @@ There are a few memory partitions which are pre-defined by the system: to specific memory domains. - ``z_libc_partition`` - Contains globals required by the C library and runtime. - Required if using newlib. Required if using minimal libc with - ``CONFIG_STACK_CANARIES`` enabled. + Required when using either the Minimal C library or the Newlib C Library. + Required when option:`CONFIG_STACK_CANARIES` is enabled. Library-specific partitions are listed in ``include/app_memory/partitions.h``. For example, to use the MBEDTLS library from user mode, the