Compare commits

...
Author SHA1 Message Date
enricobuehler 6eb89b3f34 Merge pull request 'The lint ratchets (WP2b + WP2c): crate-level gaps closed, the three-workspace hoist, three blocking grep gates' (#171) from worktree-lint-ratchets into main
docker / deploy-docs (push) Blocked by required conditions
windows-host / canary-manifest (push) Blocked by required conditions
windows-host / winget-source (push) Blocked by required conditions
apple / swift (push) Successful in 1m39s
arch / build-publish (push) In progress
windows-drivers / probe-and-proto (push) Successful in 26s
ci / rust-arm64 (push) Successful in 2m18s
windows-drivers / driver-build (push) Successful in 2m15s
windows-host / package (push) In progress
ci / web (push) Successful in 1m27s
android / android (push) Successful in 6m53s
deb / build-publish (push) In progress
deb / build-publish-host (push) In progress
ci / bun-nix (push) Successful in 1m3s
ci / docs-site (push) Successful in 1m52s
windows-msix / package (arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m14s
release / apple (push) Successful in 10m10s
windows-msix / package (x64, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 3m9s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 1m20s
deb / build-publish-client-arm64 (push) Successful in 6m37s
apple / screenshots (push) Successful in 6m16s
ci / rust (push) Successful in 14m7s
flatpak / build-publish (push) In progress
nix / flake (push) In progress
windows / build (x86_64-pc-windows-msvc) (push) Successful in 2m33s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 51s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) In progress
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) In progress
decky / build-publish (push) Successful in 33s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 8s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m23s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 10s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 9s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 10s
docker / apps (., web/Dockerfile, punktfunk-web) (push) In progress
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 10s
docker / builders-arm64cross (push) Waiting to run
2026-08-11 21:57:26 +00:00
enricobuehler bbd26ea82c Merge pull request 'Miri interprets the FFI-free leaf crates, one of them at MSVC layout' (#169) from worktree-miri-ci into main
ci / rust (push) Canceled after 0s
ci / rust-arm64 (push) Canceled after 0s
ci / web (push) Canceled after 0s
ci / docs-site (push) Canceled after 0s
ci / bun-nix (push) Canceled after 0s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Canceled after 0s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Canceled after 0s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Canceled after 0s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Canceled after 0s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Canceled after 0s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Canceled after 0s
docker / builders-arm64cross (push) Canceled after 0s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Canceled after 0s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Canceled after 0s
docker / deploy-docs (push) Canceled after 0s
audit / cargo-audit (push) Successful in 42s
audit / bun-audit (sdk) (push) Successful in 31s
audit / bun-audit (plugin-kit) (push) Successful in 43s
audit / bun-audit (web) (push) Successful in 29s
audit / pnpm-audit (push) Successful in 10s
audit / docs-site-audit (push) Successful in 28s
audit / license-gate (push) Successful in 6m9s
audit / miri (push) Successful in 7m20s
2026-08-11 21:57:24 +00:00
enricobuehler 549fdf238b Merge branch 'main' into worktree-lint-ratchets
apple / swift (pull_request) Successful in 1m45s
apple / screenshots (pull_request) Skipped
windows-drivers / driver-build (pull_request) Successful in 2m1s
ci / rust-arm64 (pull_request) Successful in 1m54s
ci / web (pull_request) Successful in 1m13s
ci / docs-site (pull_request) Successful in 1m17s
windows / build (aarch64-pc-windows-msvc) (pull_request) Successful in 1m58s
ci / bun-nix (pull_request) Successful in 1m15s
ci / rust (pull_request) Successful in 6m1s
windows / build (x86_64-pc-windows-msvc) (pull_request) Successful in 2m44s
windows-drivers / probe-and-proto (pull_request) Successful in 26s
android / android (pull_request) Successful in 7m1s
nix / flake (pull_request) Successful in 16m48s
2026-08-11 21:57:17 +00:00
enricobuehler 3ea411fa39 Merge branch 'main' into worktree-miri-ci
ci / docs-site (pull_request) Successful in 1m16s
ci / bun-nix (pull_request) Successful in 2m10s
ci / web (pull_request) Successful in 2m55s
ci / rust-arm64 (pull_request) Successful in 3m21s
ci / rust (pull_request) In progress
2026-08-11 21:57:15 +00:00
enricobuehler 3b2fcd076d Merge pull request 'chore(api): regenerate openapi.json — #164's unpair change rewrote the unpairClient description without regenerating' (#170) from worktree-openapi-regen into main
ci / rust (push) Canceled after 31s
ci / rust-arm64 (push) Canceled after 29s
ci / web (push) Canceled after 26s
ci / docs-site (push) Canceled after 0s
ci / bun-nix (push) Canceled after 0s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Canceled after 17s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 17s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Canceled after 15s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Canceled after 13s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Canceled after 0s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Canceled after 0s
docker / builders-arm64cross (push) Canceled after 0s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Canceled after 0s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Canceled after 0s
docker / deploy-docs (push) Canceled after 0s
2026-08-11 21:57:07 +00:00
enricobuehler d67ab9ede4 chore(safety): two .133 gate findings — cfg the abi lock helper, re-anchor a layer proof
ci / bun-nix (pull_request) Successful in 40s
ci / web (pull_request) Successful in 1m24s
ci / docs-site (pull_request) Successful in 1m30s
android / android (pull_request) Canceled after 1m45s
apple / swift (pull_request) Canceled after 1m41s
apple / screenshots (pull_request) Canceled after 0s
ci / rust (pull_request) Canceled after 1m43s
ci / rust-arm64 (pull_request) Canceled after 1m43s
nix / flake (pull_request) Canceled after 1m29s
windows-drivers / probe-and-proto (pull_request) Canceled after 0s
windows-drivers / driver-build (pull_request) Canceled after 1m25s
windows / build (aarch64-pc-windows-msvc) (pull_request) Canceled after 0s
windows / build (x86_64-pc-windows-msvc) (pull_request) Canceled after 0s
The tray leg builds punktfunk-core with default-features off, where
lock_recover's only callers (the quic-gated punktfunk_connection_* entry
points) do not exist — dead code under -D warnings. The helper takes the
same feature gate.

In pf-vkhdr-layer, rustfmt had reflowed destroy_surface's lookup into a
multiline closure, leaving the SAFETY comment outside the closure that
contains its unsafe block — the box's clippy rightly stopped accepting the
adjacency. The comment moves inside, directly above the block.
2026-08-11 23:51:01 +02:00
enricobuehler abec2a1457 chore(safety): nvenc_core — plain union-arm writes are safe by language rule
The .25 gate corrected the carve-out: rustc flags `unsafe { u.arm.field = x }`
as unused_unsafe — plain assignment through a union projection is safe
(writing an arm cannot itself be UB; the hazard is the mismatched READ).
The 11 plain writes go back to bare statements under their codec matches.
What stays in per-op unsafe blocks with arm-guard proofs is the real unsafe
surface: union reads, borrows, and the bindgen bitfield-setter calls — which
is exactly the surface the shipped 4:4:4 bug lived on (set_chromaFormatIDC
stamped under a wrong codec).
2026-08-11 23:45:36 +02:00
enricobuehler 5f097d530d chore(safety): exempt the two bindings-only sys crates from the hoisted deny
Linux fallout from the hoist the mac could not see: bindgen emits unsafe
blocks (layout tests/accessors) into OUT_DIR, where nobody hand-writes
SAFETY proofs — pyrowave-sys failed clippy on .25 with 17 of them, and
libvpl-sys would do the same on the Windows leg. Both crates are
bindings-only by charter (the safe wrapper lives with the consumer), so the
allow is crate-wide with the rationale at the crate root; the hand-written
link-sanity tests keep their proofs by convention.
2026-08-11 23:41:39 +02:00
enricobuehler 2bfd1cd2d5 chore(safety): three unsafe-hygiene grep gates, blocking in ci.yml (WP2c gates)
scripts/ci/check-unsafe-hygiene.sh — textual gates for three classes no lint
covers:

A. unsafe fn markers carrying no contract. unsafe_op_in_unsafe_fn forces real
   ops into blocks, so an unsafe fn with no `unsafe` in its body is a marker
   with no contract (db659809 found two by hand). Contract-deferring fns
   (Vec::set_len shape) waive with `// unsafe-fn-no-op-ok: <reason>`; fenced
   files and `unsafe extern "ABI" fn` (signature-mandated markers) are
   skipped structurally.

B. unwrap/expect/panic! inside extern "C"/"system" bodies — an abort since
   Rust 1.81, not linted, not fuzzable (8b98d0b3). catch_unwind bodies are
   exempt; `// panic-in-extern-ok: <reason>` waives a deliberate abort.

C. Safe-but-process-global APIs (env::set_var/remove_var, sigaction,
   setlocale, set_current_dir) — the 972af299 environ race lived in a file
   with zero occurrences of the word `unsafe`. Per-file count ratchet with
   the baseline in the script; any increase or new file fails.

Making gate B clean on main surfaced 14 real instances of exactly its class —
`.lock().unwrap()` in unguarded extern fns, where a poisoned mutex aborts the
embedding process: six punktfunk-core abi.rs entry points (poll_frame,
next_au, next_audio, next_audio_pcm, next_cursor_shape, next_clipboard),
seven Android JNI entry points, and the Windows client's deeplink wnd_proc.
All fixed with poison-recovering locks (the slots are last-value caches,
valid whatever a poisoned writer left) and Option::insert for the
set-then-unwrap shape; punktfunk-core's 203 lib tests pass. Gate A's
findings were six genuine contract-deferring fns — waived with reasons, not
fixed, because the markers are correct.

Gate-of-the-gate: all three shown to FAIL on deliberately planted instances
(marker fn, panicking extern callback, env::set_var in an unlisted file) and
to run clean on the tree, before the ci.yml step made them blocking.
2026-08-11 23:36:17 +02:00
enricobuehler dfebb9dfbb chore(safety): hoist the unsafe lints into the workspace tables (WP2c hoist)
undocumented_unsafe_blocks joins unsafe_op_in_unsafe_fn in
[workspace.lints], and the ~100 scattered per-file #![deny(...)] attributes
(85 files) are deleted — a new crate, or a new module in an old one, is now
covered on creation rather than on remembering. The per-file form is how
pf-vkhdr-layer, wdk-probe and half of pf-clipboard stayed uncovered.

There are THREE workspaces, so the claim is made three times: the main
Cargo.toml, packaging/windows/drivers (workspace table + [lints]
workspace = true in all seven members), and packaging/windows/pf-vkhdr-layer
(its [lints] table, previous commit). pf-update now opts into workspace
lints; the two vendored member snapshots (cros-codecs, usbip-sim) stay out
deliberately and now both say so.

Newly-covered fallout was two link-sanity tests (pyrowave-sys, libvpl-sys)
— proofs written. Stale prose that claimed the workspace held
unsafe_op_in_unsafe_fn at "warn" (it has been deny) or pointed at the
deleted attributes is corrected.

nvenc_core.rs is carved OUT of the unsafe_op_in_unsafe_fn fence: its
exemption rationale ("raw entry-table calls almost line for line") was
false — the file makes zero FFI calls. Its unsafe surface is C-union writes
whose soundness hangs on which codec arm is active, and its own 4:4:4 note
records the shipped bug (hevcConfig bytes stamped onto an AV1 config) that
per-operation blocks make visible. It now runs the strictest discipline in
the crate: clippy::multiple_unsafe_ops_per_block at deny, one union access
per block, each naming its codec guard.

Verified here: cargo fmt clean in all three workspaces; native clippy
-D warnings clean for everything that compiles on macOS (the three
pre-existing mac-native failures — pf-client-core wol.rs, pf-encode
dead-code/closure-call, probe mic_burst — reproduce on the clean tree).
Linux/Windows legs ride the .25/.133 gate.
2026-08-11 23:26:28 +02:00
enricobuehler f675b3710e chore(api): regenerate openapi.json — fcf4c9fd rewrote the unpairClient description without regenerating
ci / web (pull_request) Successful in 1m48s
ci / bun-nix (pull_request) Successful in 20s
ci / docs-site (pull_request) Successful in 2m22s
ci / rust-arm64 (pull_request) Successful in 4m3s
ci / rust (pull_request) Successful in 6m58s
2026-08-11 23:23:08 +02:00
enricobuehler 23fa03b051 chore(safety): close the three crate-level lint gaps (WP2b)
pf-vkhdr-layer — the sharpest gap: an implicit layer injected into every
Vulkan game process, 32 unsafe usages, zero SAFETY comments, own workspace so
no lint table reached it, and an explicit missing_safety_doc allow. Now: a
[lints] table (unsafe_op_in_unsafe_fn + clippy::undocumented_unsafe_blocks,
both deny), the allow removed, every unsafe operation in an explicit block
with a real proof (loader layer protocol / Vulkan valid-usage), # Safety docs
on the contract-carrying fns, const layout asserts for the SurfaceFormat2Raw
mirror, the five helpers with no caller-facing contract demoted to safe fns,
and the two redundant `unsafe impl Send` deleted (fn pointers and vk handles
are Send intrinsically — the type-check proves it).

wdk-probe — 21 unsafe blocks, 12 proofs: the 9 missing SAFETY comments are
written (the iddcx_rt.rs DDI slot-dispatch ones are about table population
and PFN/index pairing, not pattern fill), the sibling denies added at the
crate root, missing_safety_doc allow dropped, # Safety on DriverEntry, and
the crate joins windows-drivers.yml's clippy list — it was the only driver
crate not in it.

pf-clipboard — the undocumented_unsafe_blocks deny moves from host/windows.rs
to the crate root so host/wayland.rs (4 blocks), host/mutter.rs (2) and any
future backend under host/ are covered on creation. All existing blocks
already carry proofs; free today, structural tomorrow.

Verified here: pf-vkhdr-layer cargo fmt --check + clippy --release
-D warnings at x86_64-pc-windows-msvc. wdk-probe and pf-clipboard compile
checks need the WDK/Linux boxes and ride the .133/.25 gate.
2026-08-11 23:10:00 +02:00
enricobuehler 6e4638dab5 ci(audit): interpret the FFI-free leaf crates under Miri, one at MSVC layout
ci / web (pull_request) Successful in 59s
ci / rust-arm64 (pull_request) Successful in 2m3s
ci / bun-nix (pull_request) Successful in 1m17s
ci / docs-site (pull_request) Successful in 2m48s
ci / rust (pull_request) Failing after 8m8s
Adds a non-blocking `miri` job to audit.yml, per rust-safety-programme.md §7.

What it buys is one narrow, real thing: pf-driver-proto interpreted CROSS-COMPILED to
x86_64-pc-windows-msvc, on a Linux runner, with no Windows box in the loop. That crate is
`#![forbid(unsafe_code)]` and path-dep'd by BOTH the main workspace and the driver
workspace, so it is the layout oracle for every frame and IOCTL crossing that boundary,
and nothing else in CI checks it at MSVC layout. It is NOT unsafe coverage — Miri can
execute on the order of 2% of the host's unsafe and cannot run ash, windows-rs, ffmpeg,
CUDA or the WDK — so no "Miri coverage" number is reported anywhere.

Three steps, every one of them measured on 192.168.1.25 with a cold target dir and cold
sysroot cache, on the dated toolchain the job installs, BEFORE being committed:

  step A  pf-driver-proto + pf-host-config + pf-gpu   21 + 12 + 4 pass   43 s
  step B  pf-driver-proto @ x86_64-pc-windows-msvc           21 pass     26 s
  step C  punktfunk-core fec::gf8 with +avx2,+ssse3            2 pass     63 s

Four corrections to the §7.3 job spec, found while doing this and folded into comments:

* `-p punktfunk-core fec packet crypto` does not parse — cargo rejects the extra
  positionals. Corrected (filters after `--`) it selects 63 tests and was killed at a
  25-minute cap with not one test complete, so the bulk step is dropped entirely and only
  the narrow `fec::gf8` selection is kept, timed at 63 s.
* `nightly-2026-08-10` resolves to rustc 1.99.0-nightly (969b803cb 2026-08-09), NOT the
  12c36e253 2026-08-10 the doc cites: `nightly-<date>` names the day rustup PUBLISHED the
  build, which is compiled from the previous day's commit. The doc's hash came from the
  ROLLING `nightly` channel and was mislabelled. All three steps were re-run and are green
  on the dated pin actually installed here.
* fec-rs dispatches its GF(2^8) multiply through RUNTIME `is_x86_feature_detected!`, so
  step C's RUSTFLAGS are load-bearing in both directions. Verified by probe: bare,
  avx2=false and the step would silently interpret the scalar fallback; with the flags,
  avx2=true and `_mm256_shuffle_epi8` genuinely executes under the interpreter. GFNI stays
  false either way, so that branch is simply not covered.
* `RUSTC_WRAPPER: ""` is a guard, not a fix, and the comment says so — audit.yml sets no
  sccache today, and cargo-miri warns "Ignoring `RUSTC_WRAPPER`, Miri does not support
  wrapping" and carries on regardless.

Non-blocking via a step-level `||`, not job-level continue-on-error, following the
precedent audit.yml already documents for docs-site-audit. Each step additionally asserts
a non-zero pass count, so a crate rename or a filter that stops matching surfaces as a
warning rather than as a green zero-test run. Both paths were exercised directly: a
failing run emits the annotation and still exits 0, and a zero-selection run trips the
guard, while a green run with empty bin/doctest targets does not false-positive.

Leak checking stays ON (no -Zmiri-ignore-leaks); the two deliberate leaks in the tree are
named in a comment so whoever expands coverage annotates those sites instead of blanket-
disabling the check. pf-bitstream and the FFI crates are excluded with the reasons inline
so they are not helpfully re-added. `paths:` is deliberately not widened to
crates/pf-driver-proto/** — that filter is workflow-level and would fire all six audit
jobs on every driver-proto edit.
2026-08-11 22:52:28 +02:00
enricobuehler 0c2ac333ae Merge pull request 'Chore/rust safety programme' (#164) from chore/rust-safety-programme into main
apple / swift (push) Successful in 1m43s
ci / web (push) Successful in 1m33s
windows-drivers / probe-and-proto (push) Successful in 27s
ci / docs-site (push) Successful in 1m44s
ci / bun-nix (push) Successful in 21s
windows-drivers / driver-build (push) Successful in 1m45s
ci / rust-arm64 (push) Successful in 7m52s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 21s
deb / build-publish-client-arm64 (push) Successful in 2m35s
arch / build-publish (push) Successful in 11m28s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 18s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 19s
release / apple (push) Successful in 9m41s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 14s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 14s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 15s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Successful in 52s
ci / rust (push) Failing after 11m41s
android / android (push) Successful in 14m57s
deb / build-publish-host (push) Successful in 7m17s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 3m13s
apple / screenshots (push) Successful in 6m20s
docker / builders-arm64cross (push) Successful in 18s
docker / deploy-docs (push) Successful in 44s
nix / flake (push) Successful in 14m52s
windows-host / package (push) Successful in 20m38s
windows-host / winget-source (push) Skipped
deb / build-publish (push) Successful in 13m0s
windows-host / canary-manifest (push) Successful in 49s
windows-msix / package (arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m28s
windows-msix / package (x64, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 3m13s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 1m24s
flatpak / build-publish (push) Successful in 24m54s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 2m33s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 39m36s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 38m33s
2026-08-11 20:47:15 +00:00
116 changed files with 1031 additions and 450 deletions
+187
View File
@@ -15,6 +15,12 @@
# fails if any crate carries a license outside the allowlist — the regression
# guard about.toml always promised. (The Android Gradle tree has no lockfile, so
# nothing scans it — see the CRA roadmap.)
# * miri → NON-BLOCKING interpretation of the few FFI-free leaf crates, one of them
# cross-compiled to MSVC layout. Not a supply-chain scan; it lives here because
# audit.yml already has exactly the shape it needs (weekly cron,
# workflow_dispatch, the rust-ci container, the same cache pattern) and because
# ci.yml runs on every push against a fleet where 37 of 46 jobs contend for
# ubuntu-24.04. See the `miri:` job below for what it does and does not buy.
# Triggers: weekly (catch newly-disclosed CVEs in pinned deps), on every lockfile/allowlist
# change, and on demand.
# To silence a known-unfixable Rust advisory, add it to `.cargo/audit.toml` ([advisories] ignore=[…]).
@@ -44,6 +50,13 @@ on:
- 'about.toml'
- '.gitea/workflows/audit.yml'
workflow_dispatch:
# NOTE on the `paths:` list above and the `miri:` job: `crates/pf-driver-proto/**` is deliberately
# NOT listed, even though that crate is what the Miri job exists to watch. `paths:` is a
# WORKFLOW-level filter — adding it would fire all six jobs (three bun trees, pnpm, cargo-audit,
# the license gate) on every driver-proto edit, onto a fleet where 37 of 46 jobs contend for
# ubuntu-24.04, to run one 2-minute job. Weekly cron + workflow_dispatch is the day-one cadence;
# revisit once the job has a green history, and if you do, prefer moving miri to its own workflow
# file over widening this filter.
jobs:
cargo-audit:
@@ -177,3 +190,177 @@ jobs:
command -v cargo-about >/dev/null 2>&1 || cargo install --locked cargo-about --version 0.9.1 --features cli
cargo about generate about.hbs --fail -o /dev/null
cargo about generate -m packaging/windows/drivers/Cargo.toml -c about.toml about.hbs --fail -o /dev/null
# ── Miri ─────────────────────────────────────────────────────────────────────────────────────
# WHAT THIS BUYS, precisely — one thing, and it is worth having:
# It interprets `pf-driver-proto` CROSS-COMPILED TO `x86_64-pc-windows-msvc`, on a Linux
# runner, with no Windows box anywhere in the loop. That crate is `#![forbid(unsafe_code)]`
# and is path-dep'd by BOTH the main workspace and the driver workspace, so it is the layout
# oracle for every frame and IOCTL crossing that boundary — and drift there is silent
# corruption, not a compile error. Nothing else in CI checks it at MSVC layout.
# On the first run ever performed against this repo it found a real defect: a layout test
# reading an align-8 struct out of an align-1 stack buffer, which had passed on every machine
# and every CI leg since it was written because a stack `[u8; 40]` usually lands 8-aligned.
#
# WHAT IT DOES NOT BUY — do not let anyone report this as unsafe coverage, and do not publish a
# "Miri coverage" percentage; it would be noise. Miri can execute on the order of 2% of the
# host's unsafe. It cannot run ash, windows-rs, ffmpeg, CUDA or the WDK, and in those crates
# the unsafe *is* the foreign call, so there is nothing for an interpreter to execute. This
# job is a targeted instrument for three leaf surfaces, not a safety net.
#
# NON-BLOCKING, deliberately, and via a step-level `||` — NOT job-level `continue-on-error`,
# which act_runner does not reliably honor (same reasoning as docs-site-audit above; a red job
# here would take the whole run red). Flip to blocking only after several weeks of green
# establish the nightly-drift rate.
#
# Do NOT add crates here because they merely compile under Miri. Add them because they contain
# pure-Rust unsafe or a layout contract worth interpreting. Explicitly excluded:
# * pf-bitstream — its compile did not finish in 27 min at 2.1 GB RSS, and it is
# `forbid(unsafe_code)`, so there is nothing to find. Do not re-add it.
# * pf-update-check — ring; every FFI crate — dies on the first foreign call. Structural.
# * punktfunk-core in bulk — `-- fec packet crypto` selects 63 tests and was killed at a
# 25-minute cap with not one test reported complete. Only the narrow
# `fec::gf8` selection below is affordable, and it was timed before it
# was committed. Do not widen this filter without timing the result.
#
# MEASURED, not estimated — 192.168.1.25 (Ubuntu, 8 cores), on the DATED toolchain this job
# actually installs, with a COLD target dir and a COLD sysroot cache (so each step's figure
# includes building the Miri sysroot it needs) and a warm cargo registry. Every step below has
# been run start to finish; nothing here is extrapolated:
# step A 21 + 12 + 4 pass 43 s
# step B 21 pass 26 s
# step C 2 pass 63 s
# TOTAL 132 s cold. Interpretation itself is ~10 s of that; the rest is compiling, plus ~38 s
# of one-time sysroot builds (21 s host + 17 s MSVC) that the cache below then carries.
# Warm, the three steps are ~6 s / ~3 s / ~10 s. `timeout-minutes: 30` is therefore vast
# headroom, kept deliberately so a first fully-uncached run — which additionally downloads a
# ~400 MB toolchain and the registry — cannot trip it.
# If you add a step, MEASURE IT FIRST. The estimate this job replaced said "under 15 s across
# all four steps" and was extrapolated from a partial run; the real punktfunk-core figure was
# >25 min. Extrapolation is exactly how that happened.
miri:
runs-on: ubuntu-24.04
container:
image: 192.168.1.58:5010/punktfunk-rust-ci:latest
timeout-minutes: 30
env:
# A DATED nightly, bumped deliberately — exactly like rust-toolchain.toml, and for the same
# reason. The cache keys below carry this value, so bumping it self-invalidates them.
# ⚠ `nightly-<date>` names the day rustup PUBLISHED the build, and that build is compiled
# from the PREVIOUS day's commit. This pin therefore resolves to
# `rustc 1.99.0-nightly (969b803cb 2026-08-09)` [verified by installing it], NOT the
# `12c36e253 2026-08-10` that the rust-safety programme doc's §7 table cites — that figure
# came from the ROLLING `nightly` channel and was mislabelled as the dated one. Harmless,
# but do not "fix" the date to chase that hash: all three steps below were re-run and are
# green on the dated toolchain this job actually installs.
MIRI_TOOLCHAIN: nightly-2026-08-10
# A GUARD, not a fix for a present problem: audit.yml sets no sccache — only ci.yml does, at
# workflow level (ci.yml:27). `cargo-miri` REPLACES rustc and cannot be wrapped; it prints
# "Ignoring `RUSTC_WRAPPER` environment variable, Miri does not support wrapping" and
# carries on [verified]. This keeps a future workflow-level sccache from becoming a puzzle.
RUSTC_WRAPPER: ""
# -Zmiri-disable-isolation: pf-gpu's tests mkdir, and Miri aborts them without it [verified].
# -Zmiri-symbolic-alignment-check: the whole point — it refuses to let an accidentally
# favourable stack slot stand in for an alignment guarantee. This is the flag that caught
# the pf-driver-proto defect.
# NOTE the absence of -Zmiri-ignore-leaks. Miri leak-checks by DEFAULT, and that is the one
# leak-detection capability it offers here. None of the crates below leaks, so the job is
# green. The tree does contain DELIBERATE leaks (pf-umdf-util/src/section.rs `ViewCell`,
# gamepad_raii.rs leak-on-timeout) — when coverage ever reaches them, annotate those two
# sites; do not blanket-disable the check.
MIRIFLAGS: -Zmiri-disable-isolation -Zmiri-symbolic-alignment-check
steps:
- uses: actions/checkout@v4
# Two caches, split on purpose so a Cargo.lock change does not re-download a ~400 MB
# toolchain. Both use their OWN `miri-` key prefix — never a shared one.
# The Miri sysroot is per-toolchain and per-target (two are built here: host + MSVC), so it
# belongs with the toolchain, not with the lockfile.
- name: cache the nightly toolchain + Miri sysroots
uses: actions/cache@v4
with:
path: |
/usr/local/rustup/toolchains/${{ env.MIRI_TOOLCHAIN }}-x86_64-unknown-linux-gnu
~/.cache/miri
key: miri-toolchain-v1-${{ env.MIRI_TOOLCHAIN }}
- name: cache the cargo registry
uses: actions/cache@v4
with:
path: /usr/local/cargo/registry
key: miri-registry-v1-${{ hashFiles('Cargo.lock') }}
restore-keys: miri-registry-v1-
# The image needs no change for this: ci/rust-ci.Dockerfile:51-54 installs via rustup and
# `chmod -R a+w`s both RUSTUP_HOME and CARGO_HOME, so a job can add a toolchain at runtime.
# `rust-src` is required — cargo-miri builds its sysroot from source, per target.
#
# This does NOT disturb the 1.96.0 pin: `cargo +<toolchain>` overrides rust-toolchain.toml
# for that single invocation only, so `cargo fmt` / `clippy` keep resolving 1.96.0 and the
# fmt-parity contract in CLAUDE.md is untouched. The two echo lines below keep that claim
# honest in the log. They are deliberately NOT `rustup show active-toolchain`: that command
# RESOLVES the toolchain file and would install the whole 1.96.0 toolchain just to print a
# line, in a job where every cargo call is `+$MIRI_TOOLCHAIN` and 1.96.0 is never needed.
# Deliberately NOT `rustup override set` — that writes persistent per-directory state into
# the runner's rustup config, which leaks into unrelated later jobs on a self-hosted fleet.
# Deliberately NOT a second rust-toolchain.toml in a subdirectory — that would apply to
# every cargo invocation under that subtree including fmt, which is the drift the root pin
# exists to prevent.
- name: install the pinned nightly + miri
run: |
git config --global --add safe.directory "$PWD"
rustup toolchain install "$MIRI_TOOLCHAIN" \
--profile minimal \
--component miri,rust-src \
--target x86_64-pc-windows-msvc
echo "root pin, untouched by this job: $(grep -E '^channel' rust-toolchain.toml)"
cargo +"$MIRI_TOOLCHAIN" --version
# A run that reports `0 passed` is a selection that matched nothing, not a success — that
# exact mistake has already cost one round-trip here. So each step below checks a zero exit
# AND that at least one target reported a non-zero pass count, which is what catches a
# crate rename or a `--` filter that stops matching. (Each step legitimately prints several
# `0 passed` lines too — the empty bin/doctest targets — so the check is "at least one
# non-zero", not "no zeroes".) Expected counts at the time of writing: 21 + 12 + 4.
- name: miri — FFI-free leaf crates (native)
run: |
set -o pipefail
ok=1
cargo +"$MIRI_TOOLCHAIN" miri test \
-p pf-driver-proto -p pf-host-config -p pf-gpu 2>&1 | tee /tmp/miri-native.log || ok=0
grep -qE 'test result: ok\. [1-9][0-9]* passed' /tmp/miri-native.log || ok=0
[ "$ok" = 1 ] || echo "::warning::miri (FFI-free leaf crates, native) did not pass — non-blocking; see punktfunk-planning design/rust-safety-programme.md §7"
# THE step that justifies the job: pf-driver-proto at MSVC layout, on Linux, no Windows box.
# Expected: 21 passed. If this one ever goes red, treat it as a layout-contract break
# between the host and driver workspaces until proven otherwise.
- name: miri — pf-driver-proto at x86_64-pc-windows-msvc layout
run: |
set -o pipefail
ok=1
cargo +"$MIRI_TOOLCHAIN" miri test \
-p pf-driver-proto --target x86_64-pc-windows-msvc 2>&1 | tee /tmp/miri-msvc.log || ok=0
grep -qE 'test result: ok\. [1-9][0-9]* passed' /tmp/miri-msvc.log || ok=0
[ "$ok" = 1 ] || echo "::warning::miri (pf-driver-proto @ MSVC layout) did not pass — non-blocking, but this is the layout oracle for every frame and IOCTL; see design/rust-safety-programme.md §7"
# fec-rs dispatches its GF(2^8) multiply through RUNTIME `is_x86_feature_detected!`. Under
# Miri that detection reports the COMPILE-TIME target features, so WITHOUT these RUSTFLAGS
# the step silently interprets the scalar fallback and is worthless. Verified both ways on
# 192.168.1.25: bare, `avx2=false ssse3=false`; with the flags, `avx2=true ssse3=true` and
# `_mm256_shuffle_epi8` genuinely executes under the interpreter. GFNI stays false either
# way — Miri does not implement it — so the gfni branch is simply not covered here.
#
# ⚠ x86_64 ONLY, and it must stay that way. A RUSTFLAGS env var OVERRIDES config rustflags
# ENTIRELY (.cargo/config.toml:11-13 says so), and that config carries `--cfg aes_armv8` /
# `--cfg polyval_armv8` for aarch64 — worth a measured ~3x decrypt-throughput cliff if
# dropped. Harmless here because this job pins ubuntu-24.04/x86_64; fatal on mac-mini-1.
# Narrow selection is mandatory, not an optimisation: see the punktfunk-core note above.
- name: miri — punktfunk-core fec::gf8, taking the real AVX2/SSSE3 branches
env:
RUSTFLAGS: -C target-feature=+avx2,+ssse3
run: |
set -o pipefail
ok=1
cargo +"$MIRI_TOOLCHAIN" miri test \
-p punktfunk-core --lib -- fec::gf8 2>&1 | tee /tmp/miri-gf8.log || ok=0
grep -qE 'test result: ok\. [1-9][0-9]* passed' /tmp/miri-gf8.log || ok=0
[ "$ok" = 1 ] || echo "::warning::miri (punktfunk-core fec::gf8, AVX2/SSSE3) did not pass — non-blocking; see design/rust-safety-programme.md §7"
+9 -2
View File
@@ -111,6 +111,13 @@ jobs:
- name: Format
run: cargo fmt --all --check
# rust-safety WP2c: three textual gates for classes no lint covers — unsafe fn markers
# carrying no contract, panic across an extern boundary (an abort since 1.81), and
# process-global safe APIs (env::set_var & co, count-ratcheted). Pure grep/awk, no cargo.
# Both failure modes were demonstrated before this became blocking (planted instances).
- name: Unsafe-hygiene grep gates
run: sh scripts/ci/check-unsafe-hygiene.sh
- name: Clippy (deny warnings)
run: cargo clippy --workspace --all-targets --locked -- -D warnings
@@ -139,8 +146,8 @@ jobs:
# `nvenc` gates enc/linux/nvenc_cuda.rs (+ nvenc_core/nvenc_status) and `vulkan-encode` gates
# enc/linux/vulkan_video.rs (+ the vendored vk_av1_encode/vk_valve_rgb bindings) — ~8,150
# lines carrying ~70 `unsafe` blocks. Their ONLY prior CI coverage was deb.yml's
# `cargo build`, where warnings are not errors, so pf-encode's own
# `#![deny(clippy::undocumented_unsafe_blocks)]` — the crate's stated unsafe-proof gate —
# `cargo build`, where warnings are not errors, so the `undocumented_unsafe_blocks` deny
# (now hoisted into [workspace.lints]) — pf-encode's stated unsafe-proof gate —
# was never actually enforced on them. (`pyrowave` needs no extra step: punktfunk-host has
# `default = ["pyrowave"]`, so the steps above already cover it.)
#
+4 -3
View File
@@ -159,9 +159,10 @@ jobs:
# The gamepad drivers' business logic is 100% safe (it moved onto pf-umdf-util, the audited
# unsafe layer); pf-vdisplay + wdk-iddcx are inherently FFI-bound but every `unsafe {}` carries a
# `// SAFETY:` proof. Both invariants are lint-gated (`unsafe_op_in_unsafe_fn` +
# `undocumented_unsafe_blocks`); this step keeps them from regressing. (wdk-probe is a
# toolchain-only probe crate and is excluded.)
run: cargo clippy -p pf-umdf-util -p pf-xusb -p pf-gamepad -p pf-mouse -p wdk-iddcx -p pf-vdisplay --all-targets -- -D warnings
# `undocumented_unsafe_blocks`); this step keeps them from regressing. wdk-probe is a
# toolchain-only probe crate, but it holds real DDI slot-dispatch unsafe (iddcx_rt.rs), so it
# runs the same gates.
run: cargo clippy -p pf-umdf-util -p pf-xusb -p pf-gamepad -p pf-mouse -p wdk-iddcx -p pf-vdisplay -p wdk-probe --all-targets -- -D warnings
- name: cargo fmt --check the safe-layer + gamepad/mouse drivers
run: cargo fmt -p pf-umdf-util -p pf-xusb -p pf-gamepad -p pf-mouse --check
- name: Inspect /INTEGRITYCHECK (before) — expect FORCE_INTEGRITY set by wdk-build
+11
View File
@@ -101,6 +101,17 @@ repository = "https://git.unom.io/unom/punktfunk"
[workspace.lints.rust]
unsafe_op_in_unsafe_fn = "deny"
# The companion lint: every `unsafe {}` / `unsafe impl` carries a `// SAFETY:` proof. Hoisted here
# from ~85 per-file `#![deny(...)]` attributes so a NEW crate (or a new module in an old one) is
# covered on creation rather than on remembering — the per-file form left pf-vkhdr-layer,
# wdk-probe, and half of pf-clipboard uncovered for months. NOTE: this table reaches only crates
# with `[lints] workspace = true`; `packaging/windows/drivers` and `packaging/windows/pf-vkhdr-layer`
# are SEPARATE workspaces and restate it (any "workspace-wide" claim must be made three times or it
# is false). Of the members, only the two vendored snapshots (pf-bitstream/vendor/cros-codecs,
# punktfunk-host/vendor/usbip-sim) stay out, deliberately — upstream code stays pristine.
[workspace.lints.clippy]
undocumented_unsafe_blocks = "deny"
[profile.release]
opt-level = 3
lto = "thin"
+1 -1
View File
@@ -53,7 +53,7 @@
"clients"
],
"summary": "Unpair a client",
"description": "Removes the client's certificate from the pairing store (persisted — the removal survives a\nhost restart). Removing the last pairing also closes the GameStream ENet control port\n(UDP 47999), which is only bound while at least one pairing exists. Caveat: the nvhttp TLS\nlayer does not yet reject unlisted certificates (`gamestream/tls.rs` accepts any well-formed\nclient cert — a planned hardening step), so until that lands this removes the client\nfrom the listing without severing its ability to reconnect.",
"description": "Removes the client's certificate from the pairing store (persisted — the removal survives a\nhost restart). Revocation is complete: a LIVE GameStream session owned by this certificate is\nended (the client gets the standard TERMINATION+disconnect), and removing the last pairing\nalso closes the ENet control port (UDP 47999), which is only bound while at least one pairing\nexists. The nvhttp TLS layer still completes a handshake with any well-formed client cert BY\nDESIGN (authorization is per-request via the paired-fingerprint check) — an unpaired client\nthat reconnects is rejected at every post-pair endpoint.",
"operationId": "unpairClient",
"parameters": [
{
@@ -9,7 +9,7 @@ use punktfunk_core::config::{CompositorPref, GamepadPref, Mode};
use std::sync::{Arc, Mutex};
use std::time::Duration;
use super::{hex32, jni_guard, parse_hex32, SessionHandle};
use super::{hex32, jni_guard, lock_recover, parse_hex32, SessionHandle};
/// Machine token of the most recent `nativeConnect`/`nativePair` failure, taken (and cleared)
/// by `nativeTakeLastError` so Kotlin can render a cause-specific message instead of the old
@@ -41,7 +41,7 @@ pub extern "system" fn Java_io_unom_punktfunk_kit_NativeBridge_nativeTakeLastErr
env: JNIEnv<'local>,
_this: JObject<'local>,
) -> jni::sys::jstring {
let token = std::mem::take(&mut *LAST_ERROR.lock().unwrap());
let token = std::mem::take(&mut *lock_recover(&LAST_ERROR));
match env.new_string(token) {
Ok(s) => s.into_raw(),
Err(_) => JObject::null().into_raw(),
@@ -45,6 +45,15 @@ pub(crate) fn jni_guard<T>(default: T, f: impl FnOnce() -> T) -> T {
})
}
/// Poison-recovering lock for the JNI entry points that are NOT behind [`jni_guard`]: a
/// `.lock().unwrap()` there turns a poisoned mutex into a panic across the `extern "system"`
/// boundary — an abort of the whole app on Rust ≥ 1.81 (the panic-in-extern grep gate's class).
/// The slots behind these mutexes are plane-thread handles and last-value caches; whatever a
/// poisoned writer left is still valid to inspect or replace.
pub(crate) fn lock_recover<T>(m: &Mutex<T>) -> std::sync::MutexGuard<'_, T> {
m.lock().unwrap_or_else(std::sync::PoisonError::into_inner)
}
/// A live session behind the `jlong` handle: the connector + the decode thread it feeds.
pub(crate) struct SessionHandle {
// Read only by the android decode path (`nativeStartVideo` → `crate::decode`); on the host
+7 -7
View File
@@ -8,7 +8,7 @@ use jni::objects::JString;
use jni::sys::{jboolean, jdoubleArray, jintArray, jlong, jsize, jstring};
use jni::JNIEnv;
use super::{jni_guard, SessionHandle};
use super::{jni_guard, lock_recover, SessionHandle};
/// `NativeBridge.nativeStartVideo(handle, surface, decoderName, lowLatencyMode, lowLatencyFeature,
/// isTv, presentPriority, smoothBuffer)` — wrap the SurfaceView's `Surface` as an `ANativeWindow`
@@ -48,7 +48,7 @@ pub extern "system" fn Java_io_unom_punktfunk_kit_NativeBridge_nativeStartVideo(
.filter(|s| !s.is_empty());
// SAFETY: live handle per the nativeConnect/nativeClose contract.
let h = unsafe { &*(handle as *const SessionHandle) };
let mut guard = h.video.lock().unwrap();
let mut guard = lock_recover(&h.video);
if guard.is_some() {
return; // already streaming
}
@@ -222,7 +222,7 @@ pub extern "system" fn Java_io_unom_punktfunk_kit_NativeBridge_nativeVideoStats(
}
// SAFETY: live handle per the nativeConnect/nativeClose contract.
let h = unsafe { &*(handle as *const SessionHandle) };
if h.video.lock().unwrap().is_none() {
if lock_recover(&h.video).is_none() {
return std::ptr::null_mut(); // not streaming → no stats
}
let snap = h
@@ -385,7 +385,7 @@ pub extern "system" fn Java_io_unom_punktfunk_kit_NativeBridge_nativeStartAudio(
}
// SAFETY: live handle per the nativeConnect/nativeClose contract.
let h = unsafe { &*(handle as *const SessionHandle) };
let mut guard = h.audio.lock().unwrap();
let mut guard = lock_recover(&h.audio);
if guard.is_some() {
return; // already playing
}
@@ -434,7 +434,7 @@ pub extern "system" fn Java_io_unom_punktfunk_kit_NativeBridge_nativeStartMic(
}
// SAFETY: live handle per the nativeConnect/nativeClose contract.
let h = unsafe { &*(handle as *const SessionHandle) };
let mut guard = h.mic.lock().unwrap();
let mut guard = lock_recover(&h.mic);
if let Some(m) = guard.as_ref() {
return m.session_id(); // already capturing — same stream, same session
}
@@ -516,7 +516,7 @@ pub extern "system" fn Java_io_unom_punktfunk_kit_NativeBridge_nativeStartPadAud
speaker != 0,
) {
Some(p) => {
*h.pad_audio.lock().unwrap() = Some(p);
*lock_recover(&h.pad_audio) = Some(p);
1
}
None => 0,
@@ -629,6 +629,6 @@ pub extern "system" fn Java_io_unom_punktfunk_kit_NativeBridge_nativeMicActive(
}
// SAFETY: live handle per the nativeConnect/nativeClose contract.
let h = unsafe { &*(handle as *const SessionHandle) };
jboolean::from(h.mic.lock().unwrap().is_some())
jboolean::from(lock_recover(&h.mic).is_some())
})
}
+7 -1
View File
@@ -176,7 +176,13 @@ unsafe extern "system" fn wnd_proc(
let slice = unsafe { std::slice::from_raw_parts(cds.lpData as *const u16, len) };
let url = String::from_utf16_lossy(slice);
tracing::debug!(%url, "link from another instance");
INBOX.lock().unwrap().push(url);
// Poison-recover, never unwrap: a panic out of a window procedure is an abort since
// Rust 1.81, and the inbox is a plain Vec that stays valid whatever a poisoned
// writer left behind.
INBOX
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.push(url);
return LRESULT(1);
}
}
-1
View File
@@ -15,7 +15,6 @@
//! (measure the path: probe burst → goodput / loss / recommended bitrate)
// Unsafe-proof program: every `unsafe {}` in this client carries a `// SAFETY:` proof.
#![deny(clippy::undocumented_unsafe_blocks)]
// Link as a GUI (windows) subsystem binary so the default windowed launch (MSIX / double-click)
// does NOT pop a console window. The CLI paths (--headless/--discover) reattach to the launching
// terminal's console at startup (see main), so their output is still visible when run from a shell.
+7
View File
@@ -10,6 +10,11 @@
#![allow(non_snake_case)]
// Bindgen output for a C API: u128 layout warnings and the like are upstream's concern.
#![allow(improper_ctypes)]
// The workspace-wide undocumented_unsafe_blocks deny cannot apply to GENERATED code: bindgen
// emits `unsafe {}` in layout tests/accessors and nobody hand-writes proofs into OUT_DIR. This
// crate is bindings-only by charter (the safe wrapper lives with the consumer), so the allow is
// crate-wide; the hand-written link-sanity test below still carries its proof by convention.
#![allow(clippy::undocumented_unsafe_blocks)]
// Generated code — clippy findings in it (missing safety docs on generated unsafe fns, style
// nits across 14k lines) are bindgen's shape, not ours; the safe wrapper in pf-encode is the
// linted surface.
@@ -27,6 +32,8 @@ mod tests {
/// implementations — that's fine, MFXLoad itself must still succeed).
#[test]
fn dispatcher_links_and_loads() {
// SAFETY: MFXLoad allocates the dispatcher's loader context (documented to work with no
// driver present) and MFXUnload frees that same non-null handle; nothing else is touched.
unsafe {
let loader = MFXLoad();
assert!(!loader.is_null(), "MFXLoad returned NULL");
-7
View File
@@ -7,13 +7,6 @@
//! [`FrameChannelSender`] closure, so this crate reaches neither the encoder nor the host
//! orchestrator).
// Every unsafe block in this crate carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
// …and that program only covers a whole `unsafe fn` body once the body needs its own block: in
// edition 2021 `unsafe_op_in_unsafe_fn` is allow-by-default, which exempted the crate's hardest FFI
// (the ring/slot construction, the channel broker, every D3D converter ctor) from the deny above.
#![deny(unsafe_op_in_unsafe_fn)]
use anyhow::Result;
use pf_frame::{CapturedFrame, FramePayload, PixelFormat};
// The Linux capturer reaches `DmabufFrame` through `super::`; `CursorOverlay` it names directly as
-1
View File
@@ -25,7 +25,6 @@
// Every `unsafe` block in this module TREE carries a `// SAFETY:` proof; enforce it (unsafe-proof
// program). This file itself has none — the FFI lives in the child modules declared at the bottom
// (`pipewire`, `pw_cursor`, `pw_pods`, `portal`, `xfixes_cursor`), which this inner attribute covers.
#![deny(clippy::undocumented_unsafe_blocks)]
use super::{CapturedFrame, Capturer, DmabufFrame, FramePayload, PixelFormat, ZeroCopyPolicy};
use anyhow::{anyhow, Context, Result};
-3
View File
@@ -9,9 +9,6 @@
//! `crate::dxgi::*` path keeps resolving. DXGI Desktop Duplication has been removed; this
//! module contains no capturer.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
pub use pf_frame::dxgi::{make_device, pack_luid, D3d11Frame, PyroFrameShare, WinCaptureTarget};
// The P010 colour self-test (sweep Phase 5.5) — the `hdr-p010-selftest` subcommand, its f64
@@ -16,9 +16,6 @@
//! [`pf_driver_proto`] (which OWNS the contract, with `const` size asserts) — both sides `use` it, so
//! drift is a compile error rather than a "must match" comment.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use super::dxgi::{
make_device, BgraToYuvPlanes, D3d11Frame, HdrP010Converter, HdrRgb10Converter, PyroFrameShare,
VideoConverter, WinCaptureTarget,
@@ -2,9 +2,6 @@
//! capturer): duplicates the unnamed shared header / ring / event handles into the driver's WUDFHost
//! and delivers them as bare handle values over the SYSTEM-only control device.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use super::*;
/// The sealed channel's handle-duplication broker (`design/idd-push-security.md`): the frame objects
@@ -5,9 +5,6 @@
//! [`pf_frame::CursorOverlay`] the Linux portal path produces — everything downstream (the
//! cursor forwarder, the wire, the client renderer) is shared.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it.
#![deny(clippy::undocumented_unsafe_blocks)]
use super::*;
use pf_driver_proto::cursor::{
CursorShm, CURSOR_MAGIC, CURSOR_SHAPE_BYTES, CURSOR_SHAPE_MAX, CURSOR_SHAPE_OFFSET,
@@ -10,9 +10,6 @@
//! alpha-blended quad (the GDI poller's full-fidelity shape at its polled position), entirely
//! GPU-side on the capture device, before the normal conversion runs from the scratch.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use super::*;
use windows::core::s;
use windows::Win32::Graphics::Direct3D::D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
@@ -20,9 +20,6 @@
//! `winsta0\default` (the service supervisor retargets the token — `windows/service.rs`
//! `spawn_host`), so the poller thread sees the session's cursor directly; no helper process.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use super::*;
use windows::Win32::Graphics::Gdi::{
DeleteObject, GetDC, GetDIBits, GetObjectW, ReleaseDC, BITMAP, BITMAPINFO, BITMAPINFOHEADER,
@@ -1,9 +1,6 @@
//! Off-thread display-descriptor polling (plan §W4, carved out of the IDD-push capturer): the
//! live HDR state + active resolution of the virtual target, sampled off the capture loop via CCD.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use super::*;
/// The display descriptor the capture loop follows: live HDR state + active resolution of the
@@ -33,9 +33,6 @@
//! The session's `FlushTimer` is 1 s, so a bracket from the trailing second of a gap can land
//! AFTER that stall's report line — the next report (and the metronomic tally) still carries it.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use std::collections::VecDeque;
use std::sync::{Arc, Mutex, OnceLock, Weak};
use std::time::{Duration, Instant};
@@ -25,9 +25,6 @@
//! ([`acquire`]), refcounted across parallel capturers; probes sample at 20 Hz or slower and cost
//! microseconds each, so the engine is invisible next to a streaming session.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use std::collections::VecDeque;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, Weak};
@@ -1,9 +1,6 @@
//! Capture-stall detection (plan §W4, carved out of the IDD-push capturer): flags multi-hundred-ms
//! holes in DWM frame delivery that open while the desktop was actively composing.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use super::*;
/// A detected capture stall: a multi-hundred-ms hole in DWM's frame delivery that opened while the
-1
View File
@@ -18,7 +18,6 @@
// proof of why it is sound. This crate held ~91 unsafe items with NO enforcement while every
// other subsystem crate denied it — the decoders' `unsafe impl Send`s had a one-line aside
// instead of an argument precisely because nothing required one.
#![deny(clippy::undocumented_unsafe_blocks)]
#[cfg(any(target_os = "linux", windows))]
mod au_dump;
+2 -2
View File
@@ -17,8 +17,8 @@
//! (`PostMessage` is the documented thread-safe way to poke a message loop). Per-window state hangs
//! off `GWLP_USERDATA`, so multiple concurrent sessions each get their own window + state.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
// Every `unsafe` block in this file carries a `// SAFETY:` proof; the deny enforcing it sits at
// the crate root (lib.rs), covering every backend.
use std::cell::RefCell;
use std::sync::{Arc, Mutex};
+4
View File
@@ -10,6 +10,10 @@
//! [`spawn_decline_loop`] — so its control loop compiles unchanged on every host platform; the
//! platform split lives entirely behind [`start`].
// Unsafe-proof program: every `unsafe` block in any backend carries a `// SAFETY:` proof,
// enforced workspace-wide by `[workspace.lints]` — a new backend under `host/` is covered on
// creation.
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
-1
View File
@@ -11,7 +11,6 @@
//! capture hint, start banner.
// Unsafe-proof program: every `unsafe {}` in the Skia/Vulkan overlay carries a `// SAFETY:` proof.
#![deny(clippy::undocumented_unsafe_blocks)]
#[cfg(any(target_os = "linux", windows))]
mod anim;
-1
View File
@@ -52,7 +52,6 @@
//! ([`dxva::as_bytes`] / [`dxva::slice_bytes`]), fenced behind a sealed trait
//! that only this crate's `#[repr(C)]` PODs implement, and carrying a written
//! proof — enforced:
#![deny(clippy::undocumented_unsafe_blocks)]
pub mod config;
pub mod descriptors;
+2
View File
@@ -48,6 +48,8 @@ impl AvBuffer {
/// allocator returns on failure (so the `is_null` check every caller used to open-code happens
/// once, here).
///
// unsafe-fn-no-op-ok: contract-deferring constructor (`Vec::set_len` shape) — the body is
// safe; the ownership transfer promised here is what Drop/as_ptr later rely on.
/// # Safety
/// `p` must be null, or a live `AVBufferRef` whose ownership passes to the returned value —
/// nothing else may unref it.
-2
View File
@@ -12,8 +12,6 @@
//! does *not* accept — we expand it to `rgb0` (one padding byte/pixel, no colour math).
//! The encoder is opened *without* a global header so VPS/SPS/PPS are emitted in-band on
//! every IDR — the output is both a playable raw Annex-B stream and self-contained AUs.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use super::{ChromaFormat, Codec, EncodedFrame, Encoder};
use anyhow::{anyhow, bail, Context, Result};
@@ -63,8 +63,6 @@
// the signature. Clearing this file means DELETING the markers that carry no caller contract, not
// wrapping the calls — until then the lint is off HERE and enforced everywhere else.
#![allow(unsafe_op_in_unsafe_fn)]
// Every `unsafe` block / impl in this file carries a `// SAFETY:` proof; enforce it.
#![deny(clippy::undocumented_unsafe_blocks)]
use super::nvenc_core::{
apply_low_latency_config, build_init_params, cached_ceiling, cached_split_verdict, codec_guid,
-2
View File
@@ -19,8 +19,6 @@
//! hwdevice/hwframes/buffersrc/buffersink calls go through `ffmpeg::ffi` (= `ffmpeg_sys_next`),
//! as the CUDA encode path and the clients' decode paths already do. The encoder is opened
//! *without* a global header, so VPS/SPS/PPS are in-band on every IDR.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use super::{Codec, EncodedFrame, Encoder};
use anyhow::{anyhow, bail, Context, Result};
-3
View File
@@ -41,9 +41,6 @@
//! worker caches it, so the steady state passes **zero** descriptors (the PipeWire pool recycles a
//! small buffer set).
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use anyhow::{Context, Result};
use pf_frame::{CapturedFrame, CursorOverlay, DmabufFrame, FramePayload, PixelFormat};
use pf_zerocopy::ipc;
+39 -20
View File
@@ -5,12 +5,16 @@
//! `libloading`), the device binding (D3D11 vs CUDA), input-surface registration, and the
//! Windows-only async retrieve — stay in their backends. Sibling of [`super::nvenc_status`].
// UNSAFE-LINT EXEMPTION (rationale + exit criteria: `unsafe_op_in_unsafe_fn` in the workspace
// Cargo.toml). This body is raw `nvEncodeAPI` entry-table calls almost line for line; narrowing it
// would add one `unsafe {}` plus one SAFETY comment per call that could only restate the signature.
// Clearing this file means DELETING the markers that carry no caller contract, not wrapping the
// calls — until then the lint is off HERE and enforced everywhere else.
#![allow(unsafe_op_in_unsafe_fn)]
// UNSAFE-LINT EXEMPTION REMOVED — the old fence rationale ("raw nvEncodeAPI entry-table calls
// almost line for line") was false for this file: it makes ZERO FFI calls. Its unsafe surface is
// C-union access whose soundness hangs entirely on which codec arm is active, and the 4:4:4 note
// below records the shipped bug (hevcConfig bytes stamped onto an AV1 config) that per-operation
// visibility makes findable. So this file runs the strictest discipline in the crate: every
// union READ, borrow, or bitfield-setter call sits in its own `unsafe {}` block naming the codec
// guard it relies on. (Plain union-arm field WRITES are safe by language rule — writing an arm
// cannot itself be UB; the hazard is the mismatched read — so those stay bare, guarded by the
// same codec matches.)
#![deny(clippy::multiple_unsafe_ops_per_block)]
use super::Codec;
use nvidia_video_codec_sdk::sys::nvEncodeAPI as nv;
@@ -694,10 +698,9 @@ mod tests {
};
assert_eq!(cfg.profileGUID, nv::NV_ENC_HEVC_PROFILE_FREXT_GUID);
// SAFETY: an HEVC session's union arm is `hevcConfig` — the one this path wrote.
unsafe {
assert_eq!(cfg.encodeCodecConfig.hevcConfig.chromaFormatIDC(), 3);
assert_eq!(cfg.encodeCodecConfig.hevcConfig.pixelBitDepthMinus8(), 2);
}
unsafe { assert_eq!(cfg.encodeCodecConfig.hevcConfig.chromaFormatIDC(), 3) };
// SAFETY: same HEVC arm as above.
unsafe { assert_eq!(cfg.encodeCodecConfig.hevcConfig.pixelBitDepthMinus8(), 2) };
}
#[test]
@@ -1210,6 +1213,8 @@ pub(super) unsafe fn apply_low_latency_config(cfg: &mut nv::NV_ENC_CONFIG, c: Lo
// are the only accepted config). H.264 has no tier. Level 0 = autoselect for HEVC.
match c.codec {
Codec::H265 => {
// Plain union-arm writes are safe by language rule (the hazard is a mismatched
// READ later); the match on `c.codec` keeps the arm honest.
cfg.encodeCodecConfig.hevcConfig.tier = 1;
cfg.encodeCodecConfig.hevcConfig.level = 0;
}
@@ -1264,21 +1269,29 @@ pub(super) unsafe fn apply_low_latency_config(cfg: &mut nv::NV_ENC_CONFIG, c: Lo
}
if want_444 && c.codec == Codec::H265 {
cfg.profileGUID = nv::NV_ENC_HEVC_PROFILE_FREXT_GUID;
cfg.encodeCodecConfig.hevcConfig.set_chromaFormatIDC(3);
// SAFETY: HEVC session (guarded by `c.codec == Codec::H265` on this branch), so
// `hevcConfig` is the active arm.
unsafe { cfg.encodeCodecConfig.hevcConfig.set_chromaFormatIDC(3) };
if c.bit_depth == 10 {
cfg.encodeCodecConfig.hevcConfig.set_pixelBitDepthMinus8(2); // Main 4:4:4 10
// SAFETY: same HEVC arm, same branch guard. (Main 4:4:4 10)
unsafe { cfg.encodeCodecConfig.hevcConfig.set_pixelBitDepthMinus8(2) };
}
} else if c.bit_depth == 10 {
match c.codec {
Codec::H265 => {
cfg.profileGUID = nv::NV_ENC_HEVC_PROFILE_MAIN10_GUID;
cfg.encodeCodecConfig.hevcConfig.set_pixelBitDepthMinus8(2);
// SAFETY: HEVC session (matched on `c.codec`), so `hevcConfig` is the active arm.
unsafe { cfg.encodeCodecConfig.hevcConfig.set_pixelBitDepthMinus8(2) };
}
Codec::Av1 => {
cfg.encodeCodecConfig.av1Config.set_pixelBitDepthMinus8(2);
cfg.encodeCodecConfig
.av1Config
.set_inputPixelBitDepthMinus8(c.av1_input_depth_minus8);
// SAFETY: AV1 session (matched on `c.codec`), so `av1Config` is the active arm.
unsafe { cfg.encodeCodecConfig.av1Config.set_pixelBitDepthMinus8(2) };
// SAFETY: same AV1 arm, same match guard.
unsafe {
cfg.encodeCodecConfig
.av1Config
.set_inputPixelBitDepthMinus8(c.av1_input_depth_minus8)
};
}
Codec::H264 => {} // no 10-bit H.264 encode on NVENC — negotiation never asks
Codec::PyroWave => unreachable!("PyroWave never opens the direct-NVENC backend"),
@@ -1306,7 +1319,9 @@ pub(super) unsafe fn apply_low_latency_config(cfg: &mut nv::NV_ENC_CONFIG, c: Lo
};
match c.codec {
Codec::H265 => {
let vui = &mut cfg.encodeCodecConfig.hevcConfig.hevcVUIParameters;
// SAFETY: HEVC session (matched on `c.codec`), so `hevcConfig` is the active
// arm; the borrow is dropped before any other union access.
let vui = unsafe { &mut cfg.encodeCodecConfig.hevcConfig.hevcVUIParameters };
vui.videoSignalTypePresentFlag = 1;
vui.videoFullRangeFlag = 0;
vui.colourDescriptionPresentFlag = 1;
@@ -1315,7 +1330,9 @@ pub(super) unsafe fn apply_low_latency_config(cfg: &mut nv::NV_ENC_CONFIG, c: Lo
vui.colourMatrix = mat;
}
Codec::H264 => {
let vui = &mut cfg.encodeCodecConfig.h264Config.h264VUIParameters;
// SAFETY: H.264 session (matched on `c.codec`), so `h264Config` is the active
// arm; the borrow is dropped before any other union access.
let vui = unsafe { &mut cfg.encodeCodecConfig.h264Config.h264VUIParameters };
vui.videoSignalTypePresentFlag = 1;
vui.videoFullRangeFlag = 0;
vui.colourDescriptionPresentFlag = 1;
@@ -1324,7 +1341,9 @@ pub(super) unsafe fn apply_low_latency_config(cfg: &mut nv::NV_ENC_CONFIG, c: Lo
vui.colourMatrix = mat;
}
Codec::Av1 => {
let av1 = &mut cfg.encodeCodecConfig.av1Config;
// SAFETY: AV1 session (matched on `c.codec`), so `av1Config` is the active arm;
// the borrow is dropped before any other union access.
let av1 = unsafe { &mut cfg.encodeCodecConfig.av1Config };
av1.colorPrimaries = prim;
av1.transferCharacteristics = trc;
av1.matrixCoefficients = mat;
-2
View File
@@ -12,8 +12,6 @@
//! defaulting to BT.709 limited — true of every punktfunk client (`csc_rows` falls back to 709 on
//! "unspecified"), but NOT of vendor TV decoders, which guess colorimetry from RESOLUTION: an LG
//! webOS panel reads a 4K SDR stream as BT.2020 and renders it visibly washed out.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use super::{EncodedFrame, Encoder};
use anyhow::{bail, ensure, Context, Result};
-2
View File
@@ -49,8 +49,6 @@
// restate the signature. Clearing this file means DELETING the markers that carry no caller
// contract, not wrapping the calls — until then the lint is off HERE and enforced everywhere else.
#![allow(unsafe_op_in_unsafe_fn)]
// Every `unsafe` block / impl in this file carries a `// SAFETY:` proof; enforce it.
#![deny(clippy::undocumented_unsafe_blocks)]
use super::{ChromaFormat, Codec, EncodedFrame, Encoder, EncoderCaps};
use anyhow::{anyhow, bail, Context, Result};
@@ -37,8 +37,6 @@
//! through `ffmpeg::ffi` (= `ffmpeg_sys_next`), exactly as the Linux CUDA/VAAPI paths do. The
//! `AVD3D11VADeviceContext`/`AVD3D11VAFramesContext` layouts are mirrored (the bindings don't
//! allowlist `hwcontext_d3d11va.h`), as [`super::linux`] mirrors `AVCUDADeviceContext`.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use super::{ChromaFormat, Codec, EncodedFrame, Encoder};
use anyhow::{anyhow, bail, Context, Result};
@@ -39,8 +39,6 @@
// the signature. Clearing this file means DELETING the markers that carry no caller contract, not
// wrapping the calls — until then the lint is off HERE and enforced everywhere else.
#![allow(unsafe_op_in_unsafe_fn)]
// Every `unsafe` block / impl in this file carries a `// SAFETY:` proof; enforce it.
#![deny(clippy::undocumented_unsafe_blocks)]
use super::nvenc_core::{
apply_low_latency_config, build_init_params, cached_ceiling, codec_guid, plan_range_recovery,
-3
View File
@@ -37,9 +37,6 @@
//! it stays behind the same gate and falls back to IDR wherever the driver declines. 4:4:4 stays
//! `false` until probed on real hardware (design §8.6).
// Every `unsafe` block / impl in this file carries a `// SAFETY:` proof; enforce it.
#![deny(clippy::undocumented_unsafe_blocks)]
use super::{ChromaFormat, Codec, EncodedFrame, Encoder, EncoderCaps};
use anyhow::{anyhow, bail, Context, Result};
use libvpl_sys as vpl;
-1
View File
@@ -12,7 +12,6 @@
// `#[cfg(test)]` instead.
// Every unsafe block in this module tree carries a `// SAFETY:` proof; enforce it (unsafe-proof
// program). As a parent module this also covers the child modules (windows/linux backends).
#![deny(clippy::undocumented_unsafe_blocks)]
use anyhow::Result;
use pf_frame::{CapturedFrame, PixelFormat};
-3
View File
@@ -7,9 +7,6 @@
//! The win32u GPU-preference hook, the HDR/video-engine converters, and the self-tests stay in the
//! capture crate — they are capture mechanics, not shared identity.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use anyhow::{Context, Result};
use windows::core::Interface;
use windows::Win32::Foundation::{HMODULE, LUID};
-1
View File
@@ -10,7 +10,6 @@
//! tuning), and — on Windows — [`dxgi`] (the capture identity + D3D11 device creation).
// Unsafe-proof program: every `unsafe {}` / `unsafe impl` must carry a `// SAFETY:` proof.
#![deny(clippy::undocumented_unsafe_blocks)]
pub mod hdr;
pub mod metronome;
-3
View File
@@ -11,9 +11,6 @@
//! state) auto-revert at thread exit (= session end); the process-wide bits revert at process exit.
//! See `design/host-latency-plan.md` Tier 3A.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
#[cfg(target_os = "windows")]
mod imp {
#![allow(non_snake_case)]
-3
View File
@@ -3,9 +3,6 @@
//! can't deschedule them; the native, GameStream, and direct-NVENC send threads all reach this the
//! same way (`pf_frame::thread_qos::boost_thread_priority`).
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
/// Raise the current thread's OS scheduling priority so a CPU-heavy game can't deschedule our
/// capture/encode/send threads. This matters even though our GPU work is already HIGH priority: the
/// GPU scheduler can only favour commands we've actually SUBMITTED, so if a normal-priority thread is
-1
View File
@@ -23,7 +23,6 @@
//! live session actually encodes on, for the console's "in use" display.
// Unsafe-proof program: every `unsafe {}` in this leaf carries a `// SAFETY:` proof.
#![deny(clippy::undocumented_unsafe_blocks)]
use anyhow::Result;
use serde::{Deserialize, Serialize};
@@ -15,9 +15,6 @@
//! `<linux/uinput.h>` on x86_64. `/dev/uinput` needs a udev rule + `input` group membership
//! (see `scripts/60-punktfunk.rules`); creation fails with a clear error otherwise.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use crate::pad_slots::PadSlots;
use anyhow::{bail, Result};
use punktfunk_core::input::{gamepad, GamepadFrame, MAX_PADS};
@@ -17,8 +17,6 @@
//! output's logical rectangle — the same shape the libei backend uses with its EI region.
#![allow(clippy::all, dead_code, non_camel_case_types, non_snake_case, unused)]
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use super::{gs_button_to_evdev, vk_to_evdev, InputEvent, InputInjector};
use anyhow::{Context, Result};
-3
View File
@@ -6,9 +6,6 @@
//! to evdev/US), and translate events into virtual pointer/keyboard requests, tracking modifier
//! state so the compositor resolves shifted keysyms correctly.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use super::{gs_button_to_evdev, vk_to_evdev, InputEvent, InputInjector};
use anyhow::{bail, Context, Result};
use punktfunk_core::input::InputKind;
@@ -15,9 +15,6 @@
//! with its position (never at a stale point), tip edges get their own DOWN/UP frames, and a
//! range-leave is a final frame without `INRANGE`.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it.
#![deny(clippy::undocumented_unsafe_blocks)]
use anyhow::{Context, Result};
use punktfunk_core::input::{InputEvent, InputKind};
use punktfunk_core::quic::{
@@ -14,9 +14,6 @@
//! user's, and any layout re-reads a *position* as a *character* — on a German host that is
//! exactly the y↔z swap / ü-on-ö scramble.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it.
#![deny(clippy::undocumented_unsafe_blocks)]
use anyhow::Result;
use punktfunk_core::input::{InputEvent, InputKind};
use std::mem::size_of;
-7
View File
@@ -14,13 +14,6 @@
// Scaffold: trait methods + per-OS backends are defined ahead of the target that uses them.
#![allow(dead_code)]
// Every unsafe block in this crate carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
// …and its companion: without this, an `unsafe fn` body needs no blocks, so an unproven FFI call
// could hide inside one and still satisfy the deny above. The workspace keeps
// `unsafe_op_in_unsafe_fn` at `warn` while the encoder backends are cleared; this crate is at zero.
#![deny(unsafe_op_in_unsafe_fn)]
use anyhow::Result;
use punktfunk_core::input::{InputEvent, InputKind};
-1
View File
@@ -17,7 +17,6 @@
//! the decode chain there is Vulkan → D3D11VA → software.
// Unsafe-proof program: every `unsafe {}` in this crate carries a `// SAFETY:` proof.
#![deny(clippy::undocumented_unsafe_blocks)]
// THE VULKAN CONTRACT, stated once - most `// SAFETY:` proofs in this crate are an instance of it.
//
+3
View File
@@ -18,3 +18,6 @@ path = "src/main.rs"
[target.'cfg(target_os = "linux")'.dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
[lints]
workspace = true
-7
View File
@@ -39,13 +39,6 @@
// honest. (Was a bare crate-wide allow whose "scaffold, defined ahead of the target that uses them"
// rationale had stopped being true.)
#![cfg_attr(not(target_os = "linux"), allow(dead_code))]
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
// …and that program only covers a whole `unsafe fn` body once the body needs its own block: in
// edition 2021 `unsafe_op_in_unsafe_fn` is allow-by-default, which exempted this crate's hardest
// FFI from the deny above — every IOCTL wrapper, and `restore_displays_ccd`, the call the whole
// Windows teardown path depends on to give the operator their physical panels back.
#![deny(unsafe_op_in_unsafe_fn)]
use anyhow::Result;
pub use punktfunk_core::Mode;
@@ -23,9 +23,6 @@
//! "Could not find output". We talk raw Wayland on `$WAYLAND_DISPLAY`, so the host must run inside
//! the KWin session's environment.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use super::{Mode, VirtualDisplay, VirtualOutput};
use anyhow::{anyhow, bail, Context, Result};
use std::os::fd::{AsFd, AsRawFd};
@@ -20,8 +20,6 @@
//! each output's name / enabled / priority / current-mode size, then build a
//! `kde_output_configuration_v2` and `apply()` it, waiting for `applied` / `failed`.
#![deny(clippy::undocumented_unsafe_blocks)]
use std::collections::HashMap;
use std::os::fd::{AsFd, AsRawFd};
use std::time::{Duration, Instant};
@@ -14,9 +14,6 @@
//! its `Drop` releases the refcount (a *stale* lease — its monitor was preempted + recreated under it —
//! is a no-op, so it can never tear down the live monitor).
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use std::collections::BTreeMap;
use std::os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle};
use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering};
@@ -81,6 +81,8 @@ pub(crate) trait VdisplayDriver: Send + Sync {
/// The monitor is NOT departed; the caller CCD-forces the freshly-advertised mode afterwards.
/// The default errs so a backend without support routes to the re-arrival fallback.
///
// unsafe-fn-no-op-ok: trait method — the "dev is live" contract binds every impl; this
// default body is a stub that bails.
/// # Safety
/// `dev` must be the live control handle.
unsafe fn update_modes(&self, dev: HANDLE, key: &MonitorKey, mode: Mode) -> Result<()> {
@@ -114,6 +116,7 @@ mod tests {
fn open(&self, _reap_orphans: bool) -> Result<(OwnedHandle, u32, u32)> {
anyhow::bail!("fake driver has no control device")
}
// unsafe-fn-no-op-ok: signature mandated by the trait; test stub.
unsafe fn add_monitor(
&self,
_dev: HANDLE,
@@ -125,9 +128,11 @@ mod tests {
) -> Result<AddedMonitor> {
anyhow::bail!("fake driver adds no monitors")
}
// unsafe-fn-no-op-ok: signature mandated by the trait; test stub.
unsafe fn remove_monitor(&self, _dev: HANDLE, _key: &MonitorKey) -> Result<()> {
Ok(())
}
// unsafe-fn-no-op-ok: signature mandated by the trait; test stub.
unsafe fn ping(&self, _dev: HANDLE) -> Result<()> {
Ok(())
}
@@ -16,9 +16,6 @@
//! Only the driver-specific bits (GUID, IOCTL codes, request/reply structs, the version handshake) are
//! here, per `pf_driver_proto`.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use std::ffi::c_void;
use std::mem::size_of;
use std::os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle};
+3 -4
View File
@@ -112,10 +112,9 @@
//! Unsafe posture: unlike pf-bitstream (which forbids unsafe outright), this crate
//! cannot — the `ash::vk::native` bindgen structs are zero-initialized the way the
//! encode side does it (`pf-encode/src/enc/linux/vk_build.rs`), and the GPU half is
//! Vulkan FFI. Every unsafe block therefore carries a written `// SAFETY:` proof,
//! enforced (and unlike the encoder there is NO file-level
//! `unsafe_op_in_unsafe_fn` exemption every operation is individually fenced):
#![deny(clippy::undocumented_unsafe_blocks)]
//! Vulkan FFI. Every unsafe block therefore carries a written `// SAFETY:` proof — enforced by
//! the workspace `[workspace.lints]` tables, and (unlike the encoder) with NO file-level
//! `unsafe_op_in_unsafe_fn` exemption: every operation is individually fenced.
pub mod caps;
pub mod caps_av1;
-2
View File
@@ -88,8 +88,6 @@
//! the readback geometry (row pitch / crop) or intra decode; mismatches that
//! only appear on later frames point at inter prediction / DPB management.
#![deny(clippy::undocumented_unsafe_blocks)]
mod common;
use ash::vk;
-2
View File
@@ -39,8 +39,6 @@
//! so releases pass `false`), soak, and both vendors' DPB arrangements at once
//! (each box exercises only its own).
#![deny(clippy::undocumented_unsafe_blocks)]
mod common;
use ash::vk;
@@ -28,9 +28,6 @@
//! suspects — without ever touching the CCD lock itself (the display-config lock is exactly what
//! stalls during churn; the capture thread must never block on it).
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use std::collections::VecDeque;
use std::sync::{Mutex, Once, OnceLock};
use std::time::Instant;
-2
View File
@@ -12,8 +12,6 @@
// `win_display` has denied both unsafe-proof lints since its CCD helpers stopped being `unsafe fn`;
// hoist that to the crate root so the smaller modules (`input_desktop`, `monitor_devnode`,
// `display_events`) and any future one are covered by default rather than by remembering to opt in.
#![deny(clippy::undocumented_unsafe_blocks)]
#![deny(unsafe_op_in_unsafe_fn)]
#[cfg(target_os = "windows")]
pub mod display_events;
-7
View File
@@ -8,13 +8,6 @@
//! them, which let the SudoVDA backend be dropped without losing them (audit §9 / Goal 2 — done). The
//! plan's `windows/display_ccd.rs`. Extracted verbatim from the former SudoVDA backend before its removal.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
// …and that program only covers a whole `unsafe fn` body once the body needs its own block: in
// edition 2021 `unsafe_op_in_unsafe_fn` is allow-by-default, which exempted every CCD/GDI helper
// below — including `restore_displays_ccd`, the call pf-vdisplay's teardown path depends on to give
// the operator their physical panels back.
#![deny(unsafe_op_in_unsafe_fn)]
// The CCD/GDI helpers below are SAFE fns. They were `unsafe fn` for a decade of habit rather than a
// memory-safety obligation: every one takes `Copy` scalars or borrowed Rust data, returns owned
// values, and discharges its own FFI preconditions internally (`retry_set_display_config` even binds
-3
View File
@@ -14,9 +14,6 @@
//! wait, no harm, and `WaitOutcome::NoFence` tells us the driver doesn't fence (so zero-copy
//! would still race).
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use std::os::fd::RawFd;
use std::time::{Duration, Instant};
-3
View File
@@ -6,9 +6,6 @@
//! A worker death — the whole point of the isolation — surfaces as an `Err` with
//! [`RemoteImporter::dead`] set, never as a host fault.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use super::cuda::{self, CUdeviceptr, DeviceBuffer, CU_IPC_HANDLE_SIZE};
use super::egl::DmabufPlane;
use super::ipc;
-2
View File
@@ -18,8 +18,6 @@
//! driver — see [`super::egl`].)
#![allow(non_camel_case_types, non_snake_case)]
// Every `unsafe` block/impl below carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use anyhow::{bail, Result};
use std::os::raw::{c_uint, c_void};
-2
View File
@@ -5,8 +5,6 @@
//! and drive this layer.
#![allow(non_camel_case_types, non_snake_case)]
// Every `unsafe` block/impl below carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use anyhow::{bail, Result};
use std::os::raw::{c_int, c_uint, c_void};
-2
View File
@@ -12,8 +12,6 @@
//! owned [`DeviceBuffer`] so the dmabuf can be returned to the compositor immediately.
#![allow(non_upper_case_globals)]
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use super::cuda::{self, DeviceBuffer};
use anyhow::{ensure, Context as _, Result};
-2
View File
@@ -5,8 +5,6 @@
//! [`super`].
#![allow(non_upper_case_globals)]
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use anyhow::{bail, ensure, Result};
use std::os::raw::{c_int, c_void};
-3
View File
@@ -18,9 +18,6 @@
//! inode with `punktfunk-host`, because a shared inode shares the file capability — so it passes
//! its own resolved path to [`spawn_worker`] instead.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::fs::File;
-3
View File
@@ -34,9 +34,6 @@
//! Falls back cleanly: if bring-up fails the encoder allocates plain CUDA surfaces and composite
//! mode degrades to no cursor (warned once) — never a failed session.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use super::cuda::{self, CUdeviceptr};
use anyhow::{anyhow, Context as _, Result};
use ash::vk;
-3
View File
@@ -16,9 +16,6 @@
//! a stream's life). Falls back cleanly: any init/import error disables the importer and the
//! CPU mmap path takes over.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use super::cuda::{self, DeviceBuffer};
use anyhow::{anyhow, bail, Context as _, Result};
use ash::vk;
-3
View File
@@ -9,9 +9,6 @@
//! only happens after the capturer AND every in-flight frame on the host side are gone, so pooled
//! device memory is never freed under a frame the host still reads.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use super::cuda::{self, CUdeviceptr, DeviceBuffer};
use super::egl::{DmabufPlane, EglImporter};
use super::ipc;
+3 -7
View File
@@ -8,13 +8,9 @@
//! consumes the shared frame vocabulary, which sits ABOVE this crate (this crate provides the
//! `DeviceBuffer` that vocabulary's `FramePayload::Cuda` owns).
// Unsafe-proof program: every `unsafe {}` / `unsafe impl` must carry a `// SAFETY:` proof. Each
// file keeps its own `#![deny(...)]` too; this crate-root deny is the catch-all gate.
// `unsafe_op_in_unsafe_fn` closes the gap the clippy lint leaves: operations inside an
// `unsafe fn` body are not "unsafe blocks", so without it ~45 functions' worth of raw driver
// calls sat OUTSIDE the invariant this crate advertises.
#![deny(clippy::undocumented_unsafe_blocks)]
#![deny(unsafe_op_in_unsafe_fn)]
// Unsafe-proof program: every `unsafe {}` / `unsafe impl` carries a `// SAFETY:` proof, and
// `unsafe fn` bodies need explicit blocks (~45 functions' worth of raw driver calls used to sit
// outside that invariant). Both lints are enforced by the workspace `[workspace.lints]` tables.
/// Wait for a dmabuf's implicit read-ready fence (`DMA_BUF_IOCTL_EXPORT_SYNC_FILE` + poll).
#[cfg(target_os = "linux")]
+23 -14
View File
@@ -53,6 +53,19 @@ use std::os::raw::c_char;
use std::panic::AssertUnwindSafe;
use std::ptr;
/// Poison-recovering lock for the C ABI surface. `.lock().unwrap()` inside an `extern "C"` fn
/// turns a poisoned mutex (some other thread panicked mid-write) into a panic across the C
/// boundary — an abort since Rust 1.81, exactly the class the panic-in-extern grep gate exists
/// for. The slots behind these mutexes are plain last-value caches (frame/audio/cursor/clip), so
/// whatever a poisoned writer left behind is still structurally valid data to overwrite or hand
/// out; recovering the guard is strictly better than aborting the embedding application.
/// (`quic`-gated with its only callers, the `punktfunk_connection_*` entry points — a
/// `default-features = false` consumer like the tray would otherwise see dead code.)
#[cfg(feature = "quic")]
fn lock_recover<T>(m: &std::sync::Mutex<T>) -> std::sync::MutexGuard<'_, T> {
m.lock().unwrap_or_else(std::sync::PoisonError::into_inner)
}
/// Opaque session handle. Pointer-only from C.
pub struct PunktfunkSession {
inner: Session,
@@ -471,8 +484,7 @@ pub unsafe extern "C" fn punktfunk_client_poll_frame(
}
match s.inner.poll_frame() {
Ok(frame) => {
s.last_frame = Some(frame);
let f = s.last_frame.as_ref().unwrap();
let f = s.last_frame.insert(frame);
// SAFETY: per the ABI contract - `out` is a caller-owned writable slot of the
// matching `#[repr(C)]` type, written once by value.
unsafe {
@@ -2249,9 +2261,8 @@ pub unsafe extern "C" fn punktfunk_connection_next_au(
.next_frame(std::time::Duration::from_millis(timeout_ms as u64))
{
Ok(frame) => {
let mut slot = c.last.lock().unwrap();
*slot = Some(frame);
let f = slot.as_ref().unwrap();
let mut slot = lock_recover(&c.last);
let f = slot.insert(frame);
// SAFETY: per the ABI contract - `out` is a caller-owned writable slot of the
// matching `#[repr(C)]` type, written once by value.
unsafe {
@@ -2314,9 +2325,8 @@ pub unsafe extern "C" fn punktfunk_connection_next_audio(
.next_audio(std::time::Duration::from_millis(timeout_ms as u64))
{
Ok(pkt) => {
let mut slot = c.last_audio.lock().unwrap();
*slot = Some(pkt);
let p = slot.as_ref().unwrap();
let mut slot = lock_recover(&c.last_audio);
let p = slot.insert(pkt);
// SAFETY: per the ABI contract - `out` is a caller-owned writable slot of the
// matching `#[repr(C)]` type, written once by value.
unsafe {
@@ -2467,7 +2477,7 @@ pub unsafe extern "C" fn punktfunk_connection_next_audio_pcm(
Ok(pkt) => pkt,
Err(e) => return e.status(),
};
let mut state = c.audio_pcm.lock().unwrap();
let mut state = lock_recover(&c.audio_pcm);
match state.decode_packet(&pkt.data, pkt.seq, channels) {
// Nothing to hand out this call: a DTX silence marker with no loss owed before it.
Ok(0) => PunktfunkStatus::NoFrame,
@@ -3072,9 +3082,8 @@ pub unsafe extern "C" fn punktfunk_connection_next_cursor_shape(
.next_cursor_shape(std::time::Duration::from_millis(timeout_ms as u64))
{
Ok(shape) => {
let mut slot = c.last_cursor_shape.lock().unwrap();
*slot = Some(shape);
let sh = slot.as_ref().unwrap();
let mut slot = lock_recover(&c.last_cursor_shape);
let sh = slot.insert(shape);
// SAFETY: per the ABI contract - `out` is a caller-owned writable slot of the
// matching `#[repr(C)]` type, written once by value.
unsafe {
@@ -4053,7 +4062,7 @@ pub unsafe extern "C" fn punktfunk_connection_next_clipboard(
.next_clip(std::time::Duration::from_millis(timeout_ms as u64))
{
Ok(ev) => {
let mut slot = c.last_clip.lock().unwrap();
let mut slot = lock_recover(&c.last_clip);
let out_ev = build_clip_event(ev, &mut slot);
// SAFETY: per the ABI contract - a caller-owned out-param, non-null on this path,
// written once by value.
@@ -4065,7 +4074,7 @@ pub unsafe extern "C" fn punktfunk_connection_next_clipboard(
// traffic is sporadic, so without this a one-off 50 MiB paste stays resident
// for the rest of the session (there is no other release entry point). The
// borrow contract already says `out` data is valid only until the next call.
*c.last_clip.lock().unwrap() = None;
*lock_recover(&c.last_clip) = None;
e.status()
}
}
-1
View File
@@ -46,7 +46,6 @@
// `qos_windows`) — sendmmsg/recvmsg_x/USO/qWAVE move caller-owned buffers, nothing more.
// A new module parsing wire data may NOT add a carve-out.
#![deny(unsafe_code)]
#![deny(clippy::undocumented_unsafe_blocks)]
#![forbid(unsafe_op_in_unsafe_fn)]
pub mod abi;
@@ -46,9 +46,6 @@
//! `PUNKTFUNK_KEEP_DEFAULT`) leaves the user's chosen defaults untouched — the plan is still
//! computed, since the mic must still pick a target.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it.
#![deny(clippy::undocumented_unsafe_blocks)]
use super::wiring_plan::{self, plan, plan_with_formats, Endpoint, MixFormat, Wiring};
use anyhow::{anyhow, bail, Result};
use std::ffi::c_void;
@@ -21,9 +21,6 @@
//! endpoint of that name, and the probe restores the default playback/recording devices it
//! disturbed before exiting.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it.
#![deny(clippy::undocumented_unsafe_blocks)]
use super::pad_endpoint as pe;
use super::{audio_control, SAMPLE_RATE};
use anyhow::{anyhow, bail, Context, Result};
@@ -24,9 +24,6 @@
//! bundled one all carry no marker and are therefore untouchable here — uninstalling punktfunk
//! removes what punktfunk created, and nothing else.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it.
#![deny(clippy::undocumented_unsafe_blocks)]
use super::{audio_control, audio_probe, minted, pad_endpoint as pe};
use anyhow::Result;
use windows::Win32::Devices::DeviceAndDriverInstallation::SetupDiEnumDeviceInfo;
@@ -35,9 +35,6 @@
//! COM discipline matches the sibling modules: WASAPI/COM objects live on the thread that made
//! them (the provisioning worker, the capture thread); only channels and plain data cross.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it.
#![deny(clippy::undocumented_unsafe_blocks)]
use super::{audio_control, AudioCapturer, SAMPLE_RATE};
use anyhow::{anyhow, bail, Context, Result};
use std::collections::{HashSet, VecDeque};
@@ -28,9 +28,6 @@
//! ([`VirtualMic::set_target_depth`]), filling silence when the client isn't talking. WASAPI
//! objects are `!Send`, so they live entirely on that thread (mirrors `WasapiLoopbackCapturer`).
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it.
#![deny(clippy::undocumented_unsafe_blocks)]
use super::{audio_control, MicBackendStats, VirtualMic, SAMPLE_RATE};
use anyhow::{anyhow, Context, Result};
use std::collections::VecDeque;
@@ -17,9 +17,6 @@
//! data packets are consumed immediately and missing parity only costs loss recovery — so
//! the validated stereo path stays byte-identical (data packets only, exactly as before).
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it.
#![deny(clippy::undocumented_unsafe_blocks)]
#[cfg(any(target_os = "linux", target_os = "windows", test))]
use crate::audio::SAMPLE_RATE;
#[cfg(any(target_os = "linux", target_os = "windows"))]
@@ -3,9 +3,6 @@
//! either real portal desktop capture (`PUNKTFUNK_VIDEO_SOURCE=portal`, the portal PipeWire path) or
//! a synthetic test pattern (default). Runs on its own native thread.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it.
#![deny(clippy::undocumented_unsafe_blocks)]
use super::video::{FrameType, VideoPacketizer};
use super::VIDEO_PORT;
use crate::capture::{self, Capturer, FastSyntheticCapturer};
@@ -8,8 +8,6 @@
//! verified (ioctl numbers + a live signal→wait round trip), ready to wire in the moment a producer
//! gains working `SPA_META_SyncTimeline`.
#![allow(dead_code)]
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
//!
//! Compositors that render directly into the PipeWire buffer pool (Mutter's virtual
//! monitors) hand buffers over at GPU-submit time; on drivers without implicit dmabuf
@@ -37,8 +37,6 @@
//! self-heals. Deliberately
//! NOT default-on: it defeats idle downclocking for the whole box and is wrong on
//! battery-powered hosts.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use std::os::raw::{c_char, c_int, c_uint, c_void};
use std::sync::{Mutex, OnceLock};
+3 -9
View File
@@ -13,16 +13,10 @@
// Scaffold: trait methods and config paths are defined ahead of their backends.
#![allow(dead_code)]
// Unsafe-proof program: every `unsafe {}` / `unsafe impl` in the crate must carry a `// SAFETY:`
// proof of why it is sound. This crate-root deny is the permanent, catch-all gate (it also covers
// any future module); individual files keep their own `#![deny(...)]` as belt-and-suspenders.
#![deny(clippy::undocumented_unsafe_blocks)]
// The companion gate: a proof only covers what it is attached to, and an `unsafe fn` body without
// this lint needs no blocks at all — so an unproven FFI call could hide inside one and satisfy the
// deny above. The workspace sets `unsafe_op_in_unsafe_fn` to `warn` (a ratchet across ~590 sites);
// this crate is at zero, so it denies. Keep the marker only where a caller can actually violate
// Unsafe-proof program (both lints now enforced by the workspace `[workspace.lints]` tables):
// every `unsafe {}` / `unsafe impl` carries a `// SAFETY:` proof, and `unsafe fn` bodies need
// explicit blocks. Keep the `unsafe fn` marker only where a caller can actually violate
// something — a raw pointer or a borrowed `HANDLE` parameter, as in `service::spawn_host`.
#![deny(unsafe_op_in_unsafe_fn)]
mod audio;
mod bringup;
-3
View File
@@ -22,9 +22,6 @@
//! Trust: the host serves with its persistent identity (`~/.config/punktfunk/cert.pem`, shared
//! with GameStream pairing) and logs the SHA-256 fingerprint clients pin.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use anyhow::{anyhow, Context, Result};
use punktfunk_core::config::{CompositorPref, FecConfig, FecScheme, GamepadPref, Role};
use punktfunk_core::input::{InputEvent, InputKind};
@@ -9,7 +9,6 @@
//! diagnosis. The Rust-panic analogue (a panic hook that tees into `tracing`) lives in `main()`.
// Every `unsafe` block in this file carries a `// SAFETY:` proof (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use windows::Win32::Foundation::HMODULE;
use windows::Win32::System::Diagnostics::Debug::{
@@ -14,13 +14,8 @@
//! that is correct for launching *our own* streamer, but a store launcher needs the real user's token
//! for activation + auth). The host process itself stays SYSTEM.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
// …and the proofs only cover the whole file once an `unsafe fn` body needs its own blocks: the
// workspace sets `unsafe_op_in_unsafe_fn` to `warn`, which is a ratchet, not a floor. This module is
// at zero, so hold it there — `merged_env_block`'s pointer walk is the one real contract here, and
// it must not silently re-absorb the FFI calls around it.
#![deny(unsafe_op_in_unsafe_fn)]
// This module is at zero `unsafe fn` markers; hold it there — `merged_env_block`'s pointer walk
// is the one real contract here, and it must not silently re-absorb the FFI calls around it.
use anyhow::{bail, Context, Result};
use std::path::Path;
@@ -25,9 +25,6 @@
//! loaded into the service's environment and carried to the host child. Logs land in
//! `%ProgramData%\punktfunk\logs\`.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use anyhow::{bail, Context, Result};
use std::ffi::{c_void, OsString};
use std::os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle};
+4
View File
@@ -1,3 +1,7 @@
# Vendored snapshot — like pf-bitstream/vendor/cros-codecs, deliberately NOT opted into the
# workspace [lints] tables: upstream code stays as close to pristine as the trim allows, so a
# re-sync against upstream stays a diff, not an archaeology dig. (Zero `unsafe` today anyway.)
#
# Vendored + trimmed copy of the `usbip` crate (jiegec/usbip v0.8.0, MIT), reduced to the
# USB/IP *server simulation* path only: we present a virtual Steam Deck and let the local
# `vhci_hcd` attach it. The upstream crate hard-depends on `rusb`→`libusb1-sys` (for its USB
-1
View File
@@ -11,7 +11,6 @@
//! details. Windows-subsystem binary — a console exe in the HKLM Run key would flash a terminal
//! window at every sign-in.
// Unsafe-proof program: every `unsafe {}` in the tray carries a `// SAFETY:` proof.
#![deny(clippy::undocumented_unsafe_blocks)]
#![cfg_attr(windows, windows_subsystem = "windows")]
#[cfg(target_os = "linux")]
+7
View File
@@ -9,6 +9,11 @@
#![allow(non_snake_case)]
// Bindgen output for a C API: u128 layout warnings and the like are upstream's concern.
#![allow(improper_ctypes)]
// The workspace-wide undocumented_unsafe_blocks deny cannot apply to GENERATED code: bindgen
// emits `unsafe {}` in layout tests/accessors and nobody hand-writes proofs into OUT_DIR. This
// crate is bindings-only by charter (the safe wrapper lives with the consumer), so the allow is
// crate-wide; the hand-written link-sanity test below still carries its proof by convention.
#![allow(clippy::undocumented_unsafe_blocks)]
#[cfg(any(target_os = "linux", target_os = "windows"))]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
@@ -23,6 +28,8 @@ mod tests {
#[test]
fn api_version_matches_vendored_pin() {
let (mut major, mut minor, mut patch) = (0u32, 0u32, 0u32);
// SAFETY: the version query writes three u32s through live local out-pointers and
// touches no device or global state.
unsafe { pyrowave_get_api_version(&mut major, &mut minor, &mut patch) };
assert_eq!((major, minor, patch), (0, 4, 0), "vendored pyrowave API version moved — re-check the §4.2 protocol coupling before bumping");
}
+11
View File
@@ -15,6 +15,17 @@ version = "0.0.1"
license = "MIT OR Apache-2.0"
publish = false
# The same unsafe discipline as the main workspace (see its Cargo.toml for the full rationale).
# Restated here because THIS IS A SEPARATE WORKSPACE: the main tree's [workspace.lints] does not
# reach these crates, so any "workspace-wide" lint claim has to be made here too or it is false
# for the driver surface. Every member opts in with `[lints] workspace = true`.
# (`unsafe_op_in_unsafe_fn` is already the edition-2024 default; deny pins it explicitly.)
[workspace.lints.rust]
unsafe_op_in_unsafe_fn = "deny"
[workspace.lints.clippy]
undocumented_unsafe_blocks = "deny"
[workspace.dependencies]
wdk = "0.4.1"
wdk-sys = "0.5.1"
@@ -32,3 +32,6 @@ pf-umdf-util.workspace = true
default = ["hid"]
hid = ["wdk-sys/hid"]
nightly = ["wdk-sys/nightly", "wdk/nightly"]
[lints]
workspace = true
@@ -16,8 +16,6 @@
#![allow(non_snake_case, non_upper_case_globals, clippy::missing_safety_doc)]
// Every remaining `unsafe {}` (all WDF setup FFI) must carry a `// SAFETY:` proof.
#![deny(unsafe_op_in_unsafe_fn)]
#![deny(clippy::undocumented_unsafe_blocks)]
use core::sync::atomic::{AtomicPtr, AtomicU32, Ordering};
@@ -30,3 +30,6 @@ pf-umdf-util.workspace = true
default = ["hid"]
hid = ["wdk-sys/hid"]
nightly = ["wdk-sys/nightly", "wdk/nightly"]
[lints]
workspace = true
@@ -23,8 +23,6 @@
#![allow(non_snake_case, non_upper_case_globals, clippy::missing_safety_doc)]
// Every remaining `unsafe {}` (all WDF setup FFI) must carry a `// SAFETY:` proof.
#![deny(unsafe_op_in_unsafe_fn)]
#![deny(clippy::undocumented_unsafe_blocks)]
use core::sync::atomic::{AtomicPtr, AtomicU32, Ordering};

Some files were not shown because too many files have changed in this diff Show More