Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Afonso Oliveira <afonso.oliveira707@gmail.com>
Date: Tue, 1 Sep 2026 21:50:00 +0100
Subject: [PATCH] media: intel/ipu7: harden PSYS userptr pinning

Reject invalid and oversized userptr ranges before allocating their page
arrays. Keep the page count in size_t, use overflow-checked allocation,
remove FOLL_FORCE, and require the entire range to be pinned before
publishing attachment state. Clear the attachment state after releasing it.

Upstream-Status: Not submitted
---
drivers/media/pci/intel/ipu7/psys/ipu-psys.c | 68 ++++++++++++++++------------
1 file changed, 38 insertions(+), 30 deletions(-)

diff --git a/drivers/media/pci/intel/ipu7/psys/ipu-psys.c b/drivers/media/pci/intel/ipu7/psys/ipu-psys.c
index fee9ee8..c6c4b52 100644
--- a/drivers/media/pci/intel/ipu7/psys/ipu-psys.c
+++ b/drivers/media/pci/intel/ipu7/psys/ipu-psys.c
@@ -13,6 +13,7 @@
#include <linux/kthread.h>
#include <linux/mm.h>
#include <linux/module.h>
+#include <linux/overflow.h>
#include <linux/pm_runtime.h>
#include <linux/poll.h>
#include <uapi/linux/sched/types.h>
@@ -49,63 +50,63 @@ static DEFINE_MUTEX(ipu7_psys_mutex);

static int ipu7_psys_get_userpages(struct ipu7_dma_buf_attach *attach)
{
- struct vm_area_struct *vma;
- unsigned long start, end;
- int npages, array_size;
+ unsigned long start, last;
+ size_t npages;
struct page **pages;
struct sg_table *sgt;
int ret = -ENOMEM;
- int nr = 0;
+ long nr;
u32 flags;

+ if (WARN_ON_ONCE(attach->pages || attach->npages || attach->sgt))
+ return -EINVAL;
+
+ if (!attach->userptr || !attach->len || attach->len > MAX_RW_COUNT)
+ return -EINVAL;
+
start = (unsigned long)attach->userptr;
- end = PAGE_ALIGN(start + attach->len);
- npages = PHYS_PFN(end - (start & PAGE_MASK));
- array_size = npages * sizeof(struct page *);
+ if (check_add_overflow(start, (unsigned long)attach->len - 1, &last))
+ return -EOVERFLOW;
+
+ npages = (((last & PAGE_MASK) - (start & PAGE_MASK)) >> PAGE_SHIFT) + 1;
+ if (!npages || npages > INT_MAX)
+ return -E2BIG;

sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
if (!sgt)
return -ENOMEM;

- WARN_ON_ONCE(attach->npages);
-
- pages = kvzalloc(array_size, GFP_KERNEL);
+ pages = kvmalloc_array(npages, sizeof(*pages), GFP_KERNEL);
if (!pages)
goto free_sgt;

- mmap_read_lock(current->mm);
- vma = vma_lookup(current->mm, start);
- if (unlikely(!vma)) {
- ret = -EFAULT;
- goto error_up_read;
- }
- mmap_read_unlock(current->mm);
-
- flags = FOLL_WRITE | FOLL_FORCE | FOLL_LONGTERM;
+ flags = FOLL_WRITE | FOLL_LONGTERM;
nr = pin_user_pages_fast(start & PAGE_MASK, npages,
flags, pages);
- if (nr < npages)
- goto error;
-
- attach->pages = pages;
- attach->npages = npages;
+ if (nr < 0) {
+ ret = nr;
+ goto unpin_pages;
+ }
+ if ((size_t)nr != npages) {
+ ret = -EFAULT;
+ goto unpin_pages;
+ }

ret = sg_alloc_table_from_pages(sgt, pages, npages,
start & ~PAGE_MASK, attach->len,
GFP_KERNEL);
if (ret < 0)
- goto error;
+ goto unpin_pages;

+ attach->pages = pages;
+ attach->npages = npages;
attach->sgt = sgt;

return 0;

-error_up_read:
- mmap_read_unlock(current->mm);
-error:
- if (nr)
+unpin_pages:
+ if (nr > 0)
unpin_user_pages(pages, nr);
-
kvfree(pages);
free_sgt:
kfree(sgt);
@@ -126,6 +127,8 @@ static void ipu7_psys_put_userpages(struct ipu7_dma_buf_attach *attach)

sg_free_table(attach->sgt);
kfree(attach->sgt);
+ attach->pages = NULL;
+ attach->npages = 0;
attach->sgt = NULL;
}

@@ -580,6 +583,11 @@ static int ipu7_psys_getbuf(struct ipu_psys_buffer *buf,
return -EINVAL;
}

+ if (!buf->len || buf->len > MAX_RW_COUNT) {
+ dev_err(dev, "Invalid userptr buffer length\n");
+ return -EINVAL;
+ }
+
kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL);
if (!kbuf)
return -ENOMEM;
1 change: 1 addition & 0 deletions pkgbuilds/intel-ipu7-camera/70-ipu7-psys.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
KERNEL=="ipu7-psys0", GROUP="video", MODE="0660", TAG+="uaccess", SYMLINK+="ipu-psys0"
1 change: 0 additions & 1 deletion pkgbuilds/intel-ipu7-camera/90-ipu7-psys.rules

This file was deleted.

80 changes: 72 additions & 8 deletions pkgbuilds/intel-ipu7-camera/PKGBUILD
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

pkgname=intel-ipu7-camera
pkgver=1.0.5
pkgrel=1
pkgrel=2
pkgdesc="Intel IPU7 MIPI camera stack for Hurrican/Performance (OV08X40 + hardware ISP)"
arch=('x86_64')
url="https://github.com/TsaiGaggery/hurrican_omarchy_enabling"
Expand Down Expand Up @@ -44,6 +44,8 @@ source=(
"ipu7-camera-hal::git+https://github.com/intel/ipu7-camera-hal.git#commit=${_ipu7_camera_hal_commit}"
"icamerasrc::git+https://github.com/intel/icamerasrc.git#commit=${_icamerasrc_commit}"
"0004-ipu7-psys-register-device-bus.patch"
"0005-ipu7-psys-harden-userptr-pinning.patch"
"check-userptr-range.c"
"0003-icvs-set-rgbcamera_pwrup_host-0-for-Panther-Lake.patch"
"dkms-ipu7-drivers.conf"
"dkms-vision-drivers.conf"
Expand All @@ -54,23 +56,44 @@ source=(
"hide-ipu7-v4l2.conf"
"disable-libcamera.conf"
"71-ipu7-hide-isys.rules"
"90-ipu7-psys.rules"
"70-ipu7-psys.rules"
"camera-tmpfiles.conf"
"ov08x40.yaml"
"camera-sleep-hook"
"v4l2-relayd-ipu7.conf"
"v4l2-relayd-ipu7-override.conf"
)
sha256sums=(
'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP'
'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP'
'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP'
'SKIP' 'SKIP' 'SKIP'
'SKIP'
'SKIP'
'SKIP'
'SKIP'
'SKIP'
'efa80311f00b9cf2ffbe1831a73e5c648d92b69cbb6f11e9d77df5597e5e83e5'
'd29020f75e98a6972f1b4f60475fc0afcbdb132fb13f1759b04216ec38fdf944'
'1ed034193cf04cc75adc5001242a674a9acd1db8e96bd0b82e38377491c4805b'
'4f95dac5e120572e41ee934a504b4cf9f80856a84815e981f601a8d5eae23965'
'f9bc244fce9abd77d4696ac80929964f8b09654a832c9301f2f5eb3cf098c8e4'
'fa2dd2e79c5c1bfd205a5d99f6ed55aee0e9ea785b471de056ce853afecc51c0'
'b099b7d7a96c08bd7751e671c885c8e5a4e3b4b65ed8f73db93568fd49025d3a'
'c817de3a1a32dc61dc8496a5e94594232a8fc0dcc0fd17407b0a3479b74facbc'
'20b2a7f6527c6e651907e44366151f93dfb6c14076ed2af0f3a92cde6295f5b8'
'e7c7e5b9c6a88d7665f799dd89ee69066fd3d4eb58c3c594caeb837ad8cfe5e9'
'ee98ea71af7498fc169818a89e8df5afbf83ee859ddd3f1d96e0ddce5a42c73a'
'2fb30310cfeb501738391f2c7e192dc52d6ce17211d1cc285c758ccc9e269ec9'
'81043c96d53b9a4f13b68701bcfd03c9715d2ed1d8e998b24d519b2ae1e5c481'
'e164a48b49069bb397f15854f4a0c5a509baebdce982b8b1bfb7e09680d74e33'
'60860ef75b5d6f2969d6df7f8e67d59093cc198a9ffd1322baff4acb8a7b8990'
'228273640621e54784f8b75bf17c76f97f58fd6608d27bd40f0a7eb7743f26c7'
'4983e6a5a175a91d00387463cf2fb69fd6ea7473f4bc057d442d447390250657'
'2c26b850f7f5691c6b857ab7785d1092a7ad9b60d6361bbcfc174353719acf3e'
'c586f06dfab097f7a5d90c8adb4148fcc7bdb0dbb39a674986dab6c2492eae47'
)

prepare() {
cd "${srcdir}/ipu7-drivers"
patch -Np1 -i "${srcdir}/0004-ipu7-psys-register-device-bus.patch"
patch -Np1 -i "${srcdir}/0005-ipu7-psys-harden-userptr-pinning.patch"

cd "${srcdir}/vision-drivers"
git config user.email "build@localhost"
Expand Down Expand Up @@ -136,6 +159,46 @@ build() {
make -j"$(nproc)"
}

check() {
local driver_tree="${srcdir}/ipu7-drivers"
local psys_source="${srcdir}/ipu7-drivers/drivers/media/pci/intel/ipu7/psys/ipu-psys.c"
local check_tree="${srcdir}/ipu7-drivers-check"
local kernel_build

grep -q 'kvmalloc_array(npages, sizeof(\*pages)' "${psys_source}"
grep -q 'attach->len > MAX_RW_COUNT' "${psys_source}"
if grep -q 'FOLL_FORCE' "${psys_source}"; then
return 1
fi

cc -std=c11 -O2 -Wall -Wextra -Werror \
"${srcdir}/check-userptr-range.c" -o "${srcdir}/check-userptr-range"
"${srcdir}/check-userptr-range"

kernel_build="$(pacman -Qql linux-headers | sed -n \
'\#/build/Makefile$# { s#/Makefile$##; s#^#/#; p; q; }')"
if [[ ! -f "${kernel_build}/Makefile" ]]; then
printf 'Unable to locate the linux-headers build tree\n' >&2
return 1
fi

(
cd "${srcdir}"
"${kernel_build}/scripts/checkpatch.pl" --no-tree --strict --no-signoff \
0005-ipu7-psys-harden-userptr-pinning.patch
)

rm -rf "${check_tree}"
cp -a "${driver_tree}" "${check_tree}"
if ! make -C "${check_tree}" BUILD_INTEL_IPU_ACPI=1 \
KERNEL_SRC="${kernel_build}"; then
rm -rf "${check_tree}"
return 1
fi
test -s "${check_tree}/drivers/media/pci/intel/ipu7/psys/intel-ipu7-psys.ko"
rm -rf "${check_tree}"
}

package() {
# DKMS: IPU7 drivers (ISYS + PSYS + ACPI)
local ipu7_dkms_dir="${pkgdir}/usr/src/ipu7-drivers-${pkgver}"
Expand Down Expand Up @@ -215,8 +278,9 @@ package() {
# udev rules
install -Dm644 "${srcdir}/71-ipu7-hide-isys.rules" \
"${pkgdir}/usr/lib/udev/rules.d/71-ipu7-hide-isys.rules"
install -Dm644 "${srcdir}/90-ipu7-psys.rules" \
"${pkgdir}/usr/lib/udev/rules.d/90-ipu7-psys.rules"
# uaccess must be tagged before systemd's 73-seat-late.rules applies ACLs.
install -Dm644 "${srcdir}/70-ipu7-psys.rules" \
"${pkgdir}/usr/lib/udev/rules.d/70-ipu7-psys.rules"

# v4l2-relayd config for IPU7 camera
install -Dm644 "${srcdir}/v4l2-relayd-ipu7.conf" \
Expand Down
2 changes: 1 addition & 1 deletion pkgbuilds/intel-ipu7-camera/camera-tmpfiles.conf
Original file line number Diff line number Diff line change
@@ -1 +1 @@
d /run/camera 0777 root root -
d /run/camera 0770 root video -
50 changes: 50 additions & 0 deletions pkgbuilds/intel-ipu7-camera/check-userptr-range.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>

#define PAGE_SHIFT 12
#define PAGE_SIZE (1UL << PAGE_SHIFT)
#define MAX_RW_COUNT (INT_MAX & ~(PAGE_SIZE - 1))

static int page_array_size(uintptr_t start, uint64_t len,
size_t *npages, size_t *bytes)
{
uintptr_t last;

if (!len || len > MAX_RW_COUNT)
return -EINVAL;
if (__builtin_add_overflow(start, len - 1, &last))
return -EOVERFLOW;

*npages = (((last & ~(PAGE_SIZE - 1)) -
(start & ~(PAGE_SIZE - 1))) >> PAGE_SHIFT) + 1;
if (!*npages || *npages > INT_MAX)
return -E2BIG;
if (__builtin_mul_overflow(*npages, sizeof(void *), bytes))
return -EOVERFLOW;

return 0;
}

int main(void)
{
const uintptr_t start = UINT64_C(0x100000000);
const uint64_t exploit_pages = UINT64_C(0x20000001);
const uint64_t ordinary_size = UINT64_C(64) * 1024 * 1024;
size_t npages;
size_t bytes;

assert(page_array_size(start, ordinary_size, &npages, &bytes) == 0);
assert(npages == ordinary_size / PAGE_SIZE);
assert(bytes == (ordinary_size / PAGE_SIZE) * sizeof(void *));

assert(page_array_size(start, exploit_pages * PAGE_SIZE,
&npages, &bytes) == -EINVAL);
assert(page_array_size(UINTPTR_MAX - 100, 200,
&npages, &bytes) == -EOVERFLOW);
assert(page_array_size(start, 0, &npages, &bytes) == -EINVAL);

return 0;
}
25 changes: 23 additions & 2 deletions pkgbuilds/intel-ipu7-camera/intel-ipu7-camera.install
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,32 @@ _install_sleep_hook() {
chmod 755 /usr/lib/systemd/system-sleep/camera-sleep-hook
}

_apply_runtime_permissions() {
if ! /usr/bin/systemd-tmpfiles --create /usr/lib/tmpfiles.d/camera.conf; then
echo ":: WARNING: failed to restrict /run/camera; reboot before using the camera." >&2
fi

if [ -S /run/udev/control ]; then
if ! /usr/bin/udevadm control --reload-rules ||
! /usr/bin/udevadm trigger --action=change --sysname-match=ipu7-psys0; then
echo ":: WARNING: failed to restrict the IPU7 device; reboot before using the camera." >&2
fi
fi
}

_warn_if_driver_is_loaded() {
if [ -d /sys/module/intel_ipu7_psys ]; then
echo ":: SECURITY: reboot required to activate the updated IPU7 kernel driver." >&2
echo ":: The restricted device permissions are active until then." >&2
fi
}

post_install() {
_install_sleep_hook
_apply_runtime_permissions
systemctl daemon-reload
systemctl disable camera-init.service v4l2-relayd@ipu7.service 2>/dev/null || true
systemctl enable intel-ipu7-camera.service
udevadm control --reload-rules 2>/dev/null || true

if [ -n "$SUDO_USER" ]; then
usermod -aG video "$SUDO_USER" 2>/dev/null || true
Expand All @@ -37,12 +57,13 @@ post_install() {

post_upgrade() {
_install_sleep_hook
_apply_runtime_permissions
_warn_if_driver_is_loaded
systemctl daemon-reload
systemctl disable camera-init.service v4l2-relayd@ipu7.service 2>/dev/null || true
systemctl enable intel-ipu7-camera.service
# Disable old camera-feed-watch (replaced by v4l2-relayd)
systemctl --global disable camera-feed-watch.service 2>/dev/null || true
udevadm control --reload-rules 2>/dev/null || true

if [ -n "$SUDO_USER" ]; then
_add_pipewire_camera "/home/$SUDO_USER/.config/chromium-flags.conf"
Expand Down