All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jiri Olsa <jolsa@kernel.org>
To: Alexei Starovoitov <ast@kernel.org>,
	Daniel Borkmann <daniel@iogearbox.net>
Cc: bpf@vger.kernel.org, Andrii Nakryiko <andrii@kernel.org>,
	Eduard Zingerman <eddyz87@gmail.com>,
	Kumar Kartikeya Dwivedi <memxor@gmail.com>,
	linux-mm@kvack.org, Andrew Morton <akpm@linux-foundation.org>,
	"Liam R. Howlett" <liam@infradead.org>,
	Lorenzo Stoakes <ljs@kernel.org>,
	linux-kselftest@vger.kernel.org, Shuah Khan <shuah@kernel.org>,
	John Fastabend <john.fastabend@gmail.com>
Subject: [RFC bpf-next 4/9] bpf: Add kfuncs to allocate and seal user pages
Date: Wed, 21 Oct 2026 21:57:53 +0200	[thread overview]
Message-ID: <20261021195759.119421-5-jolsa@kernel.org> (raw)
In-Reply-To: <20261021195759.119421-1-jolsa@kernel.org>

Adding sleepable BPF kfuncs for allocating anonymous pages
in the current process and changing an allocated page to
read/execute permissions. The mapping remains in userspace
until it is unmapped or the process exits.

It's allowed only for sleepable kprobe type, which technicaly
means only sleepable uprobe.

Assisted-by: Codex:GPT-6
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 kernel/bpf/helpers.c | 127 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 127 insertions(+)

diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 4d9e9423826a..246f4969a144 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -29,6 +29,10 @@
 #include <linux/task_work.h>
 #include <linux/irq_work.h>
 #include <linux/buildid.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/personality.h>
+#include <asm/cacheflush.h>
 #include <asm/tlb.h>
 
 #include "../../lib/kstrtox.h"
@@ -3739,6 +3743,127 @@ __bpf_kfunc int bpf_copy_from_user_mm(void *dst, u32 dst__sz,
 	return ret;
 }
 
+/* vm_private_data token for the unmergeable anonymous mapping below. */
+static char bpf_user_page_marker;
+
+/**
+ * bpf_user_page_alloc() - Allocate lazy anonymous pages in the current mm
+ * @nr_pages: Number of pages to allocate
+ *
+ * Creates a read/write anonymous mapping in the current task's address space.
+ * The mapping is kept non-executable even for tasks with READ_IMPLIES_EXEC,
+ * to avoid creating a writable executable mapping. The physical page is
+ * allocated on its first access. The mapping remains until userspace unmaps
+ * it or the process exits.
+ *
+ * Return: The userspace address on success, or a negative error code.
+ */
+__bpf_kfunc long bpf_user_page_alloc(unsigned long nr_pages)
+{
+	const unsigned long prot = PROT_READ | PROT_WRITE;
+	const unsigned long flags = MAP_PRIVATE | MAP_ANONYMOUS;
+	struct mm_struct *mm = current->mm;
+	unsigned long addr, populate, len;
+	struct vm_area_struct *vma;
+	int err;
+
+	if (unlikely(in_interrupt() || current->flags & (PF_KTHREAD | PF_EXITING)))
+		return -EPERM;
+	if (unlikely(!nmi_uaccess_okay()))
+		return -EPERM;
+	if (unlikely(!mm))
+		return -EFAULT;
+	if (unlikely(!nr_pages || nr_pages > (ULONG_MAX >> PAGE_SHIFT)))
+		return -EINVAL;
+
+	len = nr_pages << PAGE_SHIFT;
+	err = security_mmap_file(NULL, prot, flags);
+	if (err)
+		return err;
+
+	if (mmap_write_lock_killable(mm))
+		return -EINTR;
+
+	addr = do_mmap(NULL, 0, len, prot, flags,
+		       mk_vma_flags(VMA_DONTEXPAND_BIT, VMA_DONTCOPY_BIT, VMA_DONTDUMP_BIT),
+		       0, &populate, NULL);
+	if (!IS_ERR_VALUE(addr)) {
+		/* VM_DONTEXPAND prevents merging this mapping into another VMA. */
+		vma = find_vma(mm, addr);
+		vma_start_write(vma);
+		if (current->personality & READ_IMPLIES_EXEC) {
+			vm_flags_clear(vma, VM_EXEC);
+			vma_set_page_prot(vma);
+		}
+		vma->vm_private_data = &bpf_user_page_marker;
+	}
+
+	mmap_write_unlock(mm);
+
+	if (!IS_ERR_VALUE(addr) && populate)
+		mm_populate(addr, populate);
+	return addr;
+}
+
+/**
+ * bpf_user_page_exec() - Make a BPF-allocated user page read/execute
+ * @addr: Address of the page to change
+ *
+ * Changes a writable page created by bpf_user_page_alloc() to read/execute.
+ * The page cannot be made writable again after this call.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+__bpf_kfunc int bpf_user_page_exec(unsigned long addr)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+	struct page *page;
+	int err = -EINVAL;
+
+	if (unlikely(in_interrupt() ||
+		     current->flags & (PF_KTHREAD | PF_EXITING)))
+		return -EPERM;
+	if (unlikely(!nmi_uaccess_okay()))
+		return -EPERM;
+	if (unlikely(!mm))
+		return -EFAULT;
+	if ((addr & ~PAGE_MASK) || addr > ULONG_MAX - PAGE_SIZE)
+		return -EINVAL;
+
+	if (mmap_write_lock_killable(mm))
+		return -EINTR;
+
+	vma = find_vma(mm, addr);
+	if (!vma || vma->vm_start > addr)
+		goto out;
+	/* Allow to change only vma allocated by bpf_user_page_alloc. */
+	if (vma->vm_private_data != &bpf_user_page_marker)
+		goto out;
+	if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_MAYEXEC))
+		goto out;
+
+	vma_start_write(vma);
+
+	err = get_user_pages(addr, 1, FOLL_WRITE, &page);
+	if (err != 1) {
+		if (!err)
+			err = -EFAULT;
+		goto out;
+	}
+
+	flush_icache_user_page(vma, page, addr, PAGE_SIZE);
+	err = do_mprotect_pkey_locked(addr, PAGE_SIZE, PROT_READ | PROT_EXEC, -1);
+	if (!err) {
+		vma = find_vma(mm, addr);
+		vm_flags_clear(vma, VM_MAYWRITE);
+	}
+	put_page(page);
+out:
+	mmap_write_unlock(mm);
+	return err;
+}
+
 /**
  * bpf_probe_write_user_faultable() - Copy data to userspace with page faults
  * enabled
@@ -5171,6 +5296,8 @@ static const struct btf_kfunc_id_set generic_kfunc_set = {
 };
 
 BTF_KFUNCS_START(user_mem_btf_ids)
+BTF_ID_FLAGS(func, bpf_user_page_alloc, KF_SLEEPABLE | KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_user_page_exec, KF_SLEEPABLE | KF_PERFMON)
 BTF_ID_FLAGS(func, bpf_probe_write_user_faultable, KF_SLEEPABLE | KF_PERFMON)
 BTF_KFUNCS_END(user_mem_btf_ids)
 
-- 
2.54.0


  parent reply	other threads:[~2026-10-21 19:58 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-21 19:57 [RFC bpf-next 0/9] bpf: Add support to allocate user page Jiri Olsa
2026-10-21 19:57 ` [RFC bpf-next 1/9] bpf: Add __srcptr kfunc argument suffix Jiri Olsa
2026-10-21 20:16   ` sashiko-bot
2026-10-21 19:57 ` [RFC bpf-next 2/9] bpf: Add uprobe faultable user copy kfunc Jiri Olsa
2026-10-21 20:22   ` sashiko-bot
2026-10-21 20:30   ` bot+bpf-ci
2026-10-21 19:57 ` [RFC bpf-next 3/9] mm: Expose locked mprotect operation Jiri Olsa
2026-10-21 20:30   ` bot+bpf-ci
2026-10-22 10:25   ` Lorenzo Stoakes (ARM)
2026-10-22 19:39     ` Jiri Olsa
2026-10-21 19:57 ` Jiri Olsa [this message]
2026-10-21 20:28   ` [RFC bpf-next 4/9] bpf: Add kfuncs to allocate and seal user pages sashiko-bot
2026-10-21 20:41   ` bot+bpf-ci
2026-10-26 12:51   ` kernel test robot
2026-10-26 15:27   ` kernel test robot
2026-10-21 19:57 ` [RFC bpf-next 5/9] selftests/bpf: Add __srcptr kfunc arguments test Jiri Olsa
2026-10-21 19:57 ` [RFC bpf-next 6/9] selftests/bpf: Add user page allocation test Jiri Olsa
2026-10-21 19:57 ` [RFC bpf-next 7/9] selftests/bpf: Add uprobe user page exec test Jiri Olsa
2026-10-21 19:57 ` [RFC bpf-next 8/9] selftests/bpf: Add faultable user page writes test Jiri Olsa
2026-10-21 19:57 ` [RFC bpf-next 9/9] selftests/bpf: Add user page failures test Jiri Olsa
2026-10-21 20:30   ` bot+bpf-ci
2026-10-21 21:53 ` [RFC bpf-next 0/9] bpf: Add support to allocate user page Alexei Starovoitov
2026-10-22 10:01   ` Jiri Olsa
2026-10-22 10:27   ` Lorenzo Stoakes (ARM)

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261021195759.119421-5-jolsa@kernel.org \
    --to=jolsa@kernel.org \
    --cc=akpm@linux-foundation.org \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=eddyz87@gmail.com \
    --cc=john.fastabend@gmail.com \
    --cc=liam@infradead.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=ljs@kernel.org \
    --cc=memxor@gmail.com \
    --cc=shuah@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.