<feed xmlns='http://www.w3.org/2005/Atom'>
<title>libubox, branch master</title>
<subtitle>C utility functions for OpenWrt</subtitle>
<id>https://git.openwrt.org/project/libubox/atom?h=master</id>
<link rel='self' href='https://git.openwrt.org/project/libubox/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/libubox/'/>
<updated>2026-11-03T00:17:28Z</updated>
<entry>
<title>uloop: use volatile sig_atomic_t for uloop_cancelled and uloop_status</title>
<updated>2026-11-03T00:17:28Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-04-08T22:15:39Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/libubox/commit/?id=a396f38a8568a1d670c240465cd8d24fdfdf6463'/>
<id>urn:sha1:a396f38a8568a1d670c240465cd8d24fdfdf6463</id>
<content type='text'>
Both variables are written from signal handlers (uloop_handle_sigint)
and read from the main loop. Using plain bool/int without volatile
sig_atomic_t is a data race and undefined behavior. This matches the
fix already applied to do_sigchld.

Co-Authored-By: Claude Opus 4.6 &lt;noreply@anthropic.com&gt;
Link: https://github.com/openwrt/libubox/pull/44
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>blobmsg_json: avoid out-of-bounds read on non-extended attributes</title>
<updated>2026-11-03T00:12:59Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-10-08T21:12:17Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/libubox/commit/?id=20bea2ba777dec6e62e15415ded5ffd8be1a5f18'/>
<id>urn:sha1:20bea2ba777dec6e62e15415ded5ffd8be1a5f18</id>
<content type='text'>
blobmsg_format_element() printed the attribute name whenever it formatted
an element in a table context (without_name == false), calling
blobmsg_name() unconditionally. blobmsg_name() blind-casts the attribute
payload to a struct blobmsg_hdr and returns hdr-&gt;name, and
blobmsg_format_string() then strlen()s that pointer with no length bound.

A non-extended attribute carries no blobmsg name header. Such an
attribute still passes blobmsg_check_attr(attr, false): the name check
returns true for a non-extended attribute when no name is required, and a
garbage attribute declaring itself BLOBMSG_TYPE_TABLE or BLOBMSG_TYPE_ARRAY
has no entry in blob_type[] and so skips content validation entirely.
Its raw bytes are then reinterpreted as a length-prefixed name string and
strlen()d past the end of the attribute, leaking adjacent memory into the
JSON output. Both ubus (via the monitor path) and rpcd forward
attacker-controlled, non-revalidated UBUS_ATTR_DATA to this formatter.

Guard the name access with blob_is_extended() so the name is only read
from attributes that actually carry a name header. For extended
attributes blobmsg_check_attr() has already verified the name is
NUL-terminated within bounds, so the subsequent strlen() is safe.

Add a regression test that crafts a non-extended, TABLE-typed attribute
whose payload ends exactly at the allocation boundary; on an unfixed
build the name strlen() reads past it, which the sanitized test catches.

Reported-by: Erfan-Nasiri &lt;https://github.com/Erfan-Nasiri&gt;
Fixes: 0918243e90e6 ("move json formatting to the blobmsg_json library")
Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/libubox/pull/57
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>uloop: run kqueue interval callbacks after the events are collected</title>
<updated>2026-10-08T11:19:54Z</updated>
<author>
<name>Felix Fietkau</name>
</author>
<published>2026-10-08T11:19:54Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/libubox/commit/?id=da2d205a5cf3e877c45a2eec488218cbaf452e7d'/>
<id>urn:sha1:da2d205a5cf3e877c45a2eec488218cbaf452e7d</id>
<content type='text'>
uloop_fetch_events() invoked an interval callback while it still collected
the events of the batch, and before uloop_run_events() published them in
cur_nfds. If the callback deleted a descriptor whose event was already
collected, uloop_fd_delete() did not drop the event, and uloop invoked the
callback of the deleted descriptor. If the callback cancelled another
interval that fired in the same batch, uloop invoked that interval too,
possibly after its owner freed it.

Collect the interval events first, publish the descriptor events, and then
run the interval callbacks. timer_remove() drops the pending events of a
cancelled interval. As an interval callback may nest uloop_run(), keep the
pending batches on a stack and return the number of descriptor events that
are left.

Signed-off-by: Felix Fietkau &lt;nbd@nbd.name&gt;
</content>
</entry>
<entry>
<title>uloop: report a full socket hangup on kqueue as an error</title>
<updated>2026-10-08T11:18:46Z</updated>
<author>
<name>Felix Fietkau</name>
</author>
<published>2026-10-08T11:18:46Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/libubox/commit/?id=531dbb03cbfaefbbb77a6b93f215ceee52d574b9'/>
<id>urn:sha1:531dbb03cbfaefbbb77a6b93f215ceee52d574b9</id>
<content type='text'>
epoll reports EPOLLHUP for a socket once both directions are shut down,
for example when the peer of a unix stream socket closes it, or when the
peer of a TCP connection closes after the local side shut down writing.
uloop then sets the error flag and removes the descriptor. On kqueue,
EV_EOF on a socket without a pending error only set the eof flag, so the
descriptor stayed registered.

getpeername() fails once both directions of a socket are shut down, and
succeeds while only the peer stopped sending. Use it to tell EPOLLHUP from
EPOLLRDHUP.

Signed-off-by: Felix Fietkau &lt;nbd@nbd.name&gt;
</content>
</entry>
<entry>
<title>uloop: fix edge trigger on kqueue</title>
<updated>2026-10-08T10:30:18Z</updated>
<author>
<name>Felix Fietkau</name>
</author>
<published>2026-10-08T10:29:30Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/libubox/commit/?id=ff8db08443b0928e6b5ad477aecbdb1322a011b6'/>
<id>urn:sha1:ff8db08443b0928e6b5ad477aecbdb1322a011b6</id>
<content type='text'>
Commit 17f4e41ecb80 ("uloop: improve edge trigger reliability on mac os x")
registered an edge-triggered descriptor level-triggered first and marked it
with ULOOP_EDGE_DEFER, to switch it to EV_CLEAR after the first event.
Since commit 75a3b870cace ("uloop: add support for integrating with a
different event loop"), uloop_fd_add() stores the flags of the caller in
fd-&gt;flags, which drops ULOOP_EDGE_DEFER. The switch never runs, and
ULOOP_EDGE_TRIGGER behaves like level trigger.

The switch also cannot work: EV_ADD on an existing filter does not change
its EV_CLEAR state. The filter must be deleted and added again.

Current macOS reports pending data once when a filter is added with
EV_CLEAR, so the deferral is not necessary. Remove ULOOP_EDGE_DEFER and
register EV_CLEAR directly. If the edge trigger mode changes, delete the
registered filters first.

epoll re-arms an edge-triggered descriptor on every uloop_fd_add(), because
EPOLL_CTL_MOD reports a ready descriptor again. Add the filters of an
edge-triggered descriptor again on every call, which does the same on
kqueue.

Signed-off-by: Felix Fietkau &lt;nbd@nbd.name&gt;
</content>
</entry>
<entry>
<title>uloop: report a hangup on kqueue as an error, like epoll</title>
<updated>2026-10-08T10:28:43Z</updated>
<author>
<name>Felix Fietkau</name>
</author>
<published>2026-10-08T10:28:43Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/libubox/commit/?id=6b4394f900af120e90521ed52b54aa2afe26958a'/>
<id>urn:sha1:6b4394f900af120e90521ed52b54aa2afe26958a</id>
<content type='text'>
epoll reports a pipe whose other end was closed with EPOLLHUP or EPOLLERR,
and uloop then sets the error flag and removes the descriptor, unless
ULOOP_ERROR_CB is set. kqueue reports the same condition with EV_EOF, which
uloop treated as end of file only. The descriptor stayed registered, and
the level-triggered filter invoked the callback on every loop iteration.
EV_ERROR, the flag that uloop checked, is only returned for changelist
errors and never for a hangup.

Treat EV_EOF as an error if fflags holds a socket error, or if the
descriptor is not a socket. EV_EOF on a socket without an error means that
the peer stopped sending, which epoll reports with EPOLLRDHUP, so it only
sets the eof flag.

Signed-off-by: Felix Fietkau &lt;nbd@nbd.name&gt;
</content>
</entry>
<entry>
<title>uloop: fill one event entry per descriptor on kqueue</title>
<updated>2026-10-08T10:28:13Z</updated>
<author>
<name>Felix Fietkau</name>
</author>
<published>2026-10-08T10:28:07Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/libubox/commit/?id=f9163076e12d3f99a63a59e7e852ed5f9a82df61'/>
<id>urn:sha1:f9163076e12d3f99a63a59e7e852ed5f9a82df61</id>
<content type='text'>
uloop_fetch_events() stored each kevent at its own index in cur_fds, but
did not write the entry for an interval timer event. uloop_run_events()
then replayed the stale entry from an earlier batch, which invoked a
descriptor callback without an event, or with a pointer to a freed
descriptor.

kqueue also reports read and write readiness as separate events. A
descriptor that is ready for both got two callbacks per batch, and the
second one ran even if uloop had already removed the descriptor.

Fill cur_fds with one entry per descriptor, merge the read and write
events into it, and return the number of entries.

Signed-off-by: Felix Fietkau &lt;nbd@nbd.name&gt;
</content>
</entry>
<entry>
<title>blobmsg: use flexible-array member in blobmsg_name()</title>
<updated>2026-07-21T08:47:52Z</updated>
<author>
<name>Michael Pfeifroth</name>
</author>
<published>2026-07-21T08:46:12Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/libubox/commit/?id=e7608b69283d919d031d13cc8e21692503f5dbea'/>
<id>urn:sha1:e7608b69283d919d031d13cc8e21692503f5dbea</id>
<content type='text'>
blobmsg_name() returned (const char *)(hdr + 1), i.e. a pointer to
the byte immediately after struct blobmsg_hdr. GCC's stringop
analysis treats that region as size 0, so any caller that passes the
result to strcmp()/strlen()/etc. under -Wall -Wstringop-overread
triggers a false-positive diagnostic. For example, building uhttpd
(which uses -Wall -Werror) on aarch64 glibc with GCC 12.3.0 fails
with:

  client.c:302:22: error: 'strcmp' reading 1 or more bytes from a
      region of size 0 [-Werror=stringop-overread]
    302 |    if (!strcmp(blobmsg_name(cur), "URL"))

Return hdr-&gt;name instead. The flexible array member has unknown
size in GCC's object-size model, so no warning is emitted. The
generated pointer is identical.

Signed-off-by: Michael Pfeifroth &lt;micpf@westermo.com&gt;
</content>
</entry>
<entry>
<title>ustream: allow freeing the stream from within notify callbacks</title>
<updated>2026-07-08T16:38:02Z</updated>
<author>
<name>Felix Fietkau</name>
</author>
<published>2026-07-08T16:38:02Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/libubox/commit/?id=17f527fb6c30bf9073104f03337c2b7c03158bdb'/>
<id>urn:sha1:17f527fb6c30bf9073104f03337c2b7c03158bdb</id>
<content type='text'>
Freeing a ustream from notify_read or notify_write left the core and
the fd implementation operating on freed memory: ustream_fill_read()
clears the pending callback flag after notify_read, the fd read loop
keeps filling the stream, ustream_write_pending() evaluates the EOF
state after notify_write and the poll handler continues with write and
error processing afterwards.

Let ustream_free() signal the innermost active dispatch guard through
a flag pointer stored in the stream. A triggered guard forwards the
signal to the next outer one, so nested dispatch sites all bail out
without touching the freed stream. notify_state is invoked as a tail
call and needs no guard.

Signed-off-by: Felix Fietkau &lt;nbd@nbd.name&gt;
</content>
</entry>
<entry>
<title>vlist: pass the tree as comparator context in VLIST_TREE_INIT</title>
<updated>2026-07-08T08:47:08Z</updated>
<author>
<name>Felix Fietkau</name>
</author>
<published>2026-07-04T20:12:19Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/libubox/commit/?id=7677b7a4f3a46f68e6f5ba6818f7b72fdd7dbaa0'/>
<id>urn:sha1:7677b7a4f3a46f68e6f5ba6818f7b72fdd7dbaa0</id>
<content type='text'>
vlist_init() initialises the underlying AVL tree with the vlist_tree as
the comparator context pointer, but the static VLIST_TREE_INIT() macro
passed NULL. A comparator that relies on the vlist convention of
receiving the tree as its third argument therefore worked for trees set
up with vlist_init() but received NULL for trees defined statically with
VLIST_TREE()/VLIST_TREE_INIT(), causing a crash or miscompare. Pass the
tree so both initialisation paths behave identically.

Signed-off-by: Felix Fietkau &lt;nbd@nbd.name&gt;
</content>
</entry>
</feed>
