[sched] Detect cycles and bound zombie host chain traversal

Closed
QI
7d ago
+35-2

Summary

authenticate_user() in sched/handle_request.cpp could loop indefinitely if zombie host records form a cycle. This change detects cycles and bounds the number of links followed.

Root cause

When hosts are merged on the web (merge_hosts() in html/inc/host.inc), the old host becomes a "zombie": its userid is set to 0 and rpc_seqno holds the ID of the host it was merged into. When a client sends a zombie's host ID, the scheduler follows these links:

retval = host.lookup_id(g_request->hostid);
while (!retval && host.userid==0) {
    // TODO: check for infinite loop
    retval = host.lookup_id(host.rpc_seqno);
    ...
}

If two zombie host records cross-reference each other (or one references itself), the scheduler loops until CGI timeout while issuing a DB lookup on every iteration.

Fix

  • Track visited host IDs in a small fixed array up to MAX_ZOMBIE_HOST_LINKS (16).
  • Stop immediately if host.rpc_seqno <= 0, if the next ID matches the current host or any previously visited host in the chain, or if MAX_ZOMBIE_HOST_LINKS is reached.
  • Log a MSG_CRITICAL diagnostic when an invalid target (rpc_seqno <= 0), cycle, or excessive chain length is detected, set retval = ERR_DB_NOT_FOUND, reset g_reply->hostid = 0, and fall through to the existing broken zombie chain handling (which creates a new host record).
  • Remove the TODO comment.

Fixes #2433


Summary by cubic

Fixes scheduler stalls when zombie host records form cycles. authenticate_user() follows links from merged host records (userid=0, rpc_seqno → target host), but a self-reference or a cross-reference between two zombies made it loop indefinitely, issuing a DB lookup on every iteration until the CGI timed out.

  • Tracks visited host IDs up to MAX_ZOMBIE_HOST_LINKS (16); stops on self-links, cycles, non-positive target IDs, or the hop limit.
  • Logs an MSG_CRITICAL diagnostic for broken chains and falls through to the existing handling that creates a new host record.

Fixes #2433.