Repository navigation
Conversation
srpatcha
left a comment
There was a problem hiding this comment.
Thanks for the forensic work here, @Kartikey1306 — this is the rare "repair" PR where every line of the description is checkable, and it all checked out.
What I verified locally (Linux, GCC 13, Release, vbox_test):
- master fails exactly as described:
services/linux/src/linux_security.c:695: error: #else after #else. -
git diff 3a00bd9 pr-144 -- kernel/src/task.cis only #130'swake_armedhunk, andgit show ec59df9confirms #130 had rolled back #131's scheduler fixes (idle task deletable/suspendable, TCB published beforestack_ptrwas set,g_current_sppointing at the outgoing task).context_switch.Sandkernel_internal.hfrom #131 are still on master, so the restoredtask.cmatches the port.
-
-
- Build clean, 43/43 ctest (including
test_linux_security_pathsandtest_pkg_fetch), 47/47 pytest.
Not verified here: the MSVC leg (thestatic EosConfigchange and theif(UNIX)guard).
- Build clean, 43/43 ctest (including
-
Two nits, neither blocking:
kernel/src/task.c:595-596— two trailing blank lines at EOF that aren't in #131's version; removing them makes "nothing else" literally true.-
- The follow-up commits use
tests(...)as the type; the repo convention istest. Only matters if you reword when squashing.
Approving.
- The follow-up commits use
srpatcha
left a comment
There was a problem hiding this comment.
Review — eos#144 "fix: repair master after the 09-08 batch merge"
head: a7f81f3 author: Kartikey1306 ci: pass (28 checks green, 3 skipping)
Verdict: master genuinely does not compile and this genuinely fixes it. I rebuilt origin/master and got exactly the claimed failure, then built this head clean and ran the full suite. Every number in the body is exact, including sizeof(EosConfig) to the byte and the git diff 3a00bd9 restoration claim. The kernel restoration is correct where it matters most — I traced g_current_sp/g_next_sp against context_switch.S and the old code was writing the incoming task's PSP into the outgoing task's TCB. Three follow-ups below, none blocking.
Findings
| # | Severity | File:line | Finding | Recommended fix |
|---|---|---|---|---|
| 1 | Medium | core/include/eos/config.h:99; workaround at tests/test_firmware.c:78,103,121,157,171 |
static is the right unblock for MSVC, but it papers over the real defect this PR discovered. Verified: sizeof(EosConfig) == 3953236 bytes (3.77 MB) — exactly the figure in the body — and the five statics put 18.9 MB into test_firmware's .bss (size -A reports .bss 19776352). A 3.8 MB configuration struct is a problem for an embedded OS well beyond this test binary: §6.1 requires "Memory APIs should support static allocation strategies for constrained/safety-oriented deployments", and any target under 4 MB of RAM cannot hold one of these at all, in BSS or on a stack. §25.1's MVP targets are MCUs. |
Out of scope here — do not grow this PR. Open a follow-up issue against EosConfig: the size will be fixed-width char[EOS_MAX_PATH] arrays multiplied by array counts, and the fix is indirection or a smaller path bound, not a bigger stack. Reference this PR as where it surfaced. |
| 2 | Low | kernel/include/eos/kernel.h:103-104 |
eos_task_delete(0) and eos_task_suspend(0) now return EOS_KERN_INVALID where they previously succeeded. That is a behavioural change to two public API functions, and neither declaration carries a doc comment — nothing in the header, and nothing I could find in the tree, tells a caller that handle 0 is the idle task or that it is permanent. Brief items 8 and 11: the change is right, the silence about it is the finding. The PR body explains it as restoring #131, which is true, but the header a caller reads does not. |
Two lines in kernel.h: /* Handle 0 is the idle task and cannot be deleted; returns EOS_KERN_INVALID. */ above each. The PR body should also say plainly that this is an API behaviour change for handle 0, not only a repair. |
| 3 | Low | services/linux/src/linux_security.c:674 |
Pre-existing, adjacent to your diff, raised because you are the one editing this function and .ai/reviewer.md asks about null paths: eos_busybox_install_to_rootfs dereferences bb at is_path_safe(bb->source_dir) before any NULL check, so eos_busybox_install_to_rootfs(NULL, dir) segfaults. The sibling rootfs_dir is passed to is_path_safe too, which may or may not tolerate NULL, but bb-> is an unconditional dereference either way. |
`if (!bb |
Scope note, not a finding. This is four unrelated classes of change in one PR: a one-line build unbreak, CI-gate bookkeeping, test registration, and a substantive kernel behaviour restoration. .ai/architect.md says not to restructure and change behaviour in one commit. The justification holds — the kernel change restores a merge master lost, it is not new work — and the author supplied an exact verification for it, which I reproduced. But a reviewer reading only the title will not look hard at eos_schedule(), and eos_schedule() is the part that deserved the hardest look. Worth splitting next time even when the parts share a root cause.
The kernel change is correct — here is why, checked rather than assumed
This is the only part of the PR that could break a device, so I traced it against the assembly rather than reading the diff.
g_current_sp/g_next_sp (task.c:160-170). context_switch.S:43-46 saves the outgoing PSP through g_current_sp before bl eos_schedule (:49), and :53-55 loads from g_next_sp after. So on exit from eos_schedule both pointers must name the task that is now current — g_next_sp for this switch's restore, g_current_sp for the next PendSV's save. The new g_current_sp = &g_tasks[next].stack_ptr; g_next_sp = g_current_sp; is exactly that. The old line — g_current_sp = &g_tasks[g_current >= 0 ? g_current : next].stack_ptr; evaluated before g_current = next — left g_current_sp pointing at the outgoing task, so the next PendSV would have written the newly-running task's PSP into the previous task's TCB. That is stack-pointer corruption on the second context switch, not a cosmetic bug. Restoring #131 here was not optional.
find_next_task (task.c:128). The added best < 0 || is load-bearing and easy to mistake for a tidy-up. best_prio initialises to 255 and the idle task is created at priority 255 (task.c:236), so t->priority < best_prio is 255 < 255 — false. Before this change find_next_task() could never return the idle task, and eos_kernel_start's if (first < 0) first = 0; at :258 was compensating for it at exactly one call site. eos_schedule had no such fallback.
The entry == NULL skip plus g_tasks[slot].state = EOS_TASK_DELETED (task.c:301). Both are needed together. EOS_TASK_READY == 0 (kernel.h:69), so the memset(&g_tasks[slot], 0, ...) at :300 leaves a half-built TCB in READY — the explicit DELETED closes that window and the entry == NULL guard is the second line of defence. Publishing entry and READY last (:313-314) is the right ordering.
The next < 0 early return (task.c:157). The demotion of the outgoing task now happens before find_next_task(), so on that path the current task is left READY rather than RUNNING. I checked whether it is reachable: it is not, and only because of the other changes in this PR. Slot 0 can no longer be deleted or suspended, the idle task never blocks (idle_task_func only spins/WFI), and find_next_task can now actually return it. Self-healing anyway — the next eos_schedule finds it READY and marks it RUNNING. Worth a comment saying the path is unreachable by construction, since three separate invariants are holding it up.
Critical-section balance. eos_port_enter_critical is paired on all three exits including the two new early returns. tests/test_kernel.c:437-445 tracks depth and asserts on unbalanced exit, so the host suite really does cover the pairing — not the mutual exclusion, which a single-threaded host cannot test.
Evidence check
Reproduced against git archive exports of origin/master and head a7f81f30, Linux, GCC:
| Claim in the PR | Result here |
|---|---|
master does not compile, linux_security.c:695: error: #else after #else |
Confirmed. cmake --build on origin/master exits 2 with services/linux/src/linux_security.c:695:2: error: '#else' after '#else' and :674: error: the conditional began here. |
cmake -DEOS_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=Release → 0 errors |
Confirmed, build rc=0. 89 warnings, all pre-existing -Wstringop-truncation/-Wformat-truncation/-Wunused-result; the only one in a file this PR touches is linux_security.c:458, ~230 lines from the change. No new warning. |
ctest --timeout 120 → 100% tests passed, 43/43 (was 41 registered) |
43/43 passed, 0 failed, 0.92s. test_linux_security_paths and test_pkg_fetch are #42 and #43. |
pytest tests/ → 47 passed |
47 passed in 0.60s. |
EosConfig is 3,953,236 bytes |
3953236, to the byte. |
"git diff 3a00bd9 -- kernel/src/task.c … #131's file plus #130's wake_armed hunk and nothing else" |
Confirmed — 5 insertions, 1 deletion: the wake_armed hunk with its comment. Small correction: it is that plus two trailing blank lines at EOF, which is 2 of the 5 insertions. Harmless, but "and nothing else" is not quite exact; drop them so the claim is. |
The #else choice is the right one of the two: both arms use bb->source_dir, so deleting the (void)bb; arm leaves no unused-parameter warning, and the arm that survives is the one that reports the unsupported platform and returns -1 rather than silently succeeding — fail-closed, per .ai/security.md.
ci-gate is a real gate, not a formality: ci.yml:265-268 pipes toJSON(needs) into .github/scripts/ci-gate-check.sh, which refuses empty input and fails on any result other than success, skipped and cancelled included. Adding windows-test to needs therefore does make the Windows leg blocking. I checked this specifically because "add a job to needs" is worthless against a gate whose step is exit 0.
Architecture conformance
Conforms.
- §21 / Tier 1.
eosis Tier 1 — Foundation.kernel/src/task.cis the scheduler and sync core, which §6's "EoS Kernel / Execution / Scheduler, Threads/Tasks, Priorities" places exactly there. - §5.1. Not engaged. No import, include, link line or manifest entry is added in any direction.
kernel/src/task.cincludes onlyeos/kernel.h,eos/kernel_internal.handeos/arch.h— all at or below its own tier. Nothing in this diff makes the kernel depend on eAI, eNI, eBrowser, eOffice, health or aerospace. - §6, kernel responsibility boundary. Respected: the change is scheduling, memory-region and synchronisation work. Nothing networking, database, AI or distributed is added.
- §6.1, "Deterministic scheduling behavior must be specified by configuration and target": the round-robin
start = (g_current + 1) % EOS_MAX_TASKSwith strict<gives first-found-wins among equal priorities, which is deterministic and is what the restored tests assert. - Brief item 8. The one API-compatibility item is finding 2 —
eos_task_delete/eos_task_suspendon handle 0. No signature, struct layout, manifest field or serialized format changes. if(UNIX)aroundtest_linux_security_pathsnarrows coverage rather than weakening a check, and the reason given is verifiable:linux_security.c:690refuses every install on the#elsearm beforeis_path_safe()is reached, so there is nothing for the suite to assert on MSVC. Accepted. TheNOT_REQUIREDentries added totest_ci_gate.pyare the same shape as the existingbook-build.ymlentry and the guard's own reason-check passes, so they are a classification, not a loosened gate.- No architecture proposal appended. Nothing here shows the master design wrong, stale or silent — the failure was a merge-process failure, and the
services/linux/host-orchestration placement question it brushes against is already covered by the 2026-09-03 proposal "Two Tier-1 repositories both implement build orchestration, and §21 draws no boundary between them".
Proposed changes
- Doc comments on
eos_task_delete/eos_task_suspendfor the handle-0 contract, and one line in the PR body naming it as an API behaviour change (finding 2). - Drop the two trailing blank lines at the end of
kernel/src/task.cso the restoration claim is exact. - Open a follow-up issue for
sizeof(EosConfig)(finding 1). Do not fix it here. - Optional: the NULL guard at
linux_security.c:674(finding 3), and a comment ontask.c:157recording whynext < 0is unreachable.
Not checked
- MSVC — NOT RUN. No Windows host. The
static EosConfigfix, theif(UNIX)guard and the#elserepair on the MSVC leg rest on this PR's own greenBuild & Test (Windows MSVC)check, not on anything I ran. The author says the same. - ARM Cortex-M4 and ARM64 cross builds — NOT RUN. No cross toolchain here. This matters more than usual:
context_switch.Sis ARM-only, so theg_current_sp/g_next_spcorrection is the one change in this PR that hostctestcannot exercise at all. My analysis of it is reading the assembly against the C, not running it. The greenCross-compile ARM Cortex-M4andCross-compile ARM64 kernelchecks prove it compiles, not that the switch is right on hardware. §28's evidence model would call this Implemented, not Validated. - Mutual exclusion in
eos_task_create— NOT RUN. The hosteos_port_enter_criticalis a depth counter (test_kernel.c:437); it verifies the enter/exit pairing on the new early-return paths but cannot verify that interrupts are actually masked. That needs the ARM port on a target. - QEMU simulation — NOT RUN locally. The PR's own
QEMU ARM64 simulation (13 TCs)andQEMU Boot Testchecks are green; I did not reproduce them. - CodeQL, cppcheck, clang-tidy — NOT RUN locally. Green on the PR.
- I did not verify the merge-provenance narrative (which of #115/#118/#119/#130/#131/#132 dropped what). I verified the outcome: master is broken in the ways described, and the
3a00bd9task.c comparison holds. The attribution to specific PRs is taken on the author's word. - I did not check whether the three
skippingchecks (Create GitHub Release,deep-review,mention) are supposed to be required. They are not inci-gate'sneedsandtest_ci_gate.pypasses, so the repo's own guard is satisfied; I did not independently audit the exclusion list.
Automated architecture review of a7f81f30a1c6 — scheduled, model claude-opus-5, checked against the EmbeddedOS Master Design v2.0. Advisory only: this reviewer never approves, requests changes, or merges. Reply here to discuss or push back — a wrong finding is a bug worth reporting.
Nine PRs were merged into master within minutes on 09-08, each on the base it was written against. Master has not compiled since, and the Python guards that would have named the rest never ran because the C build failed first. Compile (every C job, CodeQL, and the ARM64 kernel in the simulation): - services/linux/src/linux_security.c: embeddedos-org#119 and embeddedos-org#132 each added an #else to the same #ifndef _WIN32 in eos_busybox_install_to_rootfs(), so master has "#else after #else". The embeddedos-org#132 arm (`(void)bb;`) is the one removed -- the embeddedos-org#119 arm already uses bb and reports the unsupported platform. Guards from embeddedos-org#121 / embeddedos-org#93 that later merges walked back: - ci.yml: embeddedos-org#132 added windows-test after embeddedos-org#121's gate; the gate did not wait for it, so "CI Gate" could be green with the MSVC leg red. - test_ci_gate.py: embeddedos-org#129 gave eosim-sanity.yml and simulation-test.yml a path-filtered pull_request trigger (they test their own edits); a path-filtered check cannot be required, so both are recorded in NOT_REQUIRED with the book-build.yml reason. - tests/CMakeLists.txt: test_linux_security_paths (embeddedos-org#119) and test_pkg_fetch (embeddedos-org#115) had no add_executable(). embeddedos-org#115's replay replaced embeddedos-org#119's block with its own, and embeddedos-org#118's replay replaced that; two suites compiled against nothing. Both registered again. 41 -> 43 suites. - tests/test_kernel.c: four tests from embeddedos-org#130 and embeddedos-org#131 were defined and never called -- their RUN() lines did not survive the replay of main(). And the one that was not bookkeeping: - kernel/src/task.c: embeddedos-org#130 was merged after embeddedos-org#131 from a base that predates it, and its copy of task.c replaced embeddedos-org#131's. embeddedos-org#131 had also flattened 574 CRLF line endings, so its 1166-line diff hid a 39/21 change and the replay took embeddedos-org#130's side wholesale. Master kept embeddedos-org#131's tests and lost its kernel: the idle task could be deleted and suspended, a half-initialised TCB was published to the scheduler before its stack existed, and eos_schedule() pointed g_current_sp at the outgoing task. test_idle_task_is_permanent fails on master the moment it is called. embeddedos-org#131's task.c diff re-applied on top of embeddedos-org#130's; the result is embeddedos-org#131's file plus embeddedos-org#130's wake_armed hunk and nothing else (verified by diff against 3a00bd9). Verified locally (macOS, clang): Release build clean, 43/43 ctest; the README default configuration builds; 47/47 pytest. Not in this PR: the nightly "Upstream drift" job builds eBoot at master and eBoot master is broken separately (its own repair PR); bump EBOOT_COMMIT in eos-simulation.yml once that lands.
Re-registering it put it in front of the MSVC leg (embeddedos-org#132) for the first time, where <unistd.h> does not exist. The suite probes with access(), open(O_CREAT) and rmdir to prove refused inputs never reach a shell; on Windows the library's own #else arm refuses every install/build call before is_path_safe() matters, so there is nothing for it to test there.
… gives 1 MB Re-registering the POSIX-only suite let the MSVC leg reach ctest for the first time, and test_firmware died there with SEGFAULT: five tests declare a stack EosConfig (sizeof 3,953,236 on this ABI), which the 8 MB default stack on Linux and macOS absorbs and MSVC's 1 MB does not. test_config.c already keeps its EosConfig static for the same reason; the same here.
kernel.h declared eos_task_delete() and eos_task_suspend() without saying what handle 0 is. It is the idle task, it is permanent, and both calls return EOS_KERN_INVALID for it -- as they do for an out-of-range handle or an empty slot. The doc comments now say so, in the header's Doxygen style. eos_schedule() has an early return for "no runnable task" that is unreachable by construction: slot 0 cannot be deleted or suspended, the idle task only spins or waits for an interrupt and so never blocks, and find_next_task() can select it because of the "best < 0 ||" term. The comment records that reasoning, and that the return is self-healing if it were ever taken: the outgoing task stays READY and the next eos_schedule() marks it RUNNING. task.c also carried two stray blank lines at EOF from the re-apply of embeddedos-org#131; with them gone, the diff against 3a00bd9 is embeddedos-org#130's wake_armed hunk plus this comment and nothing else.
…rootfs is_path_safe() refuses NULL, but eos_busybox_install_to_rootfs() read bb->source_dir to hand it to the predicate, so a NULL bb reached the dereference before the guard could see it: a crash rather than the -1 every other refusal in this file produces. Both arguments are now checked first, before either is read. test_linux_security_paths gains a case for it: (NULL, dir) and (&bb, NULL) both return -1, and the directory gets no /init. With the guard removed and the target rebuilt, the suite dies with SIGSEGV (exit 139) on the first call; with it restored, the suite passes.
Records under Unreleased what embeddedos-org#144 did and why: the "#else after #else" that stopped master compiling after the 09-08 batch merge, the guards the replays walked back (two suites re-registered, four test_kernel tests wired into main(), windows-test in the CI gate, the two path-filtered simulation workflows classified), and the NULL guard in eos_busybox_install_to_rootfs(). The scheduler change gets its own Changed entry because it is a contract: eos_task_delete(0) and eos_task_suspend(0) return EOS_KERN_INVALID again, as embeddedos-org#131 specified before embeddedos-org#130's replay replaced its task.c. Changed sits above Fixed, the order the older releases in this file use.
…end checks The eos_schedule() comment claimed the early return was self-healing. It is not: find_next_task() scans every slot including the outgoing one, so next < 0 means nothing is READY, and the return leaves the state exactly as the canary check left it. The comment now says so, and that recovery is a fault handler's job rather than the scheduler's. eos_task_suspend() refuses a slot whose entry is NULL, which is what the doc comment now says instead of the looser "holds no task"; a slot the canary check marked DELETED still has its entry set and is accepted. tests/CMakeLists.txt: the reason test_linux_security_paths is POSIX-only is that the shell-building bodies are compiled out on Windows, not that the predicate is skipped there.
a7f81f3 to
9569226
Compare
|
Thanks for tracing
Current head: Release build clean, |
|
@srpatcha — your approval at |
srpatcha
left a comment
There was a problem hiding this comment.
Review — eos#144 "fix: repair master after the 09-08 batch merge"
head: 9569226 author: Kartikey1306 ci: pass (29 green, 0 red; only Create GitHub Release skipped)
Verdict: Follow-up review. All three previous findings are answered, and two of the new
commits correct mistakes in my previous report rather than merely acting on it — the author
is right both times and I have checked both against the code. One new Low, on a silent-skip
idiom the new test inherits from the file around it. Nothing blocking.
Status of the previous findings (eos-144-a7f81f30.md)
| # | Prev. severity | Status | Evidence |
|---|---|---|---|
| 1 | Medium — sizeof(EosConfig) is 3.95 MB |
Correctly deferred, and filed | eos#148 is open: "sizeof(EosConfig) is 3.95 MB — too large for a stack, for MSVC, and for any MCU target". Not touched in this PR, which is what the previous review asked for — it is an EosConfig layout problem, not a merge repair. Still an open defect against §6.1 and §25.1; it is now tracked rather than carried in a PR body. |
| 2 | Low — handle-0 API change undocumented | Resolved | 5c19f01. kernel/include/eos/kernel.h:103-123 documents both functions. I checked the doc comments against the implementations rather than assuming: eos_task_delete at task.c:358-360 returns EOS_KERN_INVALID for h == 0, h >= EOS_MAX_TASKS, state == DELETED or entry == NULL — the header says "handle 0, an out-of-range handle, or a slot that holds no task", which is exactly that set. eos_task_suspend at :373 is h == 0 || h >= EOS_MAX_TASKS || entry == NULL, and the header says so including the "never created, or deleted" gloss, which is right because eos_task_delete sets entry = NULL. The CHANGELOG carries it under Changed, not Fixed, which is the correct section for a behaviour change. |
| 3 | Low — eos_busybox_install_to_rootfs(NULL, …) segfaults |
Resolved, with a test that proves it | 9c0ada7. services/linux/src/linux_security.c:673 is now if (!bb || !rootfs_dir) return -1; ahead of both is_path_safe() calls. Negative control: with that one line deleted and the suite rebuilt, test_linux_security_paths dies with exit 139 (SIGSEGV); with it restored, exit 0, "All linux_security path checks passed". The author's reported 139 is exact. The test also asserts the rootfs got no /init, which is the right assertion — "refused" has to mean "wrote nothing", not just "returned −1". |
| — | Trailing blank lines at EOF of task.c |
Resolved | 9569226 removes both, so the "git diff 3a00bd9 is the wake_armed hunk and nothing else" claim is now exact rather than nearly exact. |
Two corrections to my previous report
Recording these because the previous report is in this PR's thread and a reader will hit it
first. In both cases the author's new text is right and mine was wrong.
1. "The next < 0 path is self-healing anyway." It is not, and the new comment at
kernel/src/task.c:159-171 says so correctly. find_next_task() (:124-133) iterates
for (int i = 0; i < EOS_MAX_TASKS; i++) over every slot, including the outgoing task,
which eos_schedule() has just demoted RUNNING → READY two lines earlier. So if anything at
all were READY, find_next_task() would have returned it; next < 0 means nothing is, and
there is no later eos_schedule() that finds a READY task to recover with. The return leaves
g_current_sp and g_next_sp naming the outgoing task and its state as the canary check left
it. The comment's conclusion — that this is a fault handler's job and nothing here should
pretend to recover — is the correct reading, and it is better than the "unreachable, and
self-healing anyway" I wrote.
2. "On Windows the library's own #else arm refuses every install/build call before
is_path_safe() matters." I accepted that from the previous PR body; it is wrong twice, and
1043ea6's rewritten comment in tests/CMakeLists.txt:359-361 is right. First, is_path_safe()
is called at linux_security.c:674, before the #ifndef _WIN32 at :675 — so the
predicate is reached on Windows, it is not bypassed. Second, the #else arm at :691-696 only
returns −1 when bb->source_dir[0] is non-empty; with an empty source_dir it falls straight
through to the /init write, so it does not refuse "every" call. The new comment states the
actual reason the suite is POSIX-only — the shell-building bodies are compiled out, and the
predicate itself is portable and covered by the POSIX legs — which is both true and a better
justification for the if(UNIX) guard than the one I signed off on.
Findings
| # | Severity | File:line | Finding | Recommended fix |
|---|---|---|---|---|
| 1 | Low | tests/test_linux_security_paths.c:741 (and 19 other sites in the same file) |
A failed mkdtemp() silently removes a security assertion from the run. The new test_busybox_install_refuses_null_arguments opens with if (!mkdtemp(rootfs)) { fprintf(stderr, "[SKIP] mkdtemp failed\n"); return; }, so if /tmp is unwritable, read-only or out of inodes the test returns before CHECK(eos_busybox_install_to_rootfs(NULL, rootfs) == -1) ever runs, PASS_IF_CLEAN is never reached, failures stays 0, the binary exits 0 and ctest is green. .ai/security.md names this shape first: "a verification step that cannot run must fail, not pass". This is a pre-existing idiom, not a regression — the file has 20 [SKIP] sites and documents the policy at :40 — and for the genuinely optional ones (make absent at :275, evmctl present at :394) a skip is the right call, because a missing build tool is not a security failure. A mkdtemp() failure is a different thing: it is the harness breaking, not an absent optional dependency, and the two are treated identically. |
Separate them. Leave the missing-tool skips alone; make the mkdtemp() failures fail: if (!mkdtemp(dir)) { fprintf(stderr, "[FAIL] mkdtemp: %s\n", strerror(errno)); failures++; return; }. Twenty one-line changes, or one macro. Out of scope for this PR if you would rather keep the repair tight — say so and it stands; I am recording it because this PR adds the twenty-first site. |
One new finding, Low, on 343 lines across seven commits. The substantive kernel work was
reviewed in detail last time and is unchanged at this head; I re-verified the build and suite
rather than re-deriving the analysis.
Verification performed for this review
Detached worktree at .ai/autoreview/state/scratch/eos-144 off refs/pull/144/head. The eos
checkout was clean before I started and is clean now (0 entries); nothing was committed or
pushed.
| Check | Result |
|---|---|
cmake -B build -DEOS_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=Release → cmake --build --parallel 4 |
PASS — 0 errors, 89 warnings, all pre-existing -Wstringop-truncation/-Wformat-truncation/-Wunused-result. Same count as the previous head: the seven new commits add no warning. |
ctest --output-on-failure --no-tests=error --timeout 120 |
PASS — 43/43, 0.86s |
pytest tests/ -q |
PASS — 47 passed, 0.39s |
./build/tests/test_linux_security_paths directly |
exit 0, "All linux_security path checks passed" |
Negative control, finding 3: deleted if (!bb || !rootfs_dir) return -1;, rebuilt the suite, re-ran |
exit 139, SIGSEGV, core dumped. Restored; git diff --stat clean. The guard is load-bearing and the test proves it. |
Doc comments vs. implementations (task.c:356-380) |
CONFIRMED accurate for both functions, including the "never created, or deleted" gloss on eos_task_suspend. |
find_next_task() scans every slot |
CONFIRMED — task.c:125, for (int i = 0; i < EOS_MAX_TASKS; i++). This is what makes the new next < 0 comment right and my previous "self-healing" claim wrong. |
is_path_safe() is reached on Windows |
CONFIRMED — linux_security.c:674 precedes the #ifndef _WIN32 at :675, and the #else arm at :691 only returns −1 for a non-empty source_dir. |
eos#148 exists and is about EosConfig |
CONFIRMED — open, titled for the 3.95 MB figure |
Was anything weakened? No. Across the seven commits: no test disabled, no lint loosened, no
assertion removed, no || true added, no permission widened. The if(UNIX) guard's comment
changed; the guard itself did not.
Architecture conformance
Conforms, unchanged from the previous review and re-checked against the new commits. §21: eos
is Tier 1 — Foundation, and kernel/src/task.c is the scheduler core that §6 places exactly
there. §5.1 is not engaged: the new commits add two doc comments, one comment block, one NULL
guard, one test function and a CHANGELOG entry — no include, link line, CMake entry or manifest
dependency changes in any direction, so nothing points up a tier and the kernel still depends
on nothing above it. §6's responsibility boundary holds: nothing networking, database, AI or
distributed enters the kernel. Brief item 8 (API compatibility) is now satisfied rather than
outstanding — the handle-0 behaviour change is documented in the public header and declared in
the CHANGELOG's Changed section, which is precisely what finding 2 asked for; silence about
a breaking change was the finding, and the silence is gone. Brief item 11 (documentation) is
likewise closed for this PR: the behaviour that changed and the docs that describe it now agree.
No architecture proposal appended. Nothing in the new commits shows the master design wrong,
stale or silent; the root cause remains a merge-process failure, and the services/linux/
placement question this repo keeps brushing against is already covered by the existing
2026-09-03 proposal.
Proposed changes
None blocking.
Optional, and explicitly out of scope if you want the repair tight:
test_linux_security_paths.c make mkdtemp() failure a failure, not a [SKIP]
(20 sites; missing-tool skips stay as they are)
Tracked elsewhere, do not fix here:
eos#148 sizeof(EosConfig) == 3,953,236
Not checked
- MSVC — NOT RUN. No Windows host. The
static EosConfigworkaround, theif(UNIX)guard
and the#elserepair on the MSVC leg rest on this PR's greenBuild & Test (Windows MSVC)
check, not on anything I ran. My statement above about the Windows#elsearm is read from
the source, not observed on Windows. - ARM Cortex-M4 / ARM64 cross builds — NOT RUN. No cross toolchain here, and this remains
the most important gap:context_switch.Sis ARM-only, so theg_current_sp/g_next_sp
correction — the one change in this PR that could break a device — cannot be exercised by
hostctestat all. The previous review traced it against the assembly and I did not redo
that; the green cross-compile checks prove it compiles, not that the switch is right on
hardware. Under §28 this is Implemented, not Validated. - The
next < 0reasoning is analysis, not execution. I confirmedfind_next_task()scans
every slot and that the comment's claims follow from the code. I did not construct a
kernel state in which the idle task isDELETEDand observe what happens; no host test can,
since the canary check needs a real stack overflow. - Mutual exclusion in
eos_task_create— NOT RUN. The hosteos_port_enter_criticalis a
depth counter; it verifies enter/exit pairing, not that interrupts are masked. - QEMU simulation, CodeQL, cppcheck, clang-tidy — NOT RUN locally. Green on the PR; logs
not read. - The merge-provenance narrative (which of #115/#119/#129/#130/#131/#132 dropped what) is
still taken on the author's word. I verified outcomes, not attribution. sizeof(EosConfig)was not re-measured at this head. The previous review measured
3,953,236 bytes ata7f81f30; nothing in these seven commits touchesconfig.h, so I expect
it unchanged, but I did not re-runsize -A.
Automated architecture review of 95692266f8f5 — scheduled, model claude-opus-5, checked against the EmbeddedOS Master Design v2.0. Advisory only: this reviewer never approves, requests changes, or merges. Reply here to discuss or push back — a wrong finding is a bug worth reporting.
|
Follow-up review read; taking the option you offered on the one open finding.
Nothing else is outstanding here. eos#148 ( |
|
Finding 1 (the Nothing pushed here. The re-approve request above stands; |
|
The follow-up I said would come for the |
|
Status for the merger, 2026-09-17: this PR is green on all checks, 0 behind |
|
@srpatcha — re-approval request, with the facts that make it a two-minute read rather than a re-review. Your approval at The repair commit you approved is unchanged. Then What is new since your approval: 6 follow-up commits, +81/−10 across 7 files, each answering a finding from your automated review — What this unblocks: 8 open PRs are stacked on this head (#151, #152, #154, #156, #158, #160, #163, #165 — each drops to its own diff once this lands), plus your #146, whose only red is inherited from master. Nothing further will be pushed to this branch. Merge order: this first. |
…e, not a skip
Twelve tests in tests/test_linux_security_paths.c opened with
"if (!mkdtemp(dir)) { fprintf(stderr, "[SKIP] mkdtemp failed"); return; }"
-- fifteen mkdtemp() calls -- and one mkdir() and one chmod() were handled
the same way. On a read-only or full /tmp each test returned before its
CHECK()s ran, failures stayed 0, the binary exited 0 and ctest was green.
The review on #144 found it as that PR added the twenty-first site.
The file's policy is right for what it was written for: a missing build
tool -- make, evmctl, setfiles -- or a host that ignores file modes means
the case cannot be exercised here, and [SKIP] is the honest answer. Those
five sites are unchanged. A temp directory the harness cannot create is
not that; it is the harness failing. REQUIRE_TMPDIR() now counts it, names
the file, line, template and errno, says which assertions did not run, and
stops the test. FIXTURE_FAILED() does the same for the mkdir() of the
directory whose name is the injection and for the chmod() of the read-only
rootfs.
Proved both ways with a stub mkdtemp() that always fails, linked into the
suite:
#144's head, old idiom: exit 0, 12 [SKIP] lines, 6 [PASS] lines --
green, twelve assertions silently gone
this commit: exit 1, 12 [FAIL] lines naming mkdtemp and
EROFS, 6 [PASS] lines
A first attempt at that negative control redefined mkdtemp as a macro and
collided with unistd.h's declaration; rc 127 was "no binary", not a result.
The stub is the mutation that actually ran.
Normal run: build clean, all linux_security path checks passed.
Closes #159
For the merger (2026-09-17):
master9ed6831failsCI — eos(34664978812) onservices/linux/src/linux_security.c:695: #else after #else, so every PR branched from it fails the same eight checks — currently #146 (@srpatcha, 34797285223). This PR's head passesCI — eos(34888516389), is 0 behindmaster, and nothing needs to land before it. Merging it takesmastergreen and lets #146 go green on a rebase (merge result built and tested: ctest 43/43, pytest 47).Problem
eos master has not compiled since the 09-08 batch merge (nine PRs within minutes, each replayed onto a master its author never saw).
CI — eos,CodeQL, the ARM64 kernel cross-build insideEoS Full-Stack SimulationandPython Unit Testsare all red ond14b62f.Verified against runs 34176148655 (
CI — eos: Linux, Windows MSVC, and the README default configuration all die on the same line), 34176148660 (CodeQL), 34176148692 (simulation) and 34176148714 (Python), and reproduced locally on master before every fix below.What broke, and which merge broke it
Compile — every C job.
services/linux/src/linux_security.c:695: error: #else after #else. #119 (mine) and #132 each added an#elseto the same#ifndef _WIN32ineos_busybox_install_to_rootfs(). The #132 arm ((void)bb;) is the one removed: the #119 arm already usesbband reports the unsupported platform. I own the interaction.Python guards from #121 / #93 that later merges walked back (
4 failed, 43 passedon master):test_ci_gate.pywindows-testruns on PRs butci-gatedoes not wait for itneedstest_ci_gate.pyeosim-sanity.yml,simulation-test.ymlunclassifiedpull_requesttrigger so they test their own editsNOT_REQUIRED— a path-filtered check cannot be required, same reason asbook-build.yml; the guard's own check that the reason is true of the workflow passestest_cmake_test_registration.pytest_linux_security_paths.c(#119),test_pkg_fetch.c(#115) have noadd_executable()test_cmake_test_registration.pytest_kernel.cfunctions defined, never calledRUN()lines did not survive the replay ofmain()(#130, #131)main()And the one that was not bookkeeping. Calling
test_idle_task_is_permanentfails on master:eos_task_delete(0) == EOS_KERN_INVALIDis false — the idle task can be deleted. #130 was merged after #131 from a base that predates it, and its copy ofkernel/src/task.creplaced #131's. #131 had also flattened 574 CRLF line endings in that file, so its 1166-line diff hid a 39/21 change and the replay took #130's side wholesale. Master kept #131's tests and lost its kernel: the idle task could be deleted and suspended, a half-initialised TCB was published to the scheduler before its stack existed, andeos_schedule()pointedg_current_spat the outgoing task. #131'stask.cdiff is re-applied on top of #130's here; verified bygit diff 3a00bd9 -- kernel/src/task.cthat the result is #131's file plus #130'swake_armedhunk and nothing else.kernel_internal.handcontext_switch.Sfrom #131 had survived.Two more, found by this PR's own CI once the MSVC leg could get past compile for the first time:
test_linux_security_paths.cincludes<unistd.h>; fix(windows): make contributor build portable across MSVC and POSIX hosts #132's MSVC leg postdates fix(linux_security): close the gaps in the shell-path guard, and test it #119 and had never compiled it. The suite probes withaccess()/open(O_CREAT)/rmdir, so it is now registered underif(UNIX)— on Windows the library's own#elsearm refuses every install/build call beforeis_path_safe()matters.test_firmwaresegfaulted on MSVC: five tests declared a stackEosConfig, which is 3,953,236 bytes (sizeof, verified). Linux/macOS's 8 MB default stack absorbs it; MSVC's 1 MB does not.test_config.calready keeps itsEosConfigstatic for exactly this reason; same here.Test plan
Verified, macOS / clang:
Not verified here: the MSVC leg and the ARM Cortex-M4 cross build (no toolchain on this machine). The
#elsefix is preprocessor structure only, identical under every compiler; CI on this PR is the check.Not in this PR
Upstream driftjob builds eBoot atmaster, and eBoot master is broken separately — fix: repair master after the 09-07 batch merge eBoot#115 is that repair. Once it lands,EBOOT_COMMITineos-simulation.ymlshould be bumped past it.Review follow-ups (2026-09-14)
Rebased onto the governance commit; findings applied as separate commits on top (the earlier commits are untouched, so the
tests(type in two of them stays as reviewed — noted for the squash):eos_task_delete(0)andeos_task_suspend(0)now returnEOS_KERN_INVALID. Handle 0 is the idle task and it is permanent; the two declarations inkernel/include/eos/kernel.hnow carry doc comments saying so (finding 2). This restores fix(kernel): preserve scheduler task invariants #131's contract, which Fix task wake timeout at tick zero #130's replay had rolled back.kernel/src/task.c: the two trailing blank lines are gone, sogit diff --ignore-cr-at-eol 3a00bd9 -- kernel/src/task.cis now Fix task wake timeout at tick zero #130'swake_armedhunk plus one new comment block at thenext < 0return and nothing else. That comment records why the return is unreachable in normal operation (slot 0 cannot be deleted or suspended,idle_task_func()never blocks,find_next_task()can now return the idle task) and what actually happens if the canary check deletes the idle task:find_next_task()scans every slot including the outgoing one, sonext < 0means nothing is READY and the return leaves state as the canary check left it — nothing recovers from that, and nothing should; an overflowed idle stack is a fault handler's job.services/linux/src/linux_security.c:eos_busybox_install_to_rootfs()refusesNULLarguments before dereferencingbb(finding 3), with a test intest_linux_security_paths.c; verified the(NULL, dir)call segfaulted without the guard (exit 139).sizeof(EosConfig)= 3,953,236 is filed as sizeof(EosConfig) is 3.95 MB — too large for a stack, for MSVC, and for any MCU target #148 (finding 1), not fixed here.Verified on the current head: Release build clean,
ctest43/43,pytest47 passed, README default configuration builds.Closing issue
Fixes #147