Files
libva-multiplanar/firefox-fourier/0001-rdd-allow-stateless-v4l2-request-api.patch
claude-noether d2d9107e62 iter5 amendment: extend Firefox sandbox patch to UtilitySandboxPolicy
Real-world YouTube avc1 playback on the iter5-G binary surfaced a
seccomp violation (`syscall 29`, `0x80047C05` = `MEDIA_IOC_REQUEST_ALLOC`)
that the autonomous Phase 7G test missed because seccomp returns
ENOSYS silently and Firefox falls back to SW decode.

Two distinct gaps:
- patch-sync drift: campaign 113-line patch (broker+RDD-seccomp) had
  drifted from container 84-line patch (broker only); iter5-G shipped
  with the broker fix but no RDD seccomp fix.
- coverage gap: FF150 routes VAAPI to the Utility process; iter3's
  RDD-only seccomp allowlist never covered Utility.

Combined patch now hits three gates across two files (six hunks):
- broker: cap-filter widen + AddV4l2RequestApiDependencies + RDD wire-in
- RDD seccomp: kMediaType allow alongside existing kVideoType
- Utility seccomp: new __NR_ioctl override mirroring RDD's allowlist

Build: incremental `makepkg -e` on existing iter5-G object tree took
2:22 wall vs the 2h27m from-scratch alternative.

phase8_iteration5_close.md: appended amendment section with verdict-
gap analysis, patch breakdown, deploy-pending status.

firefox-fourier/README.md: rewrote "The problem" from 2 gates to 3
(broker + RDD seccomp + Utility seccomp); patch summary now explains
the six hunks.

Pending: pkg deploy to ohm + lsof /dev/video1 verification once
network route to ohm is restored.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 19:20:30 +00:00

161 lines
6.9 KiB
Diff

From: claude-noether <claude@reauktion.de>
Date: 2026-05-05
Subject: [PATCH] sandbox/linux: allow V4L2 stateless request-API decoders in RDD + Utility
Firefox's RDD AND Utility process sandboxes block hardware video decode
for V4L2 stateless decoders (hantro G1/G2 on RK35xx, cedrus on Allwinner,
etc.). Since Firefox ~114 the VAAPI decode work has migrated from RDD to
the Utility process for many pipelines; both classes need the same
ioctl allowlist for V4L2-stateless to work.
Three distinct gates close the door:
1. Broker policy: AddV4l2Dependencies() filters /dev/video* by
VIDEO_M2M / VIDEO_M2M_MPLANE capability. Stateless decoders
advertise CAPTURE_MPLANE + OUTPUT_MPLANE + STREAMING but typically
not M2M, so /dev/video1 (the hantro device) is silently dropped.
2. Broker policy: GetRDDPolicy() never references /dev/media*. The
V4L2 request API lives on /dev/media* nodes that the broker won't
open from RDD.
3. Seccomp policy: RDDSandboxPolicy and UtilitySandboxPolicy do not
allow ioctl magic byte '|' (linux/media.h). Even after broker
permits the open, the kernel ioctl path is filtered, returning
ENOSYS to userspace and causing libva to abandon decode.
Empirically: "Sandbox: seccomp sandbox violation: ... syscall 29"
where syscall 29 is ioctl on aarch64 and the filtered request is
MEDIA_IOC_REQUEST_ALLOC (_IOR('|', 0x05, int)).
iter5 amendment: extends the original iter3 RDD seccomp fix to also
cover UtilitySandboxPolicy (which previously fell through to
SandboxPolicyCommon's restrictive ioctl handler). Required because
PID-by-PID inspection on Firefox 150 shows VAAPI work happening in
the Utility process, not RDD.
Tested: libva-v4l2-request-fourier on PineTab2 (RK3568, hantro G1)
playing bbb_1080p30 H.264 via Firefox 150 without
MOZ_DISABLE_RDD_SANDBOX=1.
---
--- a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
+++ b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
@@ -901,8 +901,16 @@
}
if ((cap.device_caps & V4L2_CAP_VIDEO_M2M) ||
- (cap.device_caps & V4L2_CAP_VIDEO_M2M_MPLANE)) {
- // This is an M2M device (i.e. not a webcam), so allow access
+ (cap.device_caps & V4L2_CAP_VIDEO_M2M_MPLANE) ||
+ // V4L2 stateless decoders (hantro G1/G2 on Rockchip, cedrus on
+ // Allwinner, etc.) report CAPTURE_MPLANE + OUTPUT_MPLANE +
+ // STREAMING but do not set the M2M caps. They use the request API
+ // via /dev/media* (see AddV4l2RequestApiDependencies below).
+ ((cap.device_caps & V4L2_CAP_VIDEO_CAPTURE_MPLANE) &&
+ (cap.device_caps & V4L2_CAP_VIDEO_OUTPUT_MPLANE) &&
+ (cap.device_caps & V4L2_CAP_STREAMING))) {
+ // This is an M2M or stateless decode device (i.e. not a webcam),
+ // so allow access
policy->AddPath(rdwr, path.get());
}
@@ -913,6 +921,32 @@
// FFmpeg V4L2 needs to list /dev to find V4L2 devices.
policy->AddPath(rdonly, "/dev");
}
+
+// V4L2 stateless decoders submit per-frame decode requests via the
+// media-controller framework on /dev/media* nodes (ioctls in the
+// MEDIA_REQUEST_IOC_* family, magic byte '|', defined in <linux/media.h>).
+// These are required alongside /dev/video* for any request-API decoder.
+// We allow rdwr access to all /dev/media* nodes; the kernel's
+// media-controller layer enforces device-level access control.
+// This mirrors the model AddV4l2Dependencies uses for /dev/video*.
+static void AddV4l2RequestApiDependencies(SandboxBroker::Policy* policy) {
+ DIR* dir = opendir("/dev");
+ if (!dir) {
+ SANDBOX_LOG("Couldn't list /dev for media-controller nodes");
+ return;
+ }
+
+ struct dirent* dir_entry;
+ while ((dir_entry = readdir(dir))) {
+ if (strncmp(dir_entry->d_name, "media", 5)) {
+ continue;
+ }
+ nsCString path = "/dev/"_ns;
+ path += nsDependentCString(dir_entry->d_name);
+ policy->AddPath(rdwr, path.get());
+ }
+ closedir(dir);
+}
#endif // MOZ_ENABLE_V4L2
/* static */ UniquePtr<SandboxBroker::Policy>
@@ -979,6 +1013,7 @@
#ifdef MOZ_ENABLE_V4L2
AddV4l2Dependencies(policy.get());
+ AddV4l2RequestApiDependencies(policy.get());
#endif // MOZ_ENABLE_V4L2
// Bug 1903688: NVIDIA Tegra hardware decoding from Linux4Tegra
--- a/security/sandbox/linux/SandboxFilter.cpp
+++ b/security/sandbox/linux/SandboxFilter.cpp
@@ -2067,6 +2067,11 @@
// Type 'V' for V4L2, used for hw accelerated decode
static constexpr unsigned long kVideoType =
static_cast<unsigned long>('V') << _IOC_TYPESHIFT;
+ // Type '|' for the V4L2 request API on /dev/media* nodes
+ // (MEDIA_REQUEST_IOC_QUEUE et al, defined in <linux/media.h>).
+ // Required by V4L2 stateless decoders such as hantro/cedrus/sun*.
+ static constexpr unsigned long kMediaType =
+ static_cast<unsigned long>('|') << _IOC_TYPESHIFT;
#endif
// nvidia non-tegra uses some ioctls from this range (but not actual
// fbdev ioctls; nvidia uses values >= 200 for the NR field
@@ -2088,6 +2093,7 @@
.ElseIf(shifted_type == kDmaBufType, Allow())
#ifdef MOZ_ENABLE_V4L2
.ElseIf(shifted_type == kVideoType, Allow())
+ .ElseIf(shifted_type == kMediaType, Allow())
#endif
// NVIDIA decoder from Linux4Tegra, this is specific to Tegra ARM64 SoC
#if defined(__aarch64__)
@@ -2378,6 +2384,35 @@
case __NR_set_mempolicy:
return Error(ENOSYS);
+ // VAAPI hardware video decode in the Utility process. As of Firefox
+ // ~114 the VAAPI decode work moved from RDD to Utility for many
+ // pipelines; this filter mirrors the RDD ioctl allowlist so V4L2
+ // stateless decoders (hantro G1/G2 on Rockchip, cedrus on
+ // Allwinner, etc.) can issue MEDIA_REQUEST_IOC_* ioctls (magic
+ // byte '|', linux/media.h) and VIDIOC_* ioctls (magic byte 'V'),
+ // alongside the DRM/DMA-Buf surface-management calls.
+ case __NR_ioctl: {
+ Arg<unsigned long> request(1);
+ auto shifted_type = request & kIoctlTypeMask;
+ static constexpr unsigned long kDrmType =
+ static_cast<unsigned long>('d') << _IOC_TYPESHIFT;
+ static constexpr unsigned long kDmaBufType =
+ static_cast<unsigned long>('b') << _IOC_TYPESHIFT;
+#ifdef MOZ_ENABLE_V4L2
+ static constexpr unsigned long kVideoType =
+ static_cast<unsigned long>('V') << _IOC_TYPESHIFT;
+ static constexpr unsigned long kMediaType =
+ static_cast<unsigned long>('|') << _IOC_TYPESHIFT;
+#endif
+ return If(shifted_type == kDrmType, Allow())
+ .ElseIf(shifted_type == kDmaBufType, Allow())
+#ifdef MOZ_ENABLE_V4L2
+ .ElseIf(shifted_type == kVideoType, Allow())
+ .ElseIf(shifted_type == kMediaType, Allow())
+#endif
+ .Else(SandboxPolicyCommon::EvaluateSyscall(sysno));
+ }
+
// Pass through the common policy.
default:
return SandboxPolicyCommon::EvaluateSyscall(sysno);