Repository navigation
Conversation
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.
compute_cache_key() accumulated snprintf()'s return value as the write offset. snprintf() returns the length it would have written, so one option that truncates leaves len past the end of input[2048]; the hash append then wrote at input+len and passed sizeof(input) - len, which underflows size_t. With nine 200-byte options followed by one maximal 640-byte option, len reaches 2461 and the write lands 413 bytes past the buffer -- SEGV in compute_cache_key under AddressSanitizer. Appends now clamp to what is actually in the buffer, via a helper that cannot return an offset past the end. Room for the content hash is also reserved up front. The old `len < sizeof(input) - 128` guard was reaching for this, but being checked only before an iteration it did not hold; when options crowded the hash out, two packages built from different sources produced the same cache key and the build reused the wrong artifact. That collision is deterministic, so the regression test asserts on it rather than on the sanitizer. tests/test_scheduler_cache_key.c includes scheduler.c to reach the static, the way test_ed25519_canonical_s.c reaches ed25519_verify.c's. Against the old code the first case fails on a plain build and ASan reports the SEGV; against this one, 4/4 pass and ASan is clean.
| #include <stdio.h> | ||
| #include <string.h> | ||
|
|
||
| #include "../core/src/scheduler.c" |
srpatcha
reviewed
Oct 2, 2026
srpatcha
left a comment
Member
There was a problem hiding this comment.
Reviewed. Approve: the hash space is reserved exactly, and the test fails on master (keys collide) and passes here. Nit: options beyond the 2048-byte buffer are still dropped from the key, so two packages that differ only past that point share a cache key. Hashing incrementally would remove that.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #170
Stacked on #144; review
9569226..e72cf77for this change alone — one commit, three files (+186/−6). #144 repairs master, which does not compile today, so this sits on it rather than onmaster. It is independent of the rest of the eos stack and can land in any order among them; merge order is #144, then this.The defect
core/src/scheduler.c:38-56usedsnprintf()'s return value as the next write offset. That return is the length it would have written, and the loop guard is checked only before an iteration, so one truncating append leaveslenpast the end ofinput[2048]. The hash append then evaluatesinput + len— a pointer past the end — andsizeof(input) - len, which underflowssize_t.Uniform options never expose it: they either all fit, or the guard stops the loop while room remains. It takes appends that walk
lenup near the 1920 guard and then one option too large for what is left:Those widths are what the config parser already accepts (
config.c:329-330: 127-char keys, 511-char values, up toEOS_MAX_OPTIONS= 32), and this runs on everyeos build(scheduler.c:105,:185).The change
core/src/scheduler.c, +40/−6. Appends go throughappend_clamped(), which clamps to what is actually in the buffer and can never return an offset past the end.Room for the content hash is reserved up front. The old
len < sizeof(input) - 128guard was reaching for exactly this and did not hold. It matters beyond tidiness: when options crowd the hash out, two packages built from different sources produce the same cache key, and the build reuses the wrong artifact. Reserving makes the hash always present.Tests
tests/test_scheduler_cache_key.c(new, +146) includescore/src/scheduler.cto reach the static — the same techniquetest_ed25519_canonical_s.cuses oned25519_verify.c, and the CMake comment says so. Four cases: the overflowing shape, the reserve boundary, every option count 0..31, and the no-hash path.The tests assert on the deterministic consequence, not on the sanitizer — a cache-key collision — so they discriminate on an ordinary build:
My first version of this test used uniform 640-byte options and passed against the unfixed code — it never made an append truncate. It is rewritten to the shape above; the arithmetic is in the file's header comment so the next reader does not have to rediscover which shape reaches the bug.
Verified at
e72cf77, gcc/Ninja/Debug,-DEOS_BUILD_TESTS=ON -DEOS_PRODUCT=vbox_test:ctest --no-tests=error --timeout 12044/44 passed (43 before, plus this one).Not run here: the Windows/MSVC leg and the ARM cross build — macOS/arm64 only, and CI covers both. Nothing in this change is platform-specific;
append_clamped()uses onlysnprintfandsize_tarithmetic.