Skip to content

Zephyr HTTP server kernel timeout-list corruption on spurious zsock_poll() return

Moderate
d3zd3z published GHSA-g5v9-xmfp-7gxm Aug 19, 2026

Software

zephyr

Affected versions

>= 3.7.0, < 4.4.2

Patched versions

4.4.2

Description

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

  • subsys/net/lib/http

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

Branch Pull request Status
main #111239 merged
v4.4-branch #112942 merged
v4.3-branch #112941 merged
v3.7-branch #112940 merged

For more information

If you have any questions or comments about this advisory:

embargo: 2026-08-16

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
High
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
None
Availability
High

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H

CVE ID

CVE-2026-12521

Weaknesses

Use After Free

The product reuses or references memory after it has been freed. At some point afterward, the memory may be allocated again and saved in another pointer, while the original pointer references a location somewhere within the new allocation. Any operations using the original pointer are no longer valid because the memory belongs to the code that operates on the new pointer. Learn more on MITRE.