Skip to content

mod_http3: Keep the child's threads off the child pool - #940

Open
mbeijen wants to merge 3 commits into
apache:trunkfrom
mbeijen:h3-child-pool
Open

mbeijen wants to merge 3 commits into
apache:trunkfrom
mbeijen:h3-child-pool

Conversation

@mbeijen

@mbeijen mbeijen commented Nov 4, 2026

Copy link
Copy Markdown

The threads of mod_http3 allocated from the child pool, which is not thread-safe. Under prefork, its allocator has no mutex and is shared with every request pool. prefork also skips the child_stopping hook when a child exits from a signal handler, so the threads could still be running while the child pool was destroyed. Children crashed with SIGSEGV on stop, mostly under prefork.

This gives mod_http3 a pool of its own with a locked allocator, and gives each port acquisition attempt a scratch pool that is cleared on retry (each retry used to leak a socket into the child pool). It also stops the threads from a pre-cleanup of the child pool.

  • This PR does not report or address a security vulnerability (see SECURITY.md and https://www.apache.org/security/#reporting-a-vulnerability).
  • Code follows the httpd style guide.
  • New log messages (level debug or higher) use an empty APLOGNO() tag; numbers are assigned by a committer at merge time (see docs/log-message-tags/README).
  • If the change is user-visible, a changes-entries/*.txt file is included, following the template in README.CHANGES (not needed otherwise).
  • Test cases based on pyhttpd are included where appropriate, in the test/modules/xxx directory matching the modules/xxx module source, or test/modules/core for core server changes. On Unix, verify these pass locally with e.g. make check-pytest PYTEST_DIRS=test/modules/xxx.

@mbeijen mbeijen mentioned this pull request Nov 4, 2026
5 tasks done
Comment thread modules/http3/h3_server.c Resolved
Comment thread modules/http3/h3_server.c Resolved
Comment thread modules/http3/h3_server.c Resolved
The threads of mod_http3 allocated from the child pool, which is not
thread-safe, and kept running while it was destroyed:

- The port acquirer thread, in every child that does not own the UDP
  port, created a socket in the child pool every 200 ms, racing with the
  main thread and never freeing it.
- Under prefork the child pool's allocator has no mutex and is shared
  with the request pools, which the event thread raced with when it
  created connection pools.
- prefork does not run the child_stopping hook when a child exits from
  a signal handler, so neither thread was stopped before the child pool
  was destroyed and exit() ran OpenSSL's cleanup under them.

These crashed children with SIGSEGV on stop, mostly under prefork.

Give mod_http3 a pool of its own with a locked allocator, give each port
acquisition attempt a scratch pool that is cleared on retry, and stop the
threads from a pre-cleanup of the child pool as well.

Generated-by: Claude Code (Claude Opus 5.5)
Review feedback: keep the body of stop_child_threads() in
h3_c1_child_stopping(), which the child pool cleanup now calls, so no
forward declaration is needed. Move the pool creation to h3_threads.c so
other code can reuse it.

* modules/http3/h3_server.c (h3_c1_child_stopping): Fold in
  stop_child_threads().
* modules/http3/h3_threads.c, modules/http3/include/h3_threads.h
  (h3_thread_safe_pool_create): New, from create_h3_pool() in h3_server.c.

Generated-by: Claude Code (Claude Opus 5.5)
Review feedback: the mutex on the allocator of the HTTP/3 pool covers
subpools, not allocations from the pool itself. h3_io_listen_start()
registered its pool cleanup after starting the event thread, which
allocates from the same pool, so the two could allocate at once when
the port acquirer thread started listening. Register it first.

Also destroy the pool when its mutex cannot be created, log when the
port acquirer cannot create its pool, drop an empty APLOGNO() that no
other message in the file has, and keep the declarations in
h3_child_init() at the top of their blocks, as C89 requires.

* modules/http3/h3_io.c (h3_io_listen_start): Register the cleanup
  before starting the event thread.
* modules/http3/h3_server.c (port_acquire_thread_fn): Log the pool
  creation failure.
  (h3_child_init): Declare variables first; drop the APLOGNO().
* modules/http3/h3_threads.c (h3_thread_safe_pool_create): Destroy the
  pool if its mutex cannot be created.
* modules/http3/include/h3_threads.h: Say what the mutex does not cover.

Generated-by: Claude Code (Claude Opus 5.5)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants