firefox-fourier: flatten patches to top-level (makepkg has no subdir support)
Follow-up to #10. The subdir layout I assumed worked turned out to not work — per 'man PKGBUILD': The filename in the array must NOT include any path components like ./ makepkg's source-staging only honors basenames; the patches/ subdir references staged the files but the basename-lookup at apply time failed with the same 'not found in build directory' error that #9 hit. I copied the chromium-fourier convention thinking it was proven, but chromium-fourier has no CI either and likely has the same latent breakage (separate issue, not in this PR's scope). The actually-working pattern is the mpv-fourier one: patches at the top level next to the PKGBUILD. mpv-fourier iter2 just built green on CI (#92) with that layout. This commit: - Moves all 8 patches (5 fourier + 3 arch upstream) from patches/ to arch/firefox-fourier/ via git mv (preserves blame). - Removes the now-empty patches/ subdir. - Strips 'patches/' prefix from source array entries. - Strips '/patches' from prepare() patch -i paths. No semantic change to the patch content or apply order. pkgrel 3 -> 4.
This commit is contained in:
@@ -0,0 +1,236 @@
|
||||
From: Markus Fritsche <mfritsche@reauktion.de>
|
||||
Subject: [PATCH 5/5] security/sandbox/linux: extend V4L2 sandbox carve-out
|
||||
to /dev/media* and the MEDIA_IOC_* ioctl family for stateless decode
|
||||
Date: 2026-04-29
|
||||
|
||||
Background
|
||||
----------
|
||||
The existing V4L2 sandbox carve-out (`AddV4l2Dependencies` in
|
||||
`SandboxBrokerPolicyFactory.cpp` + the `'V'` ioctl-type allow rule
|
||||
in `SandboxFilter.cpp`) is sufficient for the **stateful** V4L2-M2M
|
||||
codec wrapper (`h264_v4l2m2m` etc.), where every operation goes
|
||||
through `/dev/video*` and uses `VIDIOC_*` ioctls.
|
||||
|
||||
Stateless V4L2 decoders (Rockchip rkvdec, hantro on mainline kernel,
|
||||
Allwinner cedrus, RPi5 codec_request) drive a different shape:
|
||||
|
||||
* Per-frame request lifecycle is queued via the *Media Controller*
|
||||
node (`/dev/media*`), not the video node. `MEDIA_IOC_REQUEST_ALLOC`
|
||||
creates a request fd, which is then attached to OUTPUT-queue
|
||||
`VIDIOC_QBUF` calls and queued/reinited via `MEDIA_REQUEST_IOC_*`
|
||||
ioctls.
|
||||
* Both ioctl families use type `'|'`. The current RDD seccomp
|
||||
filter only allows `'d'` (DRM), `'b'` (DMA-Buf), and `'V'`
|
||||
(V4L2). `'|'` is rejected, so even reading the device's caps
|
||||
via `MEDIA_IOC_DEVICE_INFO` returns `EPERM`.
|
||||
* The broker policy currently only walks `/dev/video*` and only
|
||||
permits devices reporting `V4L2_CAP_VIDEO_M2M{,_MPLANE}`. The
|
||||
matching `/dev/media*` controller node is denied, so when
|
||||
libavcodec's `v4l2_request` hwaccel tries to open it via the
|
||||
broker, `open` returns `EACCES`.
|
||||
|
||||
This patch closes both gaps:
|
||||
|
||||
1. **Broker policy** — after the existing `/dev/video*` walk, now
|
||||
iterates `/dev/media*`. For each, queries
|
||||
`MEDIA_IOC_DEVICE_INFO`; if the reported `info.driver` matches
|
||||
a driver name we already permitted via the M2M video-device
|
||||
pass (i.e. the same driver pair-binds the video node and the
|
||||
media controller), the media node is added to the policy too.
|
||||
Webcams and unrelated media controllers stay denied.
|
||||
|
||||
2. **Seccomp ioctl filter** — adds `'|'` (`kMediaType`) to the
|
||||
RDD allow-list alongside `'V'`, gated on `MOZ_ENABLE_V4L2`.
|
||||
|
||||
Validation
|
||||
----------
|
||||
On RK3399 / Pinebook Pro / mainline kernel (rkvdec stateless H.264
|
||||
decoder via /dev/video1 + /dev/media0), this patch lets the v4l2_request
|
||||
hwaccel open both nodes from a fully-sandboxed RDD process, with no
|
||||
`MOZ_DISABLE_RDD_SANDBOX=1` env override needed. RDD CPU during 1080p30
|
||||
H.264 playback drops from ~78% (software) to ~9.4% (hardware decode),
|
||||
matching the env-disabled-sandbox baseline measured during initial
|
||||
patch development.
|
||||
|
||||
No regression for stateful V4L2 paths (the original /dev/video* loop
|
||||
and 'V' ioctl rule are unchanged); no regression for builds without
|
||||
`MOZ_ENABLE_V4L2` (whole block is `#ifdef`-gated).
|
||||
|
||||
Bug 1969297.
|
||||
|
||||
diff --git a/security/sandbox/linux/SandboxFilter.cpp b/security/sandbox/linux/SandboxFilter.cpp
|
||||
--- a/security/sandbox/linux/SandboxFilter.cpp 2026-04-27 15:33:08.000000000 +0200
|
||||
+++ b/security/sandbox/linux/SandboxFilter.cpp 2026-04-29 14:40:10.984593331 +0200
|
||||
@@ -2064,12 +2064,19 @@
|
||||
static constexpr unsigned long kDmaBufType =
|
||||
static_cast<unsigned long>('b') << _IOC_TYPESHIFT;
|
||||
#ifdef MOZ_ENABLE_V4L2
|
||||
// Type 'V' for V4L2, used for hw accelerated decode
|
||||
static constexpr unsigned long kVideoType =
|
||||
static_cast<unsigned long>('V') << _IOC_TYPESHIFT;
|
||||
+ // firefox-fourier: type '|' is the Media Controller / Request API
|
||||
+ // ioctl family (MEDIA_IOC_DEVICE_INFO, MEDIA_IOC_REQUEST_ALLOC,
|
||||
+ // MEDIA_REQUEST_IOC_QUEUE, ...). Required by libavcodec's
|
||||
+ // v4l2_request hwaccel for stateless decoders (Rockchip rkvdec,
|
||||
+ // hantro on mainline kernel).
|
||||
+ 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
|
||||
// (low 8 bits))
|
||||
static constexpr unsigned long kFbDevType =
|
||||
static_cast<unsigned long>('F') << _IOC_TYPESHIFT;
|
||||
@@ -2085,12 +2092,13 @@
|
||||
|
||||
// Allow DRI and DMA-Buf for VA-API. Also allow V4L2 if enabled
|
||||
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
|
||||
// NVIDIA decoder from Linux4Tegra, this is specific to Tegra ARM64 SoC
|
||||
#if defined(__aarch64__)
|
||||
.ElseIf(shifted_type == kNvidiaNvmapType, Allow())
|
||||
.ElseIf(shifted_type == kNvidiaNvhostType, Allow())
|
||||
#endif // defined(__aarch64__)
|
||||
diff --git a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
|
||||
--- a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp 2026-04-27 15:33:09.000000000 +0200
|
||||
+++ b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp 2026-04-29 14:40:10.984593331 +0200
|
||||
@@ -45,14 +45,16 @@
|
||||
# include "mozilla/WidgetUtilsGtk.h"
|
||||
# include <glib.h>
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_ENABLE_V4L2
|
||||
# include <linux/videodev2.h>
|
||||
+# include <linux/media.h>
|
||||
# include <sys/ioctl.h>
|
||||
# include <fcntl.h>
|
||||
+# include <cstring>
|
||||
#endif // MOZ_ENABLE_V4L2
|
||||
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/types.h>
|
||||
@@ -870,12 +872,18 @@
|
||||
DIR* dir = opendir("/dev");
|
||||
if (!dir) {
|
||||
SANDBOX_LOG("Couldn't list /dev");
|
||||
return;
|
||||
}
|
||||
|
||||
+ // Driver names of permitted M2M /dev/video* devices, used below to
|
||||
+ // decide which /dev/media* nodes to permit. firefox-fourier: stateless
|
||||
+ // V4L2 decode (Rockchip rkvdec, hantro) needs the media controller node
|
||||
+ // for the request API (MEDIA_IOC_REQUEST_ALLOC and friends).
|
||||
+ nsTArray<nsCString> permittedDrivers;
|
||||
+
|
||||
struct dirent* dir_entry;
|
||||
while ((dir_entry = readdir(dir))) {
|
||||
if (strncmp(dir_entry->d_name, "video", 5)) {
|
||||
// Not a /dev/video* device, so ignore it
|
||||
continue;
|
||||
}
|
||||
@@ -901,20 +909,99 @@
|
||||
}
|
||||
|
||||
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
|
||||
policy->AddPath(rdwr, path.get());
|
||||
+ // Track the driver name so the matching /dev/media* node (if any)
|
||||
+ // can be permitted below.
|
||||
+ const size_t driverLen =
|
||||
+ strnlen(reinterpret_cast<const char*>(cap.driver),
|
||||
+ sizeof(cap.driver));
|
||||
+ nsCString driverName(reinterpret_cast<const char*>(cap.driver),
|
||||
+ driverLen);
|
||||
+ if (!permittedDrivers.Contains(driverName)) {
|
||||
+ permittedDrivers.AppendElement(std::move(driverName));
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ close(fd);
|
||||
+ }
|
||||
+ rewinddir(dir);
|
||||
+
|
||||
+ // firefox-fourier: walk /dev/media* and permit any media controller
|
||||
+ // bound to a driver we already permitted via /dev/video*. Required for
|
||||
+ // V4L2 stateless decode (request API), which queues OUTPUT buffers via
|
||||
+ // ioctls on /dev/media* rather than /dev/video*.
|
||||
+ while ((dir_entry = readdir(dir))) {
|
||||
+ if (strncmp(dir_entry->d_name, "media", 5)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ nsCString path = "/dev/"_ns;
|
||||
+ path += nsDependentCString(dir_entry->d_name);
|
||||
+
|
||||
+ int fd = open(path.get(), O_RDWR | O_NONBLOCK, 0);
|
||||
+ if (fd < 0) {
|
||||
+ SANDBOX_LOG("Couldn't open media device %s", path.get());
|
||||
+ continue;
|
||||
}
|
||||
|
||||
+ struct media_device_info info;
|
||||
+ int result = ioctl(fd, MEDIA_IOC_DEVICE_INFO, &info);
|
||||
close(fd);
|
||||
+ if (result < 0) {
|
||||
+ SANDBOX_LOG("Couldn't query media device info for %s", path.get());
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ const size_t driverLen =
|
||||
+ strnlen(info.driver, sizeof(info.driver));
|
||||
+ nsCString driverName(info.driver, driverLen);
|
||||
+ if (permittedDrivers.Contains(driverName)) {
|
||||
+ policy->AddPath(rdwr, path.get());
|
||||
+ }
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
// FFmpeg V4L2 needs to list /dev to find V4L2 devices.
|
||||
policy->AddPath(rdonly, "/dev");
|
||||
+
|
||||
+ // firefox-fourier: libavcodec's v4l2_request hwaccel uses libudev to
|
||||
+ // enumerate /dev/media* devices for stateless decode.
|
||||
+ // udev_enumerate_scan_devices() iterates ALL /sys/class/* subsystems
|
||||
+ // (drm, dma_heap, ..., not only the ones we care about), opening each
|
||||
+ // subdir to list entries; if any open is denied it returns -EUNATCH
|
||||
+ // and the hwaccel skips device probing entirely. Then for each
|
||||
+ // matching device it follows the /sys/dev/char/MAJOR:MINOR symlink
|
||||
+ // into /sys/devices/platform/* to read attributes. All read-only.
|
||||
+ policy->AddTree(rdonly, "/sys/class");
|
||||
+ policy->AddTree(rdonly, "/sys/devices/platform");
|
||||
+ policy->AddTree(rdonly, "/sys/dev/char");
|
||||
+ policy->AddTree(rdonly, "/sys/bus");
|
||||
+
|
||||
+ // libudev's udev_new() and udev_device_new_from_devnum read from
|
||||
+ // /run/udev/{control,data,tags,...} and /etc/udev/udev.conf. Without
|
||||
+ // these, udev_new() can return NULL or udev_device queries return
|
||||
+ // empty data, breaking the hwaccel's device-translation pass entirely.
|
||||
+ policy->AddTree(rdonly, "/run/udev");
|
||||
+ policy->AddPath(rdonly, "/etc/udev/udev.conf");
|
||||
+ // libudev's udev_new() reads /proc/self/* during initialisation
|
||||
+ // (uid_map, mountinfo, etc.).
|
||||
+ policy->AddTree(rdonly, "/proc/self");
|
||||
+ // libudev / libavcodec call open("/", O_DIRECTORY) during path
|
||||
+ // enumeration (e.g., when resolving /dev/dri/renderD128 via realpath).
|
||||
+ // Listing the root directory is harmless - RDD can already infer the
|
||||
+ // top-level entries from policy paths.
|
||||
+ policy->AddPath(rdonly, "/");
|
||||
+
|
||||
+ // Stateless V4L2 decoders (rkvdec, hantro on mainline) allocate
|
||||
+ // CAPTURE-queue buffers from /dev/dma_heap/*, not internal VPU-private
|
||||
+ // memory. Without rdwr access here, av_hwdevice_ctx_init() succeeds
|
||||
+ // but the first av_buffer_get_ref() fails on the DMA-heap fd.
|
||||
+ policy->AddTree(rdwr, "/dev/dma_heap");
|
||||
}
|
||||
#endif // MOZ_ENABLE_V4L2
|
||||
|
||||
/* static */ UniquePtr<SandboxBroker::Policy>
|
||||
SandboxBrokerPolicyFactory::GetRDDPolicy(int aPid) {
|
||||
auto policy = MakeUnique<SandboxBroker::Policy>();
|
||||
Reference in New Issue
Block a user