The HTTP server core loop http_server_run() in subsys/net/lib/http/http_server_core.c polls the listening, stop, and client sockets with an infinite timeout and treats a zsock_poll() return of 0 as impossible, executing break and returning 0 without reaching its closing: cleanup. However, zsock_poll() (zvfs_poll_internal() in lib/os/zvfs/zvfs_poll.c) can legitimately return 0 even with an infinite timeout: after k_poll() wakes, the ZFD_IOCTL_POLL_UPDATE pass may find every fd reporting no revents (a spurious wakeup), yielding ret == 0.
On that path close_all_sockets() is skipped, so the per-client inactivity_timer (struct k_work_delayable) cancellations in close_client_connection() never run. Control returns to http_server_thread() with server_running still true, which re-enters http_server_init(). http_server_init() then memset()s the ctx->clients array — including the k_work_delayable timeout nodes — while one or more of those timers are still armed and linked in the kernel sys-timeout list.
When the kernel later services such a timeout it operates on a reinitialized object and follows the now-zeroed list links, corrupting the kernel timeout list and producing a delayed fault. The HTTP server loop is driven by unauthenticated remote network peers (CONFIG_HTTP_SERVER, all of HTTP/1/2/3), and connection churn raises the probability of the spurious-wakeup race, so a remote peer can influence the trigger. The realistic impact is a denial of service (kernel crash/hang) from the corrupted timeout list.
The fix replaces break with continue, re-polling on a spurious 0 return, which leaves the sockets and their armed timers intact and never re-initializes the context over live timers.
Affected components
Affected versions
>= 3.7.0, < 4.4.2
Fix
Fixed (merged) in 2bf1972
Fixed in 4.4.2. Release lines that have not yet taken the backport remain affected — see the affected version range above.
Introduced by: Present since the HTTP server subsystem and its inactivity_timer cleanup model were added (pre-v4.4.0); the break-on-zero is in v4.4.0's http_server_run()
Evidence
- subsys/net/lib/http/http_server_core.c:1422-1440 — main loop; pre-fix break (now continue) on zsock_poll()==0 skips the closing: cleanup
- subsys/net/lib/http/http_server_core.c:1533-1535 — closing: label runs close_all_sockets(); the ret==0 break path never reaches it
- subsys/net/lib/http/http_server_core.c:1882-1901 — http_server_thread(): on return 0 with server_running true, re-enters http_server_init() (the again: path)
- subsys/net/lib/http/http_server_core.c:283 — http_server_init() memset()s ctx->clients, zeroing embedded k_work_delayable timers
- subsys/net/lib/http/http_server_core.c:559 — k_work_cancel_delayable(&client->inactivity_timer) in close_client_connection, only reached via close_all_sockets
- subsys/net/lib/http/http_server_core.c:664,722 — inactivity_timer init and k_work_reschedule arm it while a client is active
- include/zephyr/net/http/server.h:506-509 — struct http_client_ctx embeds struct k_work_delayable inactivity_timer
- lib/os/zvfs/zvfs_poll.c:108-169 — POLL_UPDATE pass increments ret only when revents!=0; spurious k_poll wakeup yields ret==0 under infinite timeout
- v4.4.0:subsys/net/lib/http/http_server_core.c — vulnerable 'if (ret == 0) { break; }' present in the released tree (reportable)
Patches
For more information
If you have any questions or comments about this advisory:
embargo: 2026-08-16
The HTTP server core loop
http_server_run()insubsys/net/lib/http/http_server_core.cpolls the listening, stop, and client sockets with an infinite timeout and treats azsock_poll()return of0as impossible, executingbreakand returning0without reaching itsclosing:cleanup. However,zsock_poll()(zvfs_poll_internal()inlib/os/zvfs/zvfs_poll.c) can legitimately return0even with an infinite timeout: afterk_poll()wakes, theZFD_IOCTL_POLL_UPDATEpass may find every fd reporting norevents(a spurious wakeup), yieldingret == 0.On that path
close_all_sockets()is skipped, so the per-clientinactivity_timer(struct k_work_delayable) cancellations inclose_client_connection()never run. Control returns tohttp_server_thread()withserver_runningstill true, which re-entershttp_server_init().http_server_init()thenmemset()s thectx->clientsarray — including thek_work_delayabletimeout nodes — while one or more of those timers are still armed and linked in the kernel sys-timeout list.When the kernel later services such a timeout it operates on a reinitialized object and follows the now-zeroed list links, corrupting the kernel timeout list and producing a delayed fault. The HTTP server loop is driven by unauthenticated remote network peers (
CONFIG_HTTP_SERVER, all of HTTP/1/2/3), and connection churn raises the probability of the spurious-wakeup race, so a remote peer can influence the trigger. The realistic impact is a denial of service (kernel crash/hang) from the corrupted timeout list.The fix replaces
breakwithcontinue, re-polling on a spurious0return, which leaves the sockets and their armed timers intact and never re-initializes the context over live timers.Affected components
subsys/net/lib/httpAffected versions
>= 3.7.0, < 4.4.2Fix
Fixed (merged) in 2bf1972
Fixed in 4.4.2. Release lines that have not yet taken the backport remain affected — see the affected version range above.
Introduced by: Present since the HTTP server subsystem and its inactivity_timer cleanup model were added (pre-v4.4.0); the break-on-zero is in v4.4.0's http_server_run()
Evidence
Patches
mainv4.4-branchv4.3-branchv3.7-branchFor more information
If you have any questions or comments about this advisory:
embargo: 2026-08-16