[sched] Detect cycles and bound zombie host chain traversal
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 ifMAX_ZOMBIE_HOST_LINKSis reached. - Log a
MSG_CRITICALdiagnostic when an invalid target (rpc_seqno <= 0), cycle, or excessive chain length is detected, setretval = ERR_DB_NOT_FOUND, resetg_reply->hostid = 0, and fall through to the existing broken zombie chain handling (which creates a new host record). - Remove the
TODOcomment.
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_CRITICALdiagnostic for broken chains and falls through to the existing handling that creates a new host record.
Fixes #2433.