Commit graph

213 commits

Author SHA1 Message Date
bc3f76b42e
fix(scheduler): invalidate stale builtin_fcfs picker cursors across calls (bug-1686)
User-reported live crash: xce_scheduler SIGSEGV in fcfs_cursor_enter_queue
(builtin_fcfs.c), called from builtin_fcfs_next_job <- sched_job_picker
(picker thread).

priv->cursor/priv->job_cursor (fcfs_plugin_data_t) are raw ordq_item_t*
pointers that persist across SEPARATE next_job() calls, each with its own
independent priv->lock acquisition/release. The existing mutual-exclusion
fix (task 2.4) correctly protects the cursor walk from corruption DURING
one locked call, but does nothing to stop a LATER call from dereferencing
a node a properly-locked, INTERVENING call already freed.
on_queue_update()'s re-sort (remove old ordq_item_t node, insert a fresh
one via ordq_put) is exactly such a free -- with only one queue in the
deployment, the picker's cursor is essentially always parked on it
between calls (sched_queue_is_ready/sched_job_is_ready are unconditional
true stubs today, so the cursor never advances away while jobs remain),
making a queue config update land in that window trivially reachable.

Fixed by invalidating (NULLing) whichever cursor points at a node right
before that node is freed, at all three sites that can free a node the
cursors might reference: on_queue_update's reuse-and-resort path
(priv->cursor + priv->job_cursor in lockstep), builtin_fcfs_on_job_state_change's
!JOB_IS_PEND removal, and fcfs_remove_job_everywhere's cross-queue removal
loop (both job_cursor only, via an ordq_peek-then-invalidate-then-delete
pattern). Always safe: next_job() treats a NULL cursor as "no more
candidates this round", and begin_session() resets both cursors fresh
every round regardless -- worst case is a bounded one-round delay, never
a crash or a permanently lost job.

New test_builtin_fcfs_cursor_uaf.c reproduces the bug fully single-threaded
(next_job() releases the lock between calls, so no real concurrency is
needed to hit the window). RED-phase independently confirmed a real ASAN
heap-use-after-free with the exact same call chain as the live crash
(freed by on_queue_update, read by next_job()); GREEN confirms 0 failures
and no job permanently lost across the invalidation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-25 08:49:43 +00:00
b9f13355df
chore(wolf): checkpoint buglog.json (1027 bugs) 2026-08-25 08:35:40 +00:00
b040dfc068
fix(scheduler): remove double-free in limit plugin config update (bug-1685)
User-reported live crash: xce_scheduler SIGABRT via glibc malloc_printerr
(heap corruption) inside kill_limit(), called from limit_update_config()
during a live config reload.

limit_update_config() called kill_limit(limit) in its error path after a
failed key_add(privdata->limits, ...). But privdata->limits is a kvmgr
created with .datakiller = kill_limit, so on a kvtab-level failure (e.g.
-EEXIST for a duplicate limit name -- exactly the rc=-17 captured in the
crash's own backtrace), xkvmgr_add() already rolls back via vclear() ->
xzarray_clear(), which fires kill_limit(limit) once automatically. The
caller's explicit second call was a double free.

Removed the redundant call; the kvmgr's own configured datakiller already
owns cleanup on this failure path (mirrors the ownership asymmetry
job.c's restore_job_array() already documents for the opposite case,
where its kvmgr has no datakiller configured and the caller must free
failed items itself). One accepted, documented residual gap: a vstore()-
level OOM failure (never reaching the kvtab step) would leak `limit`
instead -- vanishingly rare, and a leak is strictly better than the
guaranteed crash on the common duplicate-name case; xkvmgr_add's return
code doesn't distinguish which sub-case occurred, so there's no clean way
to free conditionally without a kvmgr API change (out of scope).

New test_limit_plugin.c (this plugin had zero test coverage before) --
RED-phase independently reproduced a genuine ASAN heap-use-after-free
matching the live crash's exact call chain; GREEN confirms 0 failures.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-25 08:18:34 +00:00
307f5b82e1
chore(wolf): checkpoint buglog.json (1026 bugs) 2026-08-25 08:00:03 +00:00
6e4f25c464
chore(wolf): checkpoint buglog.json (1025 bugs) 2026-08-25 07:48:53 +00:00
b48364f006
chore(wolf): checkpoint buglog.json (1024 bugs) 2026-08-25 07:17:06 +00:00
56c658dec8
chore(wolf): checkpoint buglog.json (1024 bugs) 2026-08-25 07:05:47 +00:00
6d2668ced3
chore(wolf): checkpoint buglog.json (1023 bugs) 2026-08-25 04:50:30 +00:00
64fb19183a
chore(wolf): checkpoint buglog.json (1023 bugs) 2026-08-25 04:43:10 +00:00
0dccb3a9be
chore(wolf): checkpoint buglog.json (1022 bugs) 2026-08-25 04:31:36 +00:00
070c7869f4
chore(wolf): backfill gitea_issue linkage for bug-1674/1675/1676
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-25 04:07:31 +00:00
1b654ba1ee
chore(wolf): mark bug-1680 fixed
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-25 04:04:13 +00:00
e787cead73
chore(wolf): mark bug-1679 fixed; log bug-1680 (xcecli jobs list -a returns nothing, discovered via bug-1679 verification)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-25 03:44:31 +00:00
8f2e684f21
chore(wolf): mark bug-1678 fixed; log bug-1679 (live job-status pushes never fire, discovered via bug-1678 verification)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-25 03:13:06 +00:00
c5aa64da71
chore(wolf): mark bug-1676 fixed (full round-trip)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-24 15:14:56 +00:00
8174186948
chore(wolf): mark bug-1673/1674/1675/1677 fixed
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-24 14:23:51 +00:00
e83d207afd
fix(wolf): log bug-1678, gitea #43 IS reproducible -- corrects earlier false negative
The prior verification pass (commit 1c9cfc1, same session) restarted
controller+scheduler+worker but left the apiserver running, so it only
ever saw the apiserver's stale, still-correct in-memory job cache from
before the restart -- never the controller's actual post-recovery
state. Repeating the test with the apiserver also restarted (matching
the original report's "restart all daemons" exactly) reproduces the
bug immediately: EXEC_HOST stays permanently blank across 15 polls/30s
while STATUS correctly shows RUN throughout.

Root cause fully traced: dispatch_a_job_() (job.c:1343-1372) sets
job.allocs in memory at dispatch-commit time but the durable
WAL_JOB_DISPATCH fact (persist_submit_job_fact, wal_jobstate_t
{jobid,state}) never carries the allocation. On a full restart,
restore_job() replays only the pre-dispatch WAL_JOB_SUBMIT payload
(allocs empty by construction) and apply_committed_entry()'s
WAL_JOB_DISPATCH case routes through apply_job_update_transition(),
which by design "only ever touches state" -- job.allocs is never
reconstructed. Controller log corroborates: recovery trace shows the
WAL_JOB_DISPATCH replay applying silently (no log on the APPLIED
path), followed by each job's WAL_JOB_START fact correctly logging a
now-redundant "duplicate UPDATE ... no-op".

Fix direction confirmed with the user: WAL_JOB_DISPATCH's payload
needs to carry the scheduler's resource allocation, not just
{jobid, state}, with a corresponding decode/apply path in
apply_committed_entry() -- not implemented here, this is a
reproduce-and-root-cause pass only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-24 13:52:07 +00:00
1c9cfc1f10
fix(wolf): log 4 reproduced Gitea issues (#40-42, #44) + 1 discovered gitea-sync bug
Live-reproduced and root-caused on branch scheduler-framework via a
single-node deploy:

- bug-1673 (gitea #40): apiserver hosts-list MAX_SLOT stuck at 0 for
  workers not exact-name-matched in hosts.yaml -- display-only, C-side
  scheduling correctly uses the wildcard 'default' entry's slot count.
  Reporter's secondary "hard 8-job cap" claim did NOT reproduce.
- bug-1674 (gitea #41): `xcecli jobs list -l` fails outright (not just
  missing timestamp fields as reported) -- two call sites validate
  field lists against JobSubmitRequest instead of JobInfo.
- bug-1675 (gitea #42): connection-refused right after apiserver
  startup surfaces a raw OS error string instead of a friendly message.
- bug-1676 (gitea #44): FROM_HOST always blank -- capnp schema has no
  from_host field, and OnJobUpdated's blind store overwrite wipes the
  apiserver-local value set at creation on the first controller push.

Gitea issue #43 (EXEC_HOST lost after daemon restart) was NOT
reproduced after two restart cycles with WAL-preserving state --
EXEC_HOST/state recovered correctly in every poll. Not logged as a bug;
a related design gap (WAL_JOB_DISPATCH only carries {jobid, state}, not
allocs) was flagged as an unconfirmed latent risk, not a live finding.

bug-1677: scripts/gitea-sync.py's sync_bugs() reads buglog.json as a
bare list but the real format is the {version, bugs: [...]} wrapper --
would crash on any real run. Found via static review while checking
the gitea_issue cross-reference field convention; not executed live.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-24 13:40:47 +00:00
755976fcf2
fix(wolf): renumber 40 buglog entries that collided after Aug 13-17 counter restart
39 duplicate IDs (one tripled: bug-909) in the bug-875-bug-949 range,
where a restarted counter re-used IDs already held by June 21-22
entries. Kept each original (earliest timestamp) untouched and
renumbered the 40 later collisions to bug-1633..bug-1672 (new global
max, up from 1632), preserving array order. Verified no related_bugs
reference pointed at a renumbered entry -- the 10 existing references
to bug-924 all predate the Aug 17 collision and correctly resolve to
the original ENOSPC/overlayfs entry.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-24 13:04:04 +00:00
548ad9b2e2
chore(wolf): checkpoint buglog.json (1013 bugs) 2026-08-23 22:04:06 +00:00
5591f3603f
chore(wolf): checkpoint buglog.json (1013 bugs) 2026-08-23 21:17:20 +00:00
d4ada4474b
chore(wolf): checkpoint buglog.json (1012 bugs) 2026-08-23 19:35:33 +00:00
65390fb111
chore(wolf): checkpoint buglog.json (1011 bugs) 2026-08-23 18:02:38 +00:00
f756c4b09f
chore(wolf): checkpoint buglog.json (1011 bugs) 2026-08-23 17:21:12 +00:00
d87f4bb333
chore(wolf): checkpoint buglog.json (1011 bugs) 2026-08-23 10:13:25 +00:00
006b1b6aea
chore(wolf): checkpoint buglog.json (1011 bugs) 2026-08-23 08:36:15 +00:00
0649050ae8
chore(wolf): checkpoint buglog.json (1010 bugs) 2026-08-23 08:16:37 +00:00
018c1a818c
chore(wolf): checkpoint buglog.json (1009 bugs) 2026-08-23 08:04:48 +00:00
5e34f1f571
chore(wolf): checkpoint buglog.json (1008 bugs) 2026-08-23 04:50:57 +00:00
f8b24a993f
chore(wolf): checkpoint buglog.json (1007 bugs) 2026-08-23 04:10:48 +00:00
45acce4184
chore(wolf): checkpoint buglog.json (1005 bugs) 2026-08-23 02:56:14 +00:00
5d194db9b1
chore(wolf): checkpoint buglog.json (1004 bugs) 2026-08-22 12:48:23 +00:00
75edb7a329
chore(wolf): checkpoint buglog.json (1003 bugs) 2026-08-22 09:50:28 +00:00
c38207458f
chore(wolf): checkpoint buglog.json (1002 bugs) 2026-08-22 08:24:41 +00:00
76ca61547e
chore(wolf): checkpoint buglog.json (1001 bugs) 2026-08-22 07:13:00 +00:00
230083ae5d
chore(wolf): checkpoint buglog.json (1001 bugs) 2026-08-22 03:24:37 +00:00
811b5a11ec
chore(wolf): checkpoint buglog.json (1001 bugs) 2026-08-21 16:10:59 +00:00
044846c773
chore(wolf): checkpoint buglog.json (1001 bugs) 2026-08-21 15:58:38 +00:00
b58311f85f
chore(wolf): checkpoint buglog.json (1000 bugs) 2026-08-21 15:20:27 +00:00
7d47b8fe2a
chore(wolf): checkpoint buglog.json (1000 bugs) 2026-08-21 10:54:52 +00:00
fd53525651
chore(wolf): checkpoint buglog.json (996 bugs) 2026-08-21 09:40:27 +00:00
eeb7a9b4db
chore(wolf): checkpoint buglog.json (995 bugs) 2026-08-21 09:00:26 +00:00
995673e7cf
chore(wolf): checkpoint buglog.json (994 bugs) 2026-08-21 07:26:41 +00:00
5c8edda64c
chore(wolf): checkpoint buglog.json (993 bugs) 2026-08-21 07:00:03 +00:00
340c5a83e8
chore(wolf): checkpoint buglog.json (992 bugs) 2026-08-21 06:47:59 +00:00
13fc840f8c
chore(wolf): checkpoint buglog.json (991 bugs) 2026-08-21 04:19:26 +00:00
176f0cf835
chore(wolf): checkpoint buglog.json (989 bugs) 2026-08-20 16:24:02 +00:00
43c7384fd6
chore(wolf): checkpoint buglog.json (986 bugs) 2026-08-20 15:07:27 +00:00
bc5c669b94
feat(build-time-versioning): worker --version and startup log
Worker uses plain getopt_long(), not argp, so it has no free --version
handling like controller/scheduler. Adds a long-only OPT_VERSION=257
option and a handling case that computes xce_compose_version() into a
static buffer and returns before any startup work (check_user()/
xcecd_init/xcecd_start). Both startup INFO log branches (--peers and
no-peers) extended to log the identical buffer.

Live-verified: --version and both startup-log branches are
byte-identical (captured via a real daemon run under sudo).

Also updates bug-1519 (pre-existing, unrelated ASAN "Joining already
joined thread" abort during worker shutdown): independently reproduced
under real root during this task's review, refining its prior "never
reproduced under real root" note -- not a userns/unshare artifact, still
unfixed, does not affect this task's code path.

Task 3.3/13 of build-time-versioning. This closes out the "Core: C
daemon version display" phase (tasks 3.1-3.3).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 11:47:12 +00:00
854f776733
chore(wolf): checkpoint buglog.json (986 bugs) 2026-08-20 10:50:08 +00:00