<feed xmlns='http://www.w3.org/2005/Atom'>
<title>procd, branch master</title>
<subtitle>OpenWrt service / process manager</subtitle>
<id>https://git.openwrt.org/project/procd/atom?h=master</id>
<link rel='self' href='https://git.openwrt.org/project/procd/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/procd/'/>
<updated>2026-09-25T14:53:05Z</updated>
<entry>
<title>jail/seccomp: complete the pre-main syscall allowlists</title>
<updated>2026-09-25T14:53:05Z</updated>
<author>
<name>Daniel Golle</name>
</author>
<published>2026-09-25T14:53:05Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/procd/commit/?id=a6f9ffd4becffac99adf7368b26dde537c721f6f'/>
<id>urn:sha1:a6f9ffd4becffac99adf7368b26dde537c721f6f</id>
<content type='text'>
The loader and pre-main allowlists name the syscalls a 64-bit libc
issues before the workload reaches its entry point. A 32-bit libc sets
its thread pointer with set_tls on ARM and set_thread_area on MIPS and
i386, and maps and inspects its objects with mmap2, fstat64, fstatat64,
statx, fcntl64 and the 64-suffixed statfs calls; glibc and bionic also
read the stack limit with ugetrlimit or getrlimit there. None of these
are in the lists, so the first syscall of ld.so already hits the
profile's default action and a SCMP_ACT_KILL_PROCESS profile kills the
jailed process before the entry breakpoint, which is how umdns dies on
every 32-bit target. A static glibc binary further resolves its origin
in _dl_non_dynamic_init(), reading the /proc/self/exe link with
readlinkat before main, which the static pre-main list omits as well.

Add the names to the lists so the grants and the deltas that revoke
them stay in step; find_syscall() skips a name the target does not
have.

Fixes: f15d0c407c09 ("jail: apply OCI seccomp filters via ptrace syscall injection")
Fixes: openwrt/openwrt#25392
Signed-off-by: Daniel Golle &lt;daniel@makrotopia.org&gt;
</content>
</entry>
<entry>
<title>jail: do not leave a freed uci network path behind</title>
<updated>2026-09-22T17:53:08Z</updated>
<author>
<name>Daniel Golle</name>
</author>
<published>2026-09-22T17:17:22Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/procd/commit/?id=6af440ae47245eb1674ee2b777c758439b01b6bb'/>
<id>urn:sha1:6af440ae47245eb1674ee2b777c758439b01b6bb</id>
<content type='text'>
run_netifd() frees uci_config_network when it gives up, and asprintf()
leaves it undefined when it fails, but both paths leave the pointer set.
jail_network_teardown() only checks it for NULL, so it unlinks and frees
whatever is there a second time when the container is torn down.

Clear the pointer on both paths.

Fixes: c44ab7fa061f ("jail: netifd: generate netifd uci config and mount it")
Signed-off-by: Daniel Golle &lt;daniel@makrotopia.org&gt;
</content>
</entry>
<entry>
<title>jail: fail a container whose netifd cannot start</title>
<updated>2026-09-22T17:53:08Z</updated>
<author>
<name>Daniel Golle</name>
</author>
<published>2026-09-22T16:48:54Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/procd/commit/?id=ec5aff06e7b689e57e8c799df71799a61a9d5cf2'/>
<id>urn:sha1:ec5aff06e7b689e57e8c799df71799a61a9d5cf2</id>
<content type='text'>
jail_network_start() waited five seconds for the container's ubus
socket and then carried on regardless, so a container whose only
network never came up was started anyway and was useless. The bound was
also a guess that has nothing to do with how long the socket takes.

Wait on events instead: the socket appearing, or procd reporting that it
has given up restarting the private ubusd, which is the only way the
socket can fail to appear. A wait that ends without netifd now fails the
container rather than starting it without a network.

Fixes: 1eb4371e2534 ("jail: start ubus and netifd instances for container with netns")
Signed-off-by: Daniel Golle &lt;daniel@makrotopia.org&gt;
</content>
</entry>
<entry>
<title>jail: let procd respawn the container's private ubusd</title>
<updated>2026-09-22T17:53:08Z</updated>
<author>
<name>Daniel Golle</name>
</author>
<published>2026-09-22T16:48:09Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/procd/commit/?id=93eed466d037a514b4a2308028526ac2c1e3648e'/>
<id>urn:sha1:93eed466d037a514b4a2308028526ac2c1e3648e</id>
<content type='text'>
The private ubusd is added as an ordinary procd instance with no respawn
attribute, so procd starts it once and never again. A container whose
ubusd dies keeps a socket file that nothing listens on, and everything
inside it that talks to ubus, the jail's own netifd included, is stuck
for as long as the container runs.

Ask for a respawn with no values of its own, which leaves procd its
defaults, so the sidecar is supervised like any other service.

Fixes: 1eb4371e2534 ("jail: start ubus and netifd instances for container with netns")
Signed-off-by: Daniel Golle &lt;daniel@makrotopia.org&gt;
</content>
</entry>
<entry>
<title>jail: keep a late netifd start from ending a foreign loop</title>
<updated>2026-09-22T17:53:08Z</updated>
<author>
<name>Daniel Golle</name>
</author>
<published>2026-09-22T16:30:26Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/procd/commit/?id=9eda1acffabb586eeb1bc241ceaef82ee5681e43'/>
<id>urn:sha1:9eda1acffabb586eeb1bc241ceaef82ee5681e43</id>
<content type='text'>
jail_network_start() waits for the container's ubus socket in a nested
loop, and both run_netifd() and the give-up timeout leave that loop with
uloop_end(). Once the give-up has fired the inotify watch stays armed,
so a socket that appears later runs run_netifd() from whatever loop is
current by then and ends that one instead, which exits ujail and takes
the container down with it.

End the wait only while it is actually running, and drop the watch once
nothing more is expected from it, on teardown and when arming fails. The
descriptor now starts out invalid so that disarming before it is armed
cannot close an unrelated one.

Fixes: 1eb4371e2534 ("jail: start ubus and netifd instances for container with netns")
Signed-off-by: Daniel Golle &lt;daniel@makrotopia.org&gt;
</content>
</entry>
<entry>
<title>jail: keep a private netifd loop from ending the main loop</title>
<updated>2026-09-22T17:53:08Z</updated>
<author>
<name>Daniel Golle</name>
</author>
<published>2026-09-22T14:43:21Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/procd/commit/?id=49ba4ebb2cd75e811ab80b0607a2259abb89c5ec'/>
<id>urn:sha1:49ba4ebb2cd75e811ab80b0607a2259abb89c5ec</id>
<content type='text'>
uloop_end() sets one global flag and uloop_run() clears it only on
entry. jail_network_start() runs a private nested loop while it waits
for the container's ubus socket, and run_netifd() ends that loop the
only way there is, so the flag stays set after the nested loop returns.

Without prestart hooks the startup chain runs synchronously, so a jail
started with -i sends the start byte and returns into the loop in
main(), which sees the stale flag and exits with status 0, tearing the
container down moments after it came up. A jail waiting for a start
command enters a further loop first, which clears the flag, so only the
immediate path breaks, and that is the path uxc boot uses to autostart
containers.

Clear the flag where the private loop is owned.

Fixes: 1eb4371e2534 ("jail: start ubus and netifd instances for container with netns")
Signed-off-by: Daniel Golle &lt;daniel@makrotopia.org&gt;
</content>
</entry>
<entry>
<title>jail: refuse weightDevice leafWeight before reading it</title>
<updated>2026-09-22T17:53:08Z</updated>
<author>
<name>Daniel Golle</name>
</author>
<published>2026-09-22T10:06:37Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/procd/commit/?id=2fba0d1b7606e822458ecef4e7c8ef13157a4dc5'/>
<id>urn:sha1:2fba0d1b7606e822458ecef4e7c8ef13157a4dc5</id>
<content type='text'>
The per-device leaf weight is refused because the unified hierarchy has
no counterpart for it, but the value is read and range checked first, so
a leaf weight above CGROUP_IO_WEIGHT_MAX answers ERANGE and reports the
number as wrong when the field is not implemented at any value. Refuse
it before reading, as the container-wide leaf weight now does.

devleafweight and leafweight are left without a reader once the read is
gone, and go with it.

Fixes: 16159bb1f70f ("jail: parse OCI cgroups resources")
Signed-off-by: Daniel Golle &lt;daniel@makrotopia.org&gt;
</content>
</entry>
<entry>
<title>service: copy validation rule strings with a known length</title>
<updated>2026-09-22T17:53:08Z</updated>
<author>
<name>Daniel Golle</name>
</author>
<published>2026-09-22T00:04:01Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/procd/commit/?id=3a458d5cebcc7e08c5364ad2c528e97ef8678d89'/>
<id>urn:sha1:3a458d5cebcc7e08c5364ad2c528e97ef8678d89</id>
<content type='text'>
service_validate_add() sizes each vrule with strlen() and then fills it
with strcpy(), so the allocation and the copy take their length from two
separate evaluations of the same blobmsg accessor. GCC cannot relate the
two and rejects the copy under -Warray-bounds:

  service/validate.c:152:17: error: 'strcpy' offset 6 from the object at
  'cur' is out of the bounds of referenced subobject 'name' with type
  'uint8_t[]' at offset 6 [-Werror=array-bounds=]

The read is in bounds. blobmsg_name() casts blob_attr.data[] to struct
blobmsg_hdr and hands back its name[] member, so the compiler sees a
flexible array member of a nested object, gives it zero extent and
refuses every access at the resulting constant offset 6. The attribute
really owns blobmsg_hdrlen(namelen) bytes, which covers the name and its
terminator.

Keep the lengths the allocation already computed and copy with memcpy().
The bound then comes from the expression that sized the buffer rather
than from a second walk of the same string, which is what makes the
relationship between the two visible, to a reader and to the compiler
alike.

Signed-off-by: Daniel Golle &lt;daniel@makrotopia.org&gt;
</content>
</entry>
<entry>
<title>instance, service: signal instances through a pidfd</title>
<updated>2026-09-22T17:53:08Z</updated>
<author>
<name>Daniel Golle</name>
</author>
<published>2026-09-21T23:38:52Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/procd/commit/?id=d0aed960fd45adae43e8d24f8c7139b9e381da97'/>
<id>urn:sha1:d0aed960fd45adae43e8d24f8c7139b9e381da97</id>
<content type='text'>
in-&gt;proc.pid is assigned only in instance_start() and never reset, so
an instance of a service registered with autostart:false still holds
the calloc()'d 0 when service.signal reaches service_handle_kill().
kill(0, sig) then signals procd's whole process group, which for PID 1
is ubusd, netifd, logd and procd itself.

Hold a pidfd instead, as ujail already does for the jailed process.
instance_start() obtains it with pidfd_open() right after fork(), where
the pid cannot yet have been recycled because libubox reaps only from
the event loop; instance_exit() closes it once uloop has reaped the
child and instance_free() closes it for an instance torn down while
running, so a respawn always opens a fresh one and a never-started
instance keeps the -1 set by instance_init(). instance_signal() now
fails with ESRCH instead of signalling anything when no process is
live, which service_handle_kill() maps to UBUS_STATUS_NOT_FOUND.

kill() remains only for a failed pidfd_open(), gated on uloop still
tracking the child so that it cannot be reached with an unset or stale
pid.

Fixes: 28f584ff290d ("service: add service.signal ubus call")
Signed-off-by: Daniel Golle &lt;daniel@makrotopia.org&gt;
</content>
</entry>
<entry>
<title>jail, uxc: run the poststop hooks at delete time</title>
<updated>2026-09-22T17:53:08Z</updated>
<author>
<name>Daniel Golle</name>
</author>
<published>2026-09-21T22:19:51Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/procd/commit/?id=869017287c044880fbec6a916b07b7bc0e9eaaef'/>
<id>urn:sha1:869017287c044880fbec6a916b07b7bc0e9eaaef</id>
<content type='text'>
runtime-spec v1.3.0 runtime.md#lifecycle puts the poststop hooks at
step 13, after the container is destroyed and before delete returns.
ujail runs them when the container process is reaped and exits at once,
so they land between steps 10 and 11, before any delete.

Park a registered container in the stopped state and drive the hooks
from a delete instead, either the new method on container.&lt;name&gt;, whose
reply is deferred until the chain has finished, or the SIGTERM procd
sends an instance it is tearing down. A delete asked for before the
container has stopped is honoured rather than parked, which is what a
forced delete and every procd-driven stop look like; otherwise procd
would SIGKILL the parked supervisor and the hooks would be skipped.

uxc waits for the deferred reply or for ubus.object.remove of
container.&lt;name&gt;, whichever comes first: ubusd_proto_free_client()
frees a dead client's objects without answering anyone blocked on
them, so a ujail that died mid-invoke would never release the caller.

A seccomp handshake that failed on an already reaped payload stops
exiting on its own, and uxc drops a stopped incarnation before creating
over it, instance_update() having nothing to do for a pending process
and an unchanged config. A netifd restart still runs the hooks at
payload exit, re-execing in place with no delete behind it.

Fixes: fc9f614bf701 ("jail: parse and run OCI hooks")
Signed-off-by: Daniel Golle &lt;daniel@makrotopia.org&gt;
</content>
</entry>
</feed>
