Merge pull request 'Edition-2024 follow-up: the three gates only the PR's own CI could reach' (#180) from worktree-edition-2024 into main
ci / rust (push) Failing after 3m38s
apple / swift (push) Successful in 1m49s
android / android (push) Failing after 5m32s
ci / docs-site (push) Successful in 1m12s
ci / rust-arm64 (push) Failing after 3m36s
ci / bun-nix (push) Successful in 1m24s
ci / web (push) Successful in 2m24s
arch / build-publish (push) Failing after 5m50s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 10s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 10s
deb / build-publish (push) Failing after 1m29s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 7s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 10s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 1m1s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 17s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Successful in 17s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 1m14s
docker / builders-arm64cross (push) Successful in 10s
deb / build-publish-client-arm64 (push) Failing after 3m43s
apple / screenshots (push) Successful in 5m38s
deb / build-publish-host (push) Successful in 6m17s
docker / deploy-docs (push) Failing after 4m38s
windows-host / package (push) Successful in 12m27s
windows-host / winget-source (push) Skipped
windows-host / canary-manifest (push) Successful in 17s
flatpak / build-publish (push) Canceled after 6m57s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Canceled after 6m50s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Canceled after 6m50s

This commit was merged in pull request #180.
This commit is contained in:
2026-08-12 16:34:13 +00:00
5 changed files with 35 additions and 20 deletions
+18 -11
View File
@@ -870,6 +870,23 @@ fn deliver_deep_link(url: String) {
}
}
/// The crate's one runtime env mutation, isolated so `main.rs`'s `deny(unsafe_code)` covers
/// everything else and the exemption is a named function rather than a whole call site.
#[allow(unsafe_code)]
fn clear_steam_sdl_device_filter() {
for var in [
"SDL_GAMECONTROLLER_IGNORE_DEVICES",
"SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT",
] {
if let Ok(v) = std::env::var(var) {
tracing::info!(var, value = %v, "clearing Steam's SDL device filter");
// SAFETY: called at the top of `run()`, before GTK init or any other thread
// exists in this process — nothing reads the environment concurrently.
unsafe { std::env::remove_var(var) };
}
}
}
pub fn run() -> glib::ExitCode {
tracing_subscriber::fmt()
.with_env_filter(
@@ -879,17 +896,7 @@ pub fn run() -> glib::ExitCode {
// Steam launches its shortcuts with SDL_GAMECONTROLLER_IGNORE_DEVICES naming every
// physical pad Steam Input has virtualized; the Settings controller list needs the
// real devices (same rationale as the session binary).
for var in [
"SDL_GAMECONTROLLER_IGNORE_DEVICES",
"SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT",
] {
if let Ok(v) = std::env::var(var) {
tracing::info!(var, value = %v, "clearing Steam's SDL device filter");
// SAFETY: top of `run()`, before GTK init or any other thread exists in this
// process — nothing reads the environment concurrently.
unsafe { std::env::remove_var(var) };
}
}
clear_steam_sdl_device_filter();
// Headless paths (no GTK window).
if let Some(pin) = crate::cli::arg_value("--pair") {
return crate::cli::headless_pair(&pin);
+4 -1
View File
@@ -3,7 +3,10 @@
//! Hosts, pairing/trust, settings, and the desktop library page; every stream (and the
//! console game library) runs in the spawned `punktfunk-session` Vulkan binary — the
//! shell never touches video (punktfunk-planning `linux-client-rearchitecture.md`).
#![forbid(unsafe_code)]
// `deny`, not `forbid`, since edition 2024: clearing Steam's SDL device filter and the spawn
// test's `HOME` scoping mutate the process env, which is now an unsafe call. Both carry a named
// `#[allow(unsafe_code)]` with the proof at the site; everything else stays compiler-refused.
#![deny(unsafe_code)]
// The UI-agnostic plumbing lives in `pf-client-core`, shared with the session binary.
// Root re-exports keep every `crate::trust`-style path resolving unchanged.
+2
View File
@@ -128,6 +128,8 @@ mod tests {
/// that is merely capped. One test, one `HOME` — the stores are read from it, so this
/// deliberately does not split into several that would race over the same env var.
#[test]
// The crate's one test env mutation (the `HOME` scoping below) — see main.rs's deny note.
#[allow(unsafe_code)]
fn the_plan_carries_resolved_settings_not_defaults() {
use pf_client_core::profiles::{ProfilesFile, SettingsOverlay, StreamProfile};
use pf_client_core::trust::{KnownHost, KnownHosts, Settings};
+7 -4
View File
@@ -222,13 +222,16 @@ struct Adl {
/// out-pointers and expects the app to own them.
unsafe extern "C" fn adl_malloc(size: i32) -> *mut c_void {
let size = size.max(1) as usize;
// Panic-free: a panic here would cross the extern boundary and abort the host. The Err arm
// is unreachable in practice (size ≤ i32::MAX can't overflow the layout), and ADL treats a
// null from its allocator as an ordinary failure.
let Ok(layout) = std::alloc::Layout::from_size_align(size, 16) else {
return std::ptr::null_mut();
};
// SAFETY: non-zero size with a fixed valid alignment; the resulting buffers are deliberately
// never freed — ADL's contract wants an ADL_Main_Memory_Free symmetry, and leaking the <1 KiB
// of board-layout arrays in a one-shot probe is simpler than proving allocator parity.
unsafe {
std::alloc::alloc(std::alloc::Layout::from_size_align(size, 16).expect("tiny ADL alloc"))
as *mut c_void
}
unsafe { std::alloc::alloc(layout) as *mut c_void }
}
impl Adl {
+4 -4
View File
@@ -57,8 +57,8 @@ mod gamestream;
mod gpuclocks;
mod hooks;
// Network-facing on the secure default host (see the forbid block at `mod mgmt` below). Test
// builds carve out like `native`: the identity tests scope `PUNKTFUNK_CONFIG_DIR` via
// `env::set_var`, which edition 2024 makes an unsafe fn; shipped code keeps the forbid.
// builds carve out like `native`: the identity tests scope `PUNKTFUNK_CONFIG_DIR` by mutating
// the process env, which edition 2024 makes an unsafe call; shipped code keeps the forbid.
#[cfg_attr(not(test), forbid(unsafe_code))]
mod identity;
// The input-injection backends live in the `pf-inject` subsystem crate (plan §W6); this shim keeps
@@ -84,8 +84,8 @@ mod log_capture;
// here means a future edit cannot quietly introduce unsafe into a network-facing module.
// (`native` carves out its `#[cfg(test)]` C-ABI roundtrip tests, which exercise the CLIENT
// side of punktfunk-core against this host in-process and are unsafe by nature; `mgmt` and
// `identity` carve out test builds too — their tests scope `PUNKTFUNK_CONFIG_DIR` via
// `env::set_var`, an unsafe fn since edition 2024.)
// `identity` carve out test builds too — their tests scope `PUNKTFUNK_CONFIG_DIR` by mutating
// the process env, an unsafe call since edition 2024.)
#[cfg_attr(not(test), forbid(unsafe_code))]
mod mgmt;
#[forbid(unsafe_code)]