From 0187eb72ba1f6bdb2ce9cdcee73e31c50c5a1ee8 Mon Sep 17 00:00:00 2001 From: Stuart Cameron Date: Sun, 4 Oct 2026 23:11:41 +1100 Subject: [PATCH 1/2] fix(deps): akarin 1.4.1 -> 1.5.0, which segfaulted under SELinux (#101) The 1.4.1 Linux wheel allocated JIT code with new[] and mprotect()ed it executable. A page-sized buffer lands on the brk heap, where SELinux denies PROT_EXEC (execheap is off by default on Fedora/RHEL); the result was only ASSERTed, so a release build jumped into non-executable memory and every job died with SIGSEGV on the first akarin.Expr frame. Upstream fixed it in 1.5.0 by building with the anonymous-mmap allocator. The pin moves on all three platforms so output stays identical per OS. No hosted runner enforces SELinux, so download-deps-linux.sh now fails if libakarin.so lacks the mmap allocator's mapping name. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_014GLXdGLfPwgYjW1AkonGqN --- CLAUDE.md | 1 + Scripts/download-deps-linux.sh | 19 +++++++++++++- Scripts/download-deps-macos.sh | 2 +- Scripts/download-deps-windows.ps1 | 2 +- docs/ENGINEERING_NOTES.md | 41 ++++++++++++++++++++++++++++++- licenses/NOTICES.txt | 2 +- 6 files changed, 62 insertions(+), 5 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index d0bd1f6..0e317e3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1694,3 +1694,4 @@ Full write-ups (root causes, measurements) for each entry are in | 1.9.0 | 2026-08-15 | Adds **fluxsmooth** (unlocks havsfunc's STPresso), **bifrost** (temporal rainbow/dot-crawl removal), **retinex** (shadow-detail lift) — all pinned to the newest release with a published Windows binary | | 1.10.0 | 2026-08-31 | **Issue #82**: zsmooth now ships one build per x86 CPU baseline (haswell, x86_64_v2), fixing an illegal-instruction crash on pre-2013 CPUs. **FFmpeg pinned to 9.0 on all four platforms** (they had silently diverged: Windows on an unpinned post-9.0 master, macOS on floating 9.0.1, Linux stuck at 7.1 after BtbN garbage-collected the pinned tag) | | 1.11.0 | 2026-09 | **Issue #92**: every x86 bundle ships in two **CPU tiers** — `v3` (x86-64-v3; the plain asset names) and `v2` (`…-x64-v2`, anything older), chosen by the app from `vapourbox-worker --probe-cpu`. Each bundle carries one autoloaded zsmooth (replacing 1.10.0's two-builds-loaded-by-path). macOS v2 builds MVTools v24 from source without its AVX2 files, whose static initializers SIGILLed inside `dlopen` on pre-AVX Macs. v2 bundles are gated (SDE on Linux/Windows, static check on macOS) before publishing | +| 1.12.0 (unreleased) | — | **Issue #101**: akarin 1.4.1 → **1.5.0** on every platform. The 1.4.1 Linux wheel put JIT code on the `brk` heap, where SELinux denies `PROT_EXEC`, so every job segfaulted on Fedora/RHEL from deps 1.8.0 on. `download-deps-linux.sh` now fails if `libakarin.so` lacks the mmap allocator | diff --git a/Scripts/download-deps-linux.sh b/Scripts/download-deps-linux.sh index 81cca58..6cdca48 100755 --- a/Scripts/download-deps-linux.sh +++ b/Scripts/download-deps-linux.sh @@ -1443,7 +1443,16 @@ fi # wheel exists, the routing shim is arch-neutral, and keeping the two arches # identical avoids the same job producing different output per platform. # manylinux_2_35 is satisfied by our glibc 2.39 floor (ubuntu-24.04). -AKARIN_VERSION="1.4.1" +# +# 1.5.0 is the floor on Linux (issue #101). The 1.4.1 wheel allocated JIT code +# with new[] and then mprotect()ed it executable; a small allocation lands on +# the brk heap, where SELinux denies PROT_EXEC (execheap is off by default on +# Fedora/RHEL). The mprotect result was only ASSERTed, so a release build +# carried on and jumped into non-executable memory: SIGSEGV on the first +# akarin.Expr frame, in every job. 1.5.0 builds with +# REACTOR_ANONYMOUS_MMAP_NAME=akarin_jit, which takes the mmap path instead. +# No hosted runner enforces SELinux, so this is checked statically below. +AKARIN_VERSION="1.5.0" echo "" echo "=== Downloading akarin $AKARIN_VERSION (LLVM JIT for std.Expr) ===" if [ "$FORCE" = true ] || [ ! -f "$PLUGINS_DIR/libakarin.so" ]; then @@ -1486,6 +1495,14 @@ PYEOF exit 1 fi fi + # The mmap JIT allocator is a compile-time option whose only trace in + # the binary is the mapping's name. Without it the plugin works on the + # build machine and segfaults under SELinux (issue #101). + if ! LC_ALL=C grep -a -q "akarin_jit" "$PLUGINS_DIR/libakarin.so"; then + echo " ERROR: libakarin.so was not built with REACTOR_ANONYMOUS_MMAP_NAME;" + echo " it would allocate JIT code on the heap and crash under SELinux." + exit 1 + fi rm -rf "$BUILD_DIR/akarin" "$BUILD_DIR/akarin.whl" BUILT_PLUGINS+=("akarin") echo " Installed akarin -> libakarin.so" diff --git a/Scripts/download-deps-macos.sh b/Scripts/download-deps-macos.sh index b7a8fcd..b94fe30 100755 --- a/Scripts/download-deps-macos.sh +++ b/Scripts/download-deps-macos.sh @@ -1911,7 +1911,7 @@ download_prebuilt_plugin "LGhost" "liblghost.dylib" "$LGHOST_URL" # targets $MACOS_MIN_VERSION (issue #39), so shipping it would raise the Intel # floor to macOS 14 — and x86 already has the JIT, so it loses nothing. The # routing shim falls back to std.Expr wherever core.akarin is absent. -AKARIN_VERSION="1.4.1" +AKARIN_VERSION="1.5.0" echo "" echo "=== Downloading akarin $AKARIN_VERSION (LLVM JIT for std.Expr) ===" if [ "$ARCH" = "x86_64" ]; then diff --git a/Scripts/download-deps-windows.ps1 b/Scripts/download-deps-windows.ps1 index a6077f7..c300529 100644 --- a/Scripts/download-deps-windows.ps1 +++ b/Scripts/download-deps-windows.ps1 @@ -732,7 +732,7 @@ if (-not (Test-Path $DvdReadPath)) { # # akarin is bit-identical to std.Expr on 45 of the 46 expressions havsfunc # generates; the one exception rounds a single .5 tie down instead of to even. -$AkarinVersion = "1.4.1" +$AkarinVersion = "1.5.0" Write-Host "" Write-Host "Downloading akarin $AkarinVersion (LLVM JIT for std.Expr)..." -ForegroundColor Yellow if (-not (Test-Path "$PluginsDir\libakarin.dll")) { diff --git a/docs/ENGINEERING_NOTES.md b/docs/ENGINEERING_NOTES.md index 84ed66c..b980c3b 100644 --- a/docs/ENGINEERING_NOTES.md +++ b/docs/ENGINEERING_NOTES.md @@ -1658,13 +1658,52 @@ the system one for ffmpeg. Linux's zstd carries a **per-arch build hash** in its filename (`libzstd-5df4f4df…` on x64, `-a1561916…` on arm64), so glob it — and the ELF `NEEDED` entry uses that exact hashed name. -akarin is **LGPL-3.0** and statically links **LLVM 22.1.2** (Apache-2.0 with LLVM +akarin is **LGPL-3.0** and statically links **LLVM 22.1.8** (Apache-2.0 with LLVM exception); both are in `licenses/NOTICES.txt`. It adds ~61 MB uncompressed per platform, but only about **21 MB to each deps zip** — the earlier "the zips roughly double" estimate was wrong, because it compared uncompressed size against compressed zips. +### akarin 1.4.1 segfaulted under SELinux (issue #101, 2026-10-04) + +Every job crashed in `vspipe-bin` with `SIGSEGV` on Fedora from app 0.9.12 — the +first release on deps 1.8.0, which is where akarin arrived. The reporter's +`probe-plugin-compat` run isolated it: 22 plugins passed, `libakarin.so` crashed. + +The 1.4.1 Linux wheel was built without `REACTOR_ANONYMOUS_MMAP_NAME`, so +`expr2/reactor/ExecutableMemory.cpp` took its generic fallback: allocate the JIT +buffer with `new[]`, then `mprotect` it `r-x`. A page-sized allocation comes off +the **`brk` heap**, and SELinux refuses `PROT_EXEC` there (`execheap`, off by +default for unconfined users on Fedora/RHEL — unlike `execmem`, which is on). +The `mprotect` result is checked only by an `ASSERT`, compiled out in release, +so the plugin jumped into non-executable memory. Upstream: akarin issue #38, +fixed in **1.5.0** by defining the macro (`meson.build`), which switches to an +anonymous `mmap`. macOS (`MAP_JIT`) and Windows (`VirtualProtect`) never took +the heap path. + +Signatures worth recognising next time: + +- **A one-frame backtrace at `0x55…` with no module.** That range is the PIE + executable and its `brk` heap; a JIT that used `mmap` would be at `0x7f…`. + A crash *at* a heap address means the program counter is in data. +- **It depends on allocator state**, so it need not reproduce in every host + process: a buffer over glibc's mmap threshold comes from `mmap` and works. +- **No hosted runner enforces SELinux** (Ubuntu ships AppArmor), so CI cannot + see it. `download-deps-linux.sh` instead fails the build if `libakarin.so` + lacks the `akarin_jit` mapping name, the only trace the option leaves. + +The pin moved on all three platforms together to keep output identical per OS; +the Expr parity test passes unchanged against 1.5.0 (run on macos-arm64). 1.5.0 +also repaired constant parsing (`0x…` hex and `0…` octal prefixes, which the +README always documented but 1.4.1's `from_chars` call mishandled) — none of +havsfunc's generated expressions are affected. + +Workaround for an installed bundle that predates the fix: delete +`deps/linux-*/vapoursynth/plugins/libakarin.so`. Both the havsfunc shim and the +templates' `_expr()` fall back to `std.Expr`, which on x86 has its own JIT. + + ### Installing a deps bundle: staged, swapped, and version-directional `DependencyManager` **replaces** a bundle rather than merging into one, which is diff --git a/licenses/NOTICES.txt b/licenses/NOTICES.txt index a391685..562e3dc 100644 --- a/licenses/NOTICES.txt +++ b/licenses/NOTICES.txt @@ -160,7 +160,7 @@ LLVM — Apache-2.0 WITH LLVM-exception Copyright (c) the LLVM Project contributors https://llvm.org Licence text: Apache-2.0-LLVM-Exception.txt - LLVM 22.1.2 is statically linked into the akarin plugin above, which is where + LLVM 22.1.8 is statically linked into the akarin plugin above, which is where its JIT comes from. Listed here because that binary carries LLVM code. zsmooth — MIT From 7cd4b12370037f2573ad5ab92ae9bd9bc7e8a4bf Mon Sep 17 00:00:00 2001 From: Stuart Cameron Date: Sun, 4 Oct 2026 23:40:41 +1100 Subject: [PATCH 2/2] chore(deps): point the app at deps 1.12.0 (akarin 1.5.0, #101) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_014GLXdGLfPwgYjW1AkonGqN --- CLAUDE.md | 2 +- app/assets/deps-version.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 0e317e3..efb1d02 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1694,4 +1694,4 @@ Full write-ups (root causes, measurements) for each entry are in | 1.9.0 | 2026-08-15 | Adds **fluxsmooth** (unlocks havsfunc's STPresso), **bifrost** (temporal rainbow/dot-crawl removal), **retinex** (shadow-detail lift) — all pinned to the newest release with a published Windows binary | | 1.10.0 | 2026-08-31 | **Issue #82**: zsmooth now ships one build per x86 CPU baseline (haswell, x86_64_v2), fixing an illegal-instruction crash on pre-2013 CPUs. **FFmpeg pinned to 9.0 on all four platforms** (they had silently diverged: Windows on an unpinned post-9.0 master, macOS on floating 9.0.1, Linux stuck at 7.1 after BtbN garbage-collected the pinned tag) | | 1.11.0 | 2026-09 | **Issue #92**: every x86 bundle ships in two **CPU tiers** — `v3` (x86-64-v3; the plain asset names) and `v2` (`…-x64-v2`, anything older), chosen by the app from `vapourbox-worker --probe-cpu`. Each bundle carries one autoloaded zsmooth (replacing 1.10.0's two-builds-loaded-by-path). macOS v2 builds MVTools v24 from source without its AVX2 files, whose static initializers SIGILLed inside `dlopen` on pre-AVX Macs. v2 bundles are gated (SDE on Linux/Windows, static check on macOS) before publishing | -| 1.12.0 (unreleased) | — | **Issue #101**: akarin 1.4.1 → **1.5.0** on every platform. The 1.4.1 Linux wheel put JIT code on the `brk` heap, where SELinux denies `PROT_EXEC`, so every job segfaulted on Fedora/RHEL from deps 1.8.0 on. `download-deps-linux.sh` now fails if `libakarin.so` lacks the mmap allocator | +| 1.12.0 | 2026-10-04 | **Issue #101**: akarin 1.4.1 → **1.5.0** on every platform. The 1.4.1 Linux wheel put JIT code on the `brk` heap, where SELinux denies `PROT_EXEC`, so every job segfaulted on Fedora/RHEL from deps 1.8.0 on. `download-deps-linux.sh` now fails if `libakarin.so` lacks the mmap allocator | diff --git a/app/assets/deps-version.json b/app/assets/deps-version.json index ebe26f8..07387b3 100644 --- a/app/assets/deps-version.json +++ b/app/assets/deps-version.json @@ -1,6 +1,6 @@ { - "version": "1.11.0", - "releaseTag": "deps-v1.11.0", - "releaseDate": "2026-09-25", + "version": "1.12.0", + "releaseTag": "deps-v1.12.0", + "releaseDate": "2026-10-04", "githubRepo": "StuartCameronCode/VapourBox" }