Skip to content

ubusd_id: blocking getrandom() stalls boot for minutes on different NanoPi devices  #21

Description

@grische

Summary

Since 4ca0b14 (PR #20), ubusd allocates its object and client IDs with getrandom(buf, len, 0), which waits until the kernel CRNG is initialised. On a device where the CRNG is not seeded early in boot, this call does not return until the pool is ready. While ubusd is waiting, procd does not advance past its ubus stage, so no console login is started and the system looks unresponsive until the CRNG seeds.

I noticed this while working on the NanoPi R2S Plus support (Rockchip RK3328).
random: crng init done appeared only up to 15min on boots, and the console became usable only at that point.
I don't have another NanoPi device available to test this on other hardware right now, but nothing looks board-specific.

What was observed

Here are boot logs from OpenWrt 25.12 (kernel 6.12, ubus 2025.12.02, without PR #20):

[   11.153869] procd: - ubus -
[   11.255208] procd: - init -
              Please press Enter to activate this console.
[   11.844418] urngd: v1.0.2 started.
[   12.000179] random: crng init done

And boot logs from OpenWrt main where the boot stalls (kernel 6.18, ubus 2026.05.23, including PR #20):

[    8.778422] procd: - ubus -
... no further console output; procd does not reach "- init -" ...
[  229.443455] random: crng init done
... boot then proceeds and the console becomes usable ...

Reproduction

What I did:

  1. Built a current snapshot image (ubus 2026.05.23, with PR Misc fixes found by Claude Code AI #20) for the NanoPi R2S Plus and booted it. The console stays silent after procd: - ubus -, and random: crng init done appears after several minutes, after which boot completes.
  2. Built an OpenWrt 25.12 image (ubus 2025.12.02, before PR Misc fixes found by Claude Code AI #20) for the same board. It reaches a login prompt in roughly 20 s.

AI analysis

This board's only entropy source that seeds the pool quickly is urngd, which procd starts after the ubus stage. Because ubusd does not return during the ubus stage, urngd does not get started, so the pool can only fill from slow timer-jitter and interrupt entropy.

Affected code

ubusd_id.c, read_random() after PR #20:

static ssize_t read_random(void *buf, size_t len)
{
#ifdef __linux__
	return getrandom(buf, len, 0);
#else
	... /dev/urandom ...
#endif
}

read_random() is called from ubus_alloc_id(), which runs while ubusd is starting, so the first ID allocation waits for the CRNG. The commit message describes this as intended:

getrandom() avoids opening a file descriptor, works before /dev/urandom is accessible (e.g. early boot inside a container), and blocks until the kernel entropy pool is initialised, making it safer than reading from /dev/urandom directly.

The previous implementation read from /dev/urandom, which returns immediately whether or not the pool is seeded.

Related note

In failsafe mode the system also waited for the CRNG before presenting a shell. That path runs dropbearkey (which generates host keys) before the login hook, and key generation also waits on getrandom. This is a separate component from ubus and is mentioned only because it shares the same underlying condition (a blocking getrandom early in boot on a board whose pool seeds slowly).

Environment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions