Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/release-notes/v3.5.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,20 @@ v3.5.0 is a creator-tools release. Movies record the Zapper and the other expans
- **The Famicom peripherals**: the Family BASIC keyboard from a PS/2 keyboard, the Power Pad on port 2, and the second controller's microphone, gated against the NESdev pages.
- **Provenance disclosures** (the sibling's `docs/provenance.md`, "Disclosures"): three reads that touched derived regions of the oracle while this release was written, none of them used: the oracle's Power Pad region, read before it carried its header (the RTL follows the NESdev page and is gated against it); the comment above the oracle's EEPROM model (the data sheets were followed); and the Namco 163's output level, seen in a search result (the RTL's level comes from the NESdev page's measurements, and the audio comparison is blind to a uniform scale). They are recorded for outside review, not certified clean.
- **The MMC5 detector was checked by mutation**: six deliberate defects, three caught at once; one (the 241st-line exit) is unreachable, since line 240 makes no reads, and the other two were caught after a new stimulus that reads the IRQ status by hand (`mapper5irqack406`).
- **The oracle pin moves to v3.5.0**: the sibling pins this release's merge and re-checks every golden against it before its own release; the counts are in its v3.5.0 notes.
- **The oracle pin moves to v3.5.0** (this release's merge, `5d441f44`): `VERIFY=1` reproduces all 256 stems and 1,156 artifacts byte for byte.
- **The co-simulation ladder**: on the die 315 passed, 0 failed, 4 expected failures; off the die 365 passed, 0 failed, 10 expected failures. The four on both are `chrram-fetch` (18 of 185,725 fetches), *Mississippi Satsujin Jiken* frame 140, *Micro Machines* frame 71 and the Sunsoft 5B timed gate; of the six off the die only, five are where the oracle departs from the NESdev pages or the EEPROM data sheets (two Bandai EEPROM stimuli, the VRC7's WRAM enable, and the VRC6 and MMC5 audio timing), each graded both ways, and one is v3.4.0's *Ganbare Goemon Gaiden* frame 25, eight pixels lost to the SDRAM's CHR latency.
- **New bitstreams** are compiled from the sibling's release commit after this release merges and attached here and to the sibling's release; their seeds, checksums and timing margins are in its v3.5.0 notes.
- **New bitstreams** at seed 2, BUILD_DATE 2026-10-23, each byte-identical across two clean compiles: the off-die `RustyNES_MiSTer-v3.5.0.rbf` (md5 `737048b65de5c712cc5d40fc26b906fa`, setup +0.380 / hold +0.081 ns, SDRAM read capture +0.436 / +1.176 ns over all four corners) and the on-die `RustyNES_MiSTer-v3.5.0-lite.rbf` (md5 `3db762d73bdc3d685638121cd0ee360b`, setup +0.231 / hold +0.074 ns). They replace a seed-3 pair attached for a few hours on 2026-10-23: seed 3 followed an outdated reading of the seed rule (found in the MiSTer release review), and the rebuild also carries that review's fixes, among them two Family BASIC keys held across their twin's release and the off-die OSD's ROM-size message.

## Verification

- `cargo test --workspace --features test-roms,commercial-roms --no-fail-fast`: 3,920 passed, 0 failed, 19 ignored, with the commercial suites at 60/0, 169/0 and 6/0.
- `cargo fmt --check`; `cargo clippy --workspace --all-targets -D warnings`, and again with `scripting`, `scripting,hd-pack`, `retroachievements` and `full`, for both browser builds, and for the excluded `rustynes-cosim`; `cargo doc` with `-D warnings`; the `thumbv7em` `no_std` build; markdownlint. All clean.
- MiSTer: the co-simulation ladders above; the pin re-verification and the two-compile bitstream checks are recorded in the sibling's v3.5.0 notes.
- MiSTer: the co-simulation ladders above; `VERIFY=1` at `5d441f44` (256 stems, 1,156 artifacts); `release-rbf.sh v3.5.0 --no-build` passed every check against the compile date; both release assets downloaded back match the md5 above.

## Install

- Download the pre-built binaries for Linux, macOS, and Windows below.
- The MiSTer bitstreams are attached below once they are compiled from the sibling's release commit, as a release candidate, not hardware-verified: `RustyNES_MiSTer-v3.5.0.rbf` (off-die, needs SDRAM) and `RustyNES_MiSTer-v3.5.0-lite.rbf` (on-die).
- The MiSTer bitstreams are attached below as a release candidate, not hardware-verified: `RustyNES_MiSTer-v3.5.0.rbf` (off-die, needs SDRAM) and `RustyNES_MiSTer-v3.5.0-lite.rbf` (on-die).
- The WebAssembly build is live at [doublegate.github.io/RustyNES](https://doublegate.github.io/RustyNES/).
- The RetroArch core is in RetroArch's Online Updater on the platforms the libretro buildbot publishes to.
- Licensed under GPL-3.0-or-later.
28 changes: 28 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,34 @@ jobs:
with:
cache-key: full-build
- run: cargo build -p rustynes-frontend --features full --locked
# 2026-10-23 (after v3.5.0) — the OS shared-memory store (`shm.rs`,
# `script-ipc`) has a Windows implementation and an Apple one
# (`target_vendor = "apple"`: variadic `shm_open`, `PSHMNAMLEN`, a `u16`
# `st_mode`), each with its own tests, among them v3.5.0's Windows
# grow-while-another-program-holds-it recovery. This job built them and no
# job ran them: the `test` matrix reaches Windows and macOS only on a full
# run and without `script-ipc`, and the feature steps above are Linux only.
# This runs the store's, the script host's and memif's tests on both, on
# every ready PR (macOS: Antigravity, #625).
#
# `full`, not `script-ipc` alone: it is the feature set the step above
# just built, so the dependencies are reused rather than rebuilt under a
# second fingerprint, and it adds the `memif::` tests (Antigravity, #625).
# The checks after it exist because a passing summary proves too little:
# an empty filter exits 0, and libtest ORs its filters, so one group can
# carry the summary while another matches nothing (CodeRabbit and
# Antigravity, #625). So each group must show a passing test, and the
# Windows-only grow-recovery test must be among them by name.
- shell: bash
run: |
set -o pipefail
cargo test -p rustynes-frontend --lib --features full --locked -- shm:: script_host:: memif:: 2>&1 | tee test.log
for group in shm script_host memif; do
grep -qE "^test ${group}::[^ ]+ \.\.\. ok$" test.log || { echo "no passing ${group}:: test ran"; exit 1; }
done
if [ "$RUNNER_OS" = Windows ]; then
grep -qE '^test script_host::tests::a_larger_write_with_a_reader_attached_is_refused_and_the_region_kept \.\.\. ok$' test.log
fi

test-roms:
name: test (test-roms feature)
Expand Down
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,37 @@ cycle-accurate core later replaced.

## [Unreleased]

### Changed

- **CI runs the shared-memory store's Windows and macOS tests**: the Lua IPC
transports' OS shared-memory code has Windows and Apple implementations, and
v3.5.0 added a Windows test for growing a region another program still
holds. The `full-build` job compiled that code on both systems and no job ran
its tests. Both legs now run the store's, the script host's and `memif`'s
tests on the `full` build; each group must show a passing test, and on
Windows the grow test must pass by name (`T-WIN-SCRIPT-IPC-CI`). Its first
run passed on Windows, including the grow test, and failed one test on
macOS: the test that plants a shared-memory object other users can read
changed the object's mode with `fchmod`, which macOS does not support on
such objects. The refusal itself works there; the test now plants with the
mode the system gives a new object and reads that mode back.

### Documentation

- **v3.5.0's release notes file matches the published release**: the MiSTer
pin, bitstream and verification lines were placeholders in the merged file
and were filled in on the release page once the sibling had pinned the merge
and compiled the bitstreams; the file now carries the published text.
- **v3.5.0's Linux download is the plain build**: the release's PGO gate did
not promote. The profile-guided build was byte-identical to the plain one on
the oracle check and measured 4.92% slower (the gate needs more than 3%
faster), as v3.4.1's measured 5.05% slower. The only promotion in the release
history is still v2.9.6's.
- New tickets from the v3.5.0 reviews: `T-MMC5-LATCH-VBLANK` (the MMC5 split
and ExGrafix CHR latches survive VBlank in both cores; a documentation and
test-ROM question first), `T-WIN-SCRIPT-IPC-CI` and `T-V350-REVIEW-RESIDUE`
(suggestions not taken, to be confirmed or refuted).

## [3.5.0] - 2026-10-22 - "Chorus" (creator tools: the full movie input stream, Lua WebSockets and shared memory, an MCP server, speedrun timing and dump checks; and the MiSTer core's MMC5, VRC6, Namco 163, VRC7 and Bandai FCG boards, off the die)

### Breaking
Expand Down
62 changes: 53 additions & 9 deletions crates/rustynes-frontend/src/shm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -884,10 +884,22 @@ mod tests {
#[test]
fn a_squatted_object_with_open_permissions_is_refused() {
use std::ffi::CString;
// Unlinks the planted name when dropped, so a failing assertion below
// does not leave the object in the system's namespace (Antigravity,
// #625); dropped explicitly before the fresh create at the end.
struct Plant(CString);
impl Drop for Plant {
fn drop(&mut self) {
// SAFETY: a valid C string naming the object planted below;
// unlinking a name that is already gone only returns ENOENT.
unsafe { libc::shm_unlink(self.0.as_ptr()) };
}
}
let name = unique("sq");
let c = CString::new(os_object_name(&name)).unwrap();
// SAFETY: a valid C string; a new object, mode 0666 (umask may clear
// some bits; the fchmod below sets them regardless).
// SAFETY: a valid C string; a new object, mode 0666 less the umask.
// Outside Apple the fchmod below then forces 0666; on Apple the plant
// keeps the reduced mode (see there).
let fd = unsafe {
libc::shm_open(
c.as_ptr(),
Expand All @@ -896,22 +908,54 @@ mod tests {
)
};
assert!(fd >= 0, "plant: {}", io::Error::last_os_error());
let plant = Plant(c);
// SAFETY: `fd` is the descriptor just opened.
unsafe {
assert_eq!(libc::ftruncate(fd, 4096), 0);
assert_eq!(libc::fchmod(fd, 0o666), 0);
assert_eq!(unsafe { libc::ftruncate(fd, 4096) }, 0);
// macOS does not implement `fchmod` on a POSIX shared-memory
// descriptor (it fails; found when CI first ran this test there, #625),
// so on Apple the plant keeps the mode `shm_open` gave it: 0666 less
// the umask, 0644 under the usual 022, which others can read and the
// check must refuse. The expected message is built from the mode
// actually read back rather than assumed.
#[cfg(not(target_vendor = "apple"))]
// SAFETY: `fd` is the descriptor just opened.
assert_eq!(unsafe { libc::fchmod(fd, 0o666) }, 0);
// `st_mode` is a u32 on Linux and a u16 on macOS, as in `check_owner`.
#[allow(clippy::useless_conversion)]
// SAFETY: `fd` is open; `st` is a valid out-pointer for `fstat`.
let mode = unsafe {
let mut st: libc::stat = std::mem::zeroed();
assert_eq!(libc::fstat(fd, &raw mut st), 0);
libc::close(fd);
}
u32::from(st.st_mode) & 0o777
};
// Both preconditions only bind on Apple, where the plant keeps the
// umask-reduced mode: an unusual umask such as 0700 leaves 0066, which
// passes the second check but which `open`/`create` cannot open read-
// write, so they would fail with an OS error before the owner check
// runs (CodeRabbit, #625).
assert_eq!(
mode & 0o600,
0o600,
"the plant is mode {mode:03o}: this process's umask removes owner \
read/write, so O_RDWR cannot reach the owner check"
);
assert_ne!(
mode & 0o077,
0,
"the plant is mode {mode:03o}: this process's umask leaves the \
object private, so no squatted object can be planted to test"
);
let expected = format!("mode {mode:03o}");
for err in [
Comment thread
doublegate marked this conversation as resolved.
SharedRegion::open(&name).unwrap_err(),
SharedRegion::create(&name, 4096).unwrap_err(),
] {
assert_eq!(err.kind(), io::ErrorKind::PermissionDenied, "{err}");
assert!(err.to_string().contains("mode 666"), "{err}");
assert!(err.to_string().contains(&expected), "{err}");
}
// Clean up the plant; then the normal path works again.
// SAFETY: a valid C string naming the object planted above.
unsafe { libc::shm_unlink(c.as_ptr()) };
drop(plant);
let r = SharedRegion::create(&name, 4096).expect("a fresh create works");
assert!(r.created());
assert!(SharedRegion::open(&name).is_ok(), "and so does an open");
Expand Down
32 changes: 32 additions & 0 deletions docs/SALVAGE_MANIFEST.md
Original file line number Diff line number Diff line change
Expand Up @@ -364,3 +364,35 @@ ladder worktree copies; `live/turn1.json`, a short-lived TURN credential that
must not be kept. Earlier the same evening, 10 GB of this session's regenerable
build output (a cargo target directory and Verilator `obj_dir` trees) was
deleted to recover a full `/tmp`.

## 2026-10-23 — session f63c2db0, after the v3.5.0 release

Scope set by the maintainer: this session's scratch, curated tier. The dry run
found 60,331 files (4.9 GB) in the session scratchpad. The cut was made by
kind, not by name: build output (cargo `target` / `base-target` trees, `.o`,
`.dwo`, `.bc`, `.rmeta`, Verilator `obj_dir`, wasm `dist-*`), the CodeQL
bundle, databases and repository copies (2.5 GB), downloaded release binaries
and `.rbf` copies (on the GitHub releases and in `salvaged/evidence/v3.5.0-quartus/`),
and any PNG, snapshot, CSV or binary over 256 KB, which takes the golden IRQ
CSVs (`VERIFY=1` rebuilds them from the pinned oracle) and the 137 MB DUT dump.
**Moved: 7,395 files (166 MB)** into `salvaged/tmp-scratch/f63c2db0/`, relative
paths kept, no destination existed, no errors:

- `v350/` (7,267): the v3.5.0 agent and merge work. Oracle worktree scratch
(`ora-mmc5irq`, `ora-desktop`, `ora-script`, `ora-movie`, `ora-mcp`,
`ora-small`), sibling board work (`sib-mmc5`, `sib-namco-vrc6`,
`sib-fcg-vrc7`, `sib-tier`, with the generated stimulus `.nes` files),
`m5det` / `m5merge` / `mmc5eq` (the MMC5 detector change and merge),
the mutation runs, gate logs, review replies for #624 and #69, the seed-sweep
logs in `quartus/`, and the cut, review and PR-body drafts.
- loose files (90) and `mister/`, `v341/`, `cfdocs/`: v3.4.1 and v3.4.2 notes,
merge logs, CI workflow experiments, and a Worker tail record (no credential).

Checked for credentials before writing this entry: the only hits are repository
copies of the hosted service's docs and tests, Cloudflare's own credential
documentation, and the tail record above.

**Left in /tmp:** 53,776 files (5.1 GB) of the kinds above; the 181
background-task output logs; the VeridianOS agent slug (37 files) and the
`rusty2600-target`, `spectre-target` and `octollm-target` build trees, all of
which belong to other projects; the zellij log and harness cache-state JSON.
Loading
Loading