Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

HTTP: a code route can mutate the live route table and static root from a worker thread — unsynchronized writes racing every other worker's lookups

Open
#1,140 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
48/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
c

Research direction

Start at http_conn_thread and the g_server/eigs_http_active handling, then inspect builtin_http_route, builtin_http_route_authed, builtin_http_static, and the existing serving guard used by http_response_header. Run tests/http_readiness.py and add the live mutation case described in the issue; done means mutation fails loudly, /injected remains 404, and the concurrent probe is race-free under the planned tsan-http lane.

Written by the indexing model from the issue text.

Description

area:concurrency area:http bug

Observed (executed on main a18deac, make http)

A code route runs in a per-connection worker thread. The worker gets its own EigsState, but http_conn_thread then re-points the thread-local server accessor at the SPAWNING server so that handle_request reads the real route table:

/* register_http_builtins allocated a scratch Server on the worker state (so
 * http_route/http_serve called inside a code route don't crash) ... Re-point
 * at the spawning Server so handle_request reads the main route table. */
eigs_http_active = main_server;

The comment's intent (a scratch server absorbs config calls from a code route) is defeated two lines later: g_server is (*eigs_http_active), so http_route, http_route_authed and http_static invoked from a code route WRITE the main server's config while every other worker reads it lock-free.

Probe:

r is http_route of ["GET", "/", "page"]
c is http_route of ["GET", "/mutate", "code",
    "http_route of [\"GET\", \"/injected\", \"INJECTED\"]\nhttp_static of [\"/static\", \"/tmp\"]\n\"mutated\""]
http_serve of 24911
before: GET /injected -> 404
        GET /mutate   -> mutated
after:  GET /injected -> 200 INJECTED

One request permanently changed the routing table for the whole process.

Why it is a concurrency defect, not just a design oddity

  • builtin_http_route fills routes[route_count]'s four string fields, then does g_server.route_count++ — a plain non-atomic int with no release fence. A concurrent worker iterating for (i < route_count) can observe the incremented count before the slot's fields are visible and call strcmp on NULL/garbage.
  • builtin_http_static swaps static_prefix/static_dir under no lock while workers dereference them (the old strings are leaked rather than freed, so no UAF today — by accident).
  • The route_count >= MAX_ROUTES check is check-then-act across threads.
  • Exploitation needs a trusted code route that calls these builtins, so this is a correctness/robustness issue, not a remote hole — but it is a genuine data race in the runtime's most concurrent code, and it lives in the same file that #1137 just cleaned up.

Suggested fix

Freeze configuration at http_serve, the way http_response_header already does: builtin_http_route, builtin_http_route_authed and builtin_http_static check g_server.serving (under response_mu, or make it atomic) and raise a loud error once serving. Every lock-free read of routes/static config is then reading immutable data, which is the property the current code silently assumes.

Gate

  • tests/http_readiness.py: a live case with a code route that calls http_route/http_static — the request must fail loudly (500 with the error text) and /injected must remain 404 afterwards; plant: revert the freeze → red.
  • Once #1139 lands a tsan-http lane, a probe hammering /mutate and / concurrently should report the race on today's tree and be silent after the fix.

Found during the concurrency review that followed PR #1138 (2026-09-14). The rest of the file's threading — init responder, response builder, shared store, per-IP table, config caches — reviewed sound.

Dominant language
C
Stars
3
Forks
7
Avg merge
3h 58m
Merged PRs (30d)
105

Getting set up

Open in Codespaces

Starts the project's dev container in your browser, under your own GitHub account.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from InauguralSystems/EigenScript

All issues in InauguralSystems/EigenScript

Similar issues

More C issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.