<feed xmlns='http://www.w3.org/2005/Atom'>
<title>rpcd, branch master</title>
<subtitle>OpenWrt ubus RPC daemon</subtitle>
<id>https://git.openwrt.org/project/rpcd/atom?h=master</id>
<link rel='self' href='https://git.openwrt.org/project/rpcd/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/rpcd/'/>
<updated>2026-06-03T23:20:50Z</updated>
<entry>
<title>cast char arguments to unsigned char for ctype.h functions</title>
<updated>2026-06-03T23:20:50Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-31T16:19:22Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/rpcd/commit/?id=28faf6403792d25b9826043aaf37880624c19568'/>
<id>urn:sha1:28faf6403792d25b9826043aaf37880624c19568</id>
<content type='text'>
isspace()/isxdigit()/isalnum()/tolower() are only defined for arguments
representable as unsigned char or EOF.  Several call sites passed a plain
(signed) char taken from client- or file-controlled data (uci names, the
session id, apk db lines, ACL list entries); a byte &gt;= 0x80 sign-extends
to a negative int, which is undefined behaviour and can index out of
bounds of a ctype lookup table on some C libraries.

Cast the argument to unsigned char at each site.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/rpcd/pull/34
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>iwinfo: fix error handling and backend leak in survey</title>
<updated>2026-06-03T23:20:46Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-31T16:16:22Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/rpcd/commit/?id=2decaec3ef1bb6dc2caf7a4a63081aac82ff7d07'/>
<id>urn:sha1:2decaec3ef1bb6dc2caf7a4a63081aac82ff7d07</id>
<content type='text'>
rpc_iwinfo_survey() opened the iwinfo backend and the "results" blob
array, then on any error (open failed, survey call failed, negative
length) did "return UBUS_STATUS_OK" without sending a reply and without
calling rpc_iwinfo_close().  That leaked the iwinfo backend until the
next iwinfo call, abandoned a half-built reply, and masked an open
failure as success.

Restructure it like the freqlist/txpowerlist/countrylist handlers:
return the open error directly, otherwise emit the (possibly empty)
results array, send the reply and always release the backend.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/rpcd/pull/34
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>ucode: bound recursion when converting blob arguments to ucode values</title>
<updated>2026-06-03T23:20:41Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-31T16:14:05Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/rpcd/commit/?id=cd1d9588da6368af1dbc99596ded90d7e5e027e1'/>
<id>urn:sha1:cd1d9588da6368af1dbc99596ded90d7e5e027e1</id>
<content type='text'>
rpc_ucode_blob_array_to_ucv()/rpc_ucode_blob_to_ucv() recurse once per
nesting level of the incoming request message, which is fully attacker
controlled.  ubus permits messages up to UBUS_MAX_MSGLEN (1 MiB) and
libubox enforces no nesting limit, so a deeply nested array/table
argument to a ucode plugin method can drive tens of thousands of
recursion levels and overflow the stack, crashing rpcd.

Thread a depth counter through the conversion and stop descending past
RPC_UCODE_MAX_NESTING (32) levels, which is far beyond any legitimate
ubus message.  Over-deep subtrees become null values instead of
crashing.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/rpcd/pull/34
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>file: avoid zero-length b64_decode() on empty write data</title>
<updated>2026-06-03T23:20:36Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-31T16:08:25Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/rpcd/commit/?id=79c8087c8e8eb8933980fa6fc584703526388446'/>
<id>urn:sha1:79c8087c8e8eb8933980fa6fc584703526388446</id>
<content type='text'>
rpc_file_write() computes data_len as blobmsg_data_len() - 1, which is 0
for an empty "data" string.  With base64 enabled it then called
b64_decode(data, data, 0); b64_decode() asserts dest size &gt; 0, so a
request like file.write {"data":"","base64":true} aborts the daemon when
libubox is built with assertions enabled (and is a pointless call
otherwise).

Skip the decode when there is no data; the file is still created/
truncated and zero bytes are written, which is the correct result.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/rpcd/pull/34
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>rpc-sys: packagelist: read world file instead of mmap to avoid SIGBUS</title>
<updated>2026-06-03T23:20:31Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-31T15:56:20Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/rpcd/commit/?id=e22aea1a51df4a5bc7197eb77a9194ef2c312d48'/>
<id>urn:sha1:e22aea1a51df4a5bc7197eb77a9194ef2c312d48</id>
<content type='text'>
The /etc/apk/world parser depends on a NUL byte one past the file
contents (world buffer is sized st_size + 1) to terminate its string
scans, and the world[] pointer walk and is_all_or_world() dereference a
sentinel that points at that trailing byte.  The file was mapped with
mmap(st_size + 1); when the file size is an exact multiple of the page
size, the byte at offset st_size lies in a page entirely beyond EOF and
accessing it raises SIGBUS, crashing the daemon.

Read the file into a malloc'd buffer and NUL terminate it explicitly so
the terminator is always backed by real memory.  This also drops the now
unused &lt;sys/mman.h&gt; include.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/rpcd/pull/34
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>rpc-sys: packagelist: close status file on world parsing error paths</title>
<updated>2026-06-03T23:20:26Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-31T15:53:19Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/rpcd/commit/?id=fd4fcdeb186b83bfbe9db09f13994eec3d7162c9'/>
<id>urn:sha1:fd4fcdeb186b83bfbe9db09f13994eec3d7162c9</id>
<content type='text'>
rpc_sys_packagelist() opens /lib/apk/db/installed and then, for the
non-"all" case, opens and parses /etc/apk/world.  Every early error
return in that parsing block (missing world file, fstat failure, empty
or newline-less world, mmap failure) returned without fclose()ing the
already open status file, leaking a FILE/descriptor on each call.  The
most easily triggered case is a system whose installed db exists but
that has no /etc/apk/world.

Close the status file on all of these error paths.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/rpcd/pull/34
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>rc: reap killed child on list "running" check timeout</title>
<updated>2026-06-03T23:20:21Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-31T15:51:59Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/rpcd/commit/?id=f5ffec54d7c74051286f9f6bca533271bd655c0b'/>
<id>urn:sha1:f5ffec54d7c74051286f9f6bca533271bd655c0b</id>
<content type='text'>
When a script's "running" check exceeds RC_LIST_EXEC_TIMEOUT_MS,
rpc_list_exec_timeout_cb() removes the process from uloop and SIGKILLs
it, but never waits on it.  Since uloop_process_delete() stops uloop
from reaping the child, it is left as a zombie for the lifetime of the
daemon.

Add a waitpid() after the kill to reap the just-killed child.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/rpcd/pull/34
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>treat exec failures in forked children with _exit() instead of return</title>
<updated>2026-06-03T23:20:16Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-31T15:51:23Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/rpcd/commit/?id=3037a0e36856159075624fb00b5410d5577ed5fe'/>
<id>urn:sha1:3037a0e36856159075624fb00b5410d5577ed5fe</id>
<content type='text'>
Several handlers fork and, in the child, fall back to "return &lt;ubus
status&gt;" when open()/malloc()/execv() etc. fail.  Returning a ubus
status code from the child does not terminate it: control unwinds back
into the ubus dispatch loop and the child keeps running as a duplicate
rpcd, racing the parent on the ubus socket and pipes.

Replace these child-side returns with _exit(127) so a child that cannot
exec the target simply terminates, which is also what the parent's
WEXITSTATUS based reporting expects.  Affected paths: exec.c (rpc_exec),
file.c (rpc_file_exec_run), sys.c (rpc_cgi_password_set) and plugin.c
(rpc_plugin_register_exec).

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/rpcd/pull/34
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>rc: use a per-request blob_buf for the list reply</title>
<updated>2026-06-03T23:20:12Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-31T15:42:56Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/rpcd/commit/?id=75470f4b5124074ff078ed7052bb756e3b694712'/>
<id>urn:sha1:75470f4b5124074ff078ed7052bb756e3b694712</id>
<content type='text'>
rc_list() built the reply in a function-local "static struct blob_buf",
shared by every invocation, and kept using it across the deferred,
asynchronous directory walk.  Two overlapping "rc list" requests (the
walk waits on forked children, so a second request is easily interleaved)
would both blob_buf_init() and append to the same buffer, corrupting
each other's reply or crashing.

Move the blob_buf into the per-request context so each request owns its
own buffer, and free it when the request completes.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/rpcd/pull/34
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>rc: copy list "name" filter to avoid use-after-free</title>
<updated>2026-06-03T23:20:06Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-31T15:41:10Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/rpcd/commit/?id=0de6668115593f2d316070573d12c29409c89078'/>
<id>urn:sha1:0de6668115593f2d316070573d12c29409c89078</id>
<content type='text'>
rc_list() stored the "name" argument as a bare pointer into the parsed
request message and then deferred the request.  The init.d directory is
walked asynchronously across multiple uloop iterations (each script's
"running" check is a forked child), during which the single per-context
ubus receive buffer that backs the message is overwritten by any other
incoming ubus request.  c-&gt;req_name then points at unrelated/freed data
and is compared against directory entries, leading to wrong matches or a
crash.

Duplicate the name into the context and release it when the request
completes.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/rpcd/pull/34
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
</feed>
