<feed xmlns='http://www.w3.org/2005/Atom'>
<title>ubus, branch master</title>
<subtitle>OpenWrt system message/RPC bus</subtitle>
<id>https://git-03.infra.openwrt.org/project/ubus/atom?h=master</id>
<link rel='self' href='https://git-03.infra.openwrt.org/project/ubus/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/ubus/'/>
<updated>2026-06-28T14:46:56Z</updated>
<entry>
<title>ubusd_id: use GRND_INSECURE to avoid blocking boot on getrandom()</title>
<updated>2026-06-28T14:46:56Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-06-28T12:01:07Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/ubus/commit/?id=24864e7840b3a02a9ef76284a373f6b2f00b8a9b'/>
<id>urn:sha1:24864e7840b3a02a9ef76284a373f6b2f00b8a9b</id>
<content type='text'>
Commit 4ca0b141e9a7 ("ubusd_id: use getrandom(2) unconditionally on
Linux") switched ID allocation from non-blocking /dev/urandom reads to
getrandom(buf, len, 0). With flags == 0, getrandom() blocks until the
kernel CRNG is fully seeded.

ubusd's first ID allocation runs inside procd's "ubus" stage, and procd
does not advance past that stage until ubusd is up. urngd, which seeds
the entropy pool quickly, is only started by procd after the "ubus"
stage. On boards whose CRNG seeds slowly (e.g. Rockchip RK3328: NanoPi
R2S / R2S Plus / R4S, Orange Pi R1 Plus LTS) nothing seeds the pool
while ubusd waits, so boot stalls for minutes until slow timer/interrupt
entropy fills it.

ubus IDs only need to be hard to guess, not cryptographically strong,
and were sourced from non-blocking /dev/urandom for years. Use
GRND_INSECURE, which returns bytes immediately without waiting for the
CRNG, restoring the previous early-boot behaviour while keeping the
benefits of getrandom() (no file descriptor, works before /dev/urandom
exists). Define GRND_INSECURE for libc headers predating Linux 5.6.

Fixes: https://github.com/openwrt/ubus/issues/21
Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/ubus/pull/24
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>ubusd: use fixed-width types for sequence counters</title>
<updated>2026-05-23T00:48:52Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-21T21:46:14Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/ubus/commit/?id=795b32bb96b611493f423666236e9c1e49e0736c'/>
<id>urn:sha1:795b32bb96b611493f423666236e9c1e49e0736c</id>
<content type='text'>
The message sequence number is a uint16_t on the wire (ubus_msghdr),
and ubusd_acl_seq is emitted as a u32. Declaring the counters with
the matching fixed-width types removes the implicit conversion that
happened on every store or send:

  event_seq               int          -&gt; uint16_t
  ubus_object.invoke_seq  unsigned int -&gt; uint16_t
  ubus_monitor.seq        uint32_t     -&gt; uint16_t
  ubusd_acl_seq           int          -&gt; uint32_t

The values placed on the wire are unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;
Link: https://github.com/openwrt/ubus/pull/20
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>ubusd: use size_t for string and blob length variables</title>
<updated>2026-05-23T00:48:47Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-21T21:45:59Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/ubus/commit/?id=f92ffd289dcc07b20d0fcf34e10e5c685240e5bd'/>
<id>urn:sha1:f92ffd289dcc07b20d0fcf34e10e5c685240e5bd</id>
<content type='text'>
strlen() and blob_raw_len() return size_t; storing their result in
an int relies on an implicit narrowing conversion. Use size_t for
the affected length variables in ubusd_alloc_event_pattern(),
ubus_create_obj_method() and __ubusd_handle_lookup().

ubusd_alloc_event_pattern() checked the length with "&lt;= 0", which is
only ever true for 0 once the variable is unsigned; replace it with
"!len".

Co-Authored-By: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;
Link: https://github.com/openwrt/ubus/pull/20
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>ubusd_acl: use size_t for strlen result in ubusd_acl_alloc_obj</title>
<updated>2026-05-23T00:48:42Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-21T21:21:55Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/ubus/commit/?id=020a64b9b1693295a3fde34147aef48bf47c76ae'/>
<id>urn:sha1:020a64b9b1693295a3fde34147aef48bf47c76ae</id>
<content type='text'>
len held the result of strlen() in an int. It is later passed as
len + 1 to calloc_a(), whose variadic length arguments are read via
va_arg as size_t in __calloc_a(). Passing an int where size_t is
expected is undefined behaviour and can yield a wrong allocation
size on LP64 platforms.

Co-Authored-By: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;
Link: https://github.com/openwrt/ubus/pull/20
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>libubus-io: reset sock.fd to -1 after close on ubus_reconnect error path</title>
<updated>2026-05-23T00:48:37Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-03T21:02:05Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/ubus/commit/?id=747013f6ea05a168d000531553f8de90f8b87b7f'/>
<id>urn:sha1:747013f6ea05a168d000531553f8de90f8b87b7f</id>
<content type='text'>
If ubus_reconnect() fails after the new socket has been opened (HELLO
read failure, header validation, OOM, body read failure, ID validation),
the out_close: handler closes ctx-&gt;sock.fd but leaves the field pointing
at the now-closed descriptor.

A subsequent ubus_shutdown() / ubus_free() then calls close() on that
stale value, and a second reconnect attempt will pass the entry guard at
the top of ubus_reconnect() and close it again. In both cases the kernel
may have recycled the slot for an unrelated descriptor in the meantime,
causing us to close someone else's fd — the same class of bug fixed in
ubus_shutdown() previously.

Set ctx-&gt;sock.fd = -1 alongside the close so the field is a faithful
indicator of socket ownership.

Co-Authored-By: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;
Link: https://github.com/openwrt/ubus/pull/20
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>libubus-io: byte-swap peer in HELLO when storing as local_id</title>
<updated>2026-05-23T00:48:32Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-03T21:01:43Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/ubus/commit/?id=7a068bac5a9b877805f94797915d8d14fd434fb0'/>
<id>urn:sha1:7a068bac5a9b877805f94797915d8d14fd434fb0</id>
<content type='text'>
Every other receive path converts the wire-format ubus_msghdr fields to
host byte order before use (see get_next_msg() and ubusd_main client_cb).
ubus_reconnect() forgot to do this for the HELLO peer field and stored
the raw big-endian value into ctx-&gt;local_id.

On little-endian hosts this means local_id holds a byte-swapped client
id forever:

  - The defensive `if (ctx-&gt;local_id &lt;= UBUS_CLIENT_ID_CHANNEL)` check
    no longer rejects an id of 1, since 0x01000000 &gt; 1. The check was
    intended to catch a server handing out the channel id (1) to a
    regular client.
  - ubus_context_is_channel() can never match an actual non-channel
    local_id by accident, but the value is still semantically wrong and
    breaks any future code that treats local_id as a real id.

Apply be32_to_cpu() to match the other receive paths.

Co-Authored-By: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;
Link: https://github.com/openwrt/ubus/pull/20
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>libubus-io: close recv_fd captured before get_next_msg failure</title>
<updated>2026-05-23T00:48:27Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-03T21:00:25Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/ubus/commit/?id=8188f5ce8564e7f4114e2db2dded1e6361957b2a'/>
<id>urn:sha1:8188f5ce8564e7f4114e2db2dded1e6361957b2a</id>
<content type='text'>
recv_retry() captures any SCM_RIGHTS fd into the caller's *recv_fd as soon
as the first recvmsg succeeds, then continues looping to drain the rest of
the iov. If a subsequent recvmsg in the same call returns 0 (EOF) or
fails fatally, recv_retry returns -1 with the captured fd already written
to the caller's variable. The same hand-off survives errors in the body
read or in the validate/alloc path of get_next_msg().

ubus_handle_data() simply broke out of its loop in those cases, dropping
the fd on the floor. With long-running clients on a flaky link this is a
slow descriptor leak that eventually exhausts the process's fd table.

Reset recv_fd to -1 once it has been handed off to ubus_process_msg(),
and close it at loop exit if anything is still parked there.

Co-Authored-By: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;
Link: https://github.com/openwrt/ubus/pull/20
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>libubus: actually set FD_CLOEXEC on the ubus socket</title>
<updated>2026-05-23T00:48:22Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-03T20:58:55Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/ubus/commit/?id=bcc45ca981fd61e11934776bcee18f628156a7f7'/>
<id>urn:sha1:bcc45ca981fd61e11934776bcee18f628156a7f7</id>
<content type='text'>
fcntl(fd, F_SETFL, ... | O_CLOEXEC) is a no-op for the close-on-exec bit.
F_SETFL only manipulates file *status* flags (O_APPEND, O_ASYNC,
O_NONBLOCK, ...), and silently masks out anything else; close-on-exec is
a file *descriptor* flag that has to be set with F_SETFD/FD_CLOEXEC.

Both ubus_reconnect() and ubus_channel_connect() relied on the broken
form and therefore left the ubus socket inheritable across exec(2). Any
client that fork+exec'd a child (a common pattern in OpenWrt services)
would leak the ubus connection into the new process.

Set FD_CLOEXEC explicitly via F_SETFD in addition to the F_SETFL call
that toggles O_NONBLOCK.

Co-Authored-By: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;
Link: https://github.com/openwrt/ubus/pull/20
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>ubusd: fix NULL dereference on OOM in ubus_msg_enqueue</title>
<updated>2026-05-23T00:48:17Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-03T20:44:02Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/ubus/commit/?id=09d2df45bf3862fc259d5b1e1527c8777e2e5d23'/>
<id>urn:sha1:09d2df45bf3862fc259d5b1e1527c8777e2e5d23</id>
<content type='text'>
ubus_msg_ref() can return NULL when the source message uses an external
buffer and the ubus_msg_new() allocation fails. ubus_msg_enqueue() stored
the result without checking and queued the list entry anyway. Later, when
the queue entry was freed via ubus_msg_list_free() -&gt; ubus_msg_free(NULL),
the NULL message pointer was dereferenced.

Drop the queue entry and return early if the reference cannot be taken,
so we never link a list entry with a NULL message.

Co-Authored-By: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;
Link: https://github.com/openwrt/ubus/pull/20
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>ubusd_id: fix continue in do-while skipping random ID retry</title>
<updated>2026-05-23T00:48:12Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-03T20:43:02Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/ubus/commit/?id=239edcbaaac85fe2dd80c985823a826ad18adabc'/>
<id>urn:sha1:239edcbaaac85fe2dd80c985823a826ad18adabc</id>
<content type='text'>
In ubus_alloc_id() the `continue` statement was intended to discard random
IDs that fall in the reserved system-object range (0..UBUS_SYSTEM_OBJECT_MAX-1)
and retry the read. In a do-while loop, however, `continue` jumps to the
loop-continuation expression, not back to the start of the body. The
expression then calls avl_insert() with the still-reserved ID, and on
success the loop exits — successfully assigning a reserved ID to a client,
object, or object type.

This breaks invariants relied upon elsewhere, e.g. ubusd_alloc_event_pattern()
rejects any object with id &lt; UBUS_SYSTEM_OBJECT_MAX as a permission error,
making event registration fail for any client object that happens to draw a
low random ID.

Wrap the read in an inner do-while so a low ID actually triggers a re-read
before insertion is attempted.

Co-Authored-By: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;
Link: https://github.com/openwrt/ubus/pull/20
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
</feed>
