<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-07-21T08:47:52Z</updated>
<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>
<entry>
<title>uloop: fix use-after-free in uloop_handle_processes when a callback deletes a process</title>
<updated>2026-07-08T08:36:58Z</updated>
<author>
<name>Felix Fietkau</name>
</author>
<published>2026-07-04T20:42:56Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/libubox/commit/?id=c08a4ab5312917e21104fd225136b2ae35e79427'/>
<id>urn:sha1:c08a4ab5312917e21104fd225136b2ae35e79427</id>
<content type='text'>
uloop_handle_processes() walked the process list with list_for_each_entry_safe(),
whose cached next entry became dangling if a process callback deleted and freed
another process watcher (duplicate pids are allowed, so a sibling with the same
pid could be freed mid-dispatch). Track the next entry in a module-level pointer
advanced by uloop_process_delete(), matching the signal_consume() fix, so the
loop tolerates arbitrary deletions.

Signed-off-by: Felix Fietkau &lt;nbd@nbd.name&gt;
</content>
</entry>
<entry>
<title>uloop: fix use-after-free in signal_consume when a callback deletes a watcher</title>
<updated>2026-07-08T08:34:44Z</updated>
<author>
<name>Felix Fietkau</name>
</author>
<published>2026-07-04T20:41:40Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/libubox/commit/?id=a9ab90bd1d5d6cf96051700bce395579264d6c5f'/>
<id>urn:sha1:a9ab90bd1d5d6cf96051700bce395579264d6c5f</id>
<content type='text'>
signal_consume() dispatched signal callbacks with list_for_each_entry_safe(),
which caches the next entry. A callback that deleted and freed a different
signal watcher (its own successor in the list) left the iterator pointing at
freed memory, dereferenced on the next iteration. Track the next entry to be
visited in a module-level pointer and advance it from uloop_signal_delete()
when that entry is removed, so the loop stays valid across arbitrary deletions,
mirroring the cur_fds[] fixup used for fd dispatch.

Signed-off-by: Felix Fietkau &lt;nbd@nbd.name&gt;
</content>
</entry>
<entry>
<title>uloop: fix kqueue timer interval arithmetic</title>
<updated>2026-07-08T08:34:23Z</updated>
<author>
<name>Felix Fietkau</name>
</author>
<published>2026-07-04T20:17:12Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/libubox/commit/?id=0c3eec553828b8859b7585b05bebc60e397a0b0e'/>
<id>urn:sha1:0c3eec553828b8859b7585b05bebc60e397a0b0e</id>
<content type='text'>
The kqueue timer computed msecs * 1000 in unsigned int, which overflows
for intervals above roughly 71 minutes and armed a far shorter timer
than requested. timer_next() used the same overflowing expression and,
for a zero interval, spun forever in 'while (t1 &lt; t2) t1 += 0', hanging
the process on the first uloop_interval_remaining() call. Compute the
interval in int64_t and return 0 remaining for a non-positive step.

Signed-off-by: Felix Fietkau &lt;nbd@nbd.name&gt;
</content>
</entry>
<entry>
<title>uloop: fix kevent() eventlist size argument in register_kevent</title>
<updated>2026-07-08T08:32:57Z</updated>
<author>
<name>Felix Fietkau</name>
</author>
<published>2026-07-04T20:16:50Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/libubox/commit/?id=08081477ad6c4ce94c50caedb30dd441b4274d25'/>
<id>urn:sha1:08081477ad6c4ce94c50caedb30dd441b4274d25</id>
<content type='text'>
register_kevent() passed a flags value (0 or EV_DELETE) as the nevents
argument of kevent() while giving a NULL eventlist. On deletion this
asked the kernel to copy out up to EV_DELETE (2) events into a NULL
buffer, which fails with EFAULT when events are pending, so
uloop_fd_delete() spuriously returned -1 and pending events could be
consumed and lost. Per-filter EV_DELETE is already set via get_flags(),
so pass 0 as the eventlist size.

Signed-off-by: Felix Fietkau &lt;nbd@nbd.name&gt;
</content>
</entry>
<entry>
<title>utils: guard cbuf_order against zero and one</title>
<updated>2026-07-08T08:32:36Z</updated>
<author>
<name>Felix Fietkau</name>
</author>
<published>2026-07-04T20:16:05Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/libubox/commit/?id=be161d0320daed63823a95f11684fde780aa6802'/>
<id>urn:sha1:be161d0320daed63823a95f11684fde780aa6802</id>
<content type='text'>
cbuf_order() computed 32 - __builtin_clz(x - 1). For x == 1 the argument
to __builtin_clz() is 0, whose result is undefined, and for x == 0 it
returned a nonsensical order of 32. Both feed cbuf_size()/cbuf_alloc().
Return order 0 for x &lt;= 1, which cbuf_size() then clamps to the page
size as before.

Signed-off-by: Felix Fietkau &lt;nbd@nbd.name&gt;
</content>
</entry>
<entry>
<title>ustream: reset byte and buffer counters when freeing buffers</title>
<updated>2026-07-08T08:32:03Z</updated>
<author>
<name>Felix Fietkau</name>
</author>
<published>2026-07-04T20:15:36Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/libubox/commit/?id=72e2b396bd9a9beb370ecbec106af3cc4006fbda'/>
<id>urn:sha1:72e2b396bd9a9beb370ecbec106af3cc4006fbda</id>
<content type='text'>
ustream_free_buffers() cleared the buffer list pointers but left
data_bytes and buffers at their old non-zero values. When a write error
tears down the write buffer list (ustream_state_change_cb()), the stale
w.data_bytes then made ustream_pending_data() and direct data_bytes
checks report buffered data forever. Since writes are disabled after a
write error, the count could never drain, so callers that wait for
pending write data to reach zero before closing stalled indefinitely.
Reset the counters along with the pointers.

Signed-off-by: Felix Fietkau &lt;nbd@nbd.name&gt;
</content>
</entry>
<entry>
<title>usock: retry poll() on EINTR in usock_wait_ready()</title>
<updated>2026-07-08T08:30:08Z</updated>
<author>
<name>Felix Fietkau</name>
</author>
<published>2026-07-04T20:15:24Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/libubox/commit/?id=dda814a9750ace028aae734efbfb7282f2b5758f'/>
<id>urn:sha1:dda814a9750ace028aae734efbfb7282f2b5758f</id>
<content type='text'>
usock_wait_ready() reported a signal interruption of poll() as a hard
failure: poll() is not restarted by SA_RESTART, so any signal arriving
while waiting for a non-blocking connect (e.g. SIGCHLD in a daemon) made
the function return a positive errno and the caller abandon a still-
viable connection, also losing the remaining timeout. Route the wait
through poll_restart(), which retries with the remaining timeout, and
make poll_restart() treat a negative timeout as an infinite wait so the
blocking case keeps working.

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