Skip to content

stdlib/rand: replace weak LCG with xorshift32 PRNG#19244

Merged
xiaoxiang781216 merged 1 commit into
apache:masterfrom
Zepp-Hanzj:fix/rand-period
Jul 3, 2026
Merged

stdlib/rand: replace weak LCG with xorshift32 PRNG#19244
xiaoxiang781216 merged 1 commit into
apache:masterfrom
Zepp-Hanzj:fix/rand-period

Conversation

@Zepp-Hanzj

Copy link
Copy Markdown
Contributor

Summary

The existing first-order LCG (seed = 470001 * seed % 999563) has several quality problems:

  • Output range limited to [1, 999562] (~20 bits) instead of full 32-bit
  • Lower bits have very short periods (8-bit period = 1, 16-bit = 105)
  • Overall period only ~1M, far too short for many applications
  • Causes mbedtls_rsa_gen_key to loop forever when rand() consumption aligns with the cycle length ([BUG] rand()'s cycle seems too short #16760)

Replace the entire order-based LCG implementation (CONFIG_LIBC_RAND_ORDER 0-3) with Marsaglia's xorshift32:

  • Full 32-bit output range
  • Period 2^32 - 1 (~4.29 billion)
  • Fast: just three XOR/shift operations
  • No floating-point math needed
  • No CONFIG_LIBC_RAND_ORDER configuration required

Remove the CONFIG_LIBC_RAND_ORDER Kconfig option and clean up all defconfig references (12 boards) and related comments.

Verification

Built and tested on sim:nsh. Replaced hello_main.c with a test program that checks rand() quality, then restored it after testing.

Build command:

make distclean
tools/configure.sh sim:nsh
make -j$(nproc)

Run command:

printf 'hello\npoweroff\n' | timeout 30 ./nuttx 2>/dev/null

Test output (3 consecutive runs, all identical as expected for deterministic PRNG with same seed):

NuttShell (NSH) NuttX-13.0.0-RC0
nsh> hello
=== rand() quality test ===
Range after 10000 calls: [84907, 2147393180]
Expected: [0, 2147483647]
Full value repeat within 500k: NOT FOUND (period=0)
Expected: NOT FOUND (period should be ~4 billion)
srand(0) first call: 270369 (should be non-zero)
nrand(100) = 89 (should be 0-99)
=== test complete ===
nsh> poweroff

What each check means:

Range [84907, 2147393180] — max value close to INT_MAX (2147483647), old LCG only reached ~999562
Full value repeat: NOT FOUND — no repeat within 500k calls, old LCG repeated within ~1M
srand(0) non-zero — zero seed does not degenerate the generator
nrand(100) in [0,99] — range mapping works correctly

Also confirmed via nm that the NuttX xorshift32 is linked into the binary (not host GLIBC):

$ nm nuttx | grep xorshift32
000000004019d160 b __gcov0.xorshift32
00000000401740a0 d _gcov.xorshift32
00000000400c4052 t xorshift32

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

MemBrowse Memory Report

qemu-armv8a

  • Code: .rodata -16 B, .text.nrand -48 B (-0.0%, 315,310 B)

qemu-intel64

@xiaoxiang781216

Copy link
Copy Markdown
Contributor

@Zepp-Hanzj flash is too small:

====================================================================================
Configuration/Tool: stm32l0538-disco/nsh,CONFIG_ARM_TOOLCHAIN_GNU_EABI
2026-07-01 08:26:56
------------------------------------------------------------------------------------
  Cleaning...
  Configuring...
  Disabling CONFIG_ARM_TOOLCHAIN_GNU_EABI
  Enabling CONFIG_ARM_TOOLCHAIN_GNU_EABI
  Building NuttX...
arm-none-eabi-ld: /github/workspace/sources/nuttx/nuttx section `.data' will not fit in region `flash'
arm-none-eabi-ld: region `flash' overflowed by 20 bytes
make[1]: *** [Makefile:230: nuttx] Error 1
make: *** [tools/Unix.mk:569: nuttx] Error 2
make: Target 'all' not remade because of errors.

@michallenc

Copy link
Copy Markdown
Contributor

@Zepp-Hanzj flash is too small:

====================================================================================
Configuration/Tool: stm32l0538-disco/nsh,CONFIG_ARM_TOOLCHAIN_GNU_EABI
2026-07-01 08:26:56
------------------------------------------------------------------------------------
  Cleaning...
  Configuring...
  Disabling CONFIG_ARM_TOOLCHAIN_GNU_EABI
  Enabling CONFIG_ARM_TOOLCHAIN_GNU_EABI
  Building NuttX...
arm-none-eabi-ld: /github/workspace/sources/nuttx/nuttx section `.data' will not fit in region `flash'
arm-none-eabi-ld: region `flash' overflowed by 20 bytes
make[1]: *** [Makefile:230: nuttx] Error 1
make: *** [tools/Unix.mk:569: nuttx] Error 2
make: Target 'all' not remade because of errors.

This seems like a mainline issue, happens across all new merge requests.

The existing first-order LCG (seed = 470001 * seed % 999563) has several
quality problems:
- Output range limited to [1, 999562] (~20 bits) instead of full 32-bit
- Lower bits have very short periods (8-bit period = 1, 16-bit = 105)
- Overall period only ~1M, far too short for many applications
- Causes mbedtls_rsa_gen_key to loop forever when rand() consumption
  aligns with the cycle length (issue apache#16760)

Replace the entire order-based LCG implementation (CONFIG_LIBC_RAND_ORDER
0-3) with Marsaglia's xorshift32:
- Full 32-bit output range
- Period 2^32 - 1 (~4.29 billion)
- Fast: just three XOR/shift operations
- No floating-point math needed
- No CONFIG_LIBC_RAND_ORDER configuration required

Remove the CONFIG_LIBC_RAND_ORDER Kconfig option and clean up all
defconfig references (12 boards) and related comments.

Signed-off-by: hanzhijian <hanzhijian@zepp.com>
@xiaoxiang781216

Copy link
Copy Markdown
Contributor

@Zepp-Hanzj please fix:

../nuttx/tools/checkpatch.sh -c -u -m -g  f0c94bb64acff1471d4ba5b23b1e0e5c8d57019c..HEAD
❌ Missing Signed-off-by
❌ Missing Signed-off-by
Used config files:
    1: .codespellrc
Some checks failed. For contributing guidelines, see:
  https://github.com/apache/nuttx/blob/master/CONTRIBUTING.md

@Zepp-Hanzj

Copy link
Copy Markdown
Contributor Author

@xiaoxiang781216 Sorry for the noise. The two empty "ci: retrigger build" commits were added to retrigger CI after the stm32l0538-disco flash overflow issue was resolved on upstream master. They have been removed now — the PR contains only the single actual change commit. CI should pass cleanly this time.

@xiaoxiang781216 xiaoxiang781216 merged commit 9e46738 into apache:master Jul 3, 2026
53 checks passed
@Zepp-Hanzj Zepp-Hanzj deleted the fix/rand-period branch July 3, 2026 09:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants