From nobody Sat Sep 26 19:35:38 2026 Received: from mail-wr1-f47.google.com (mail-wr1-f47.google.com [209.85.221.47]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 29F073E00A0 for ; Mon, 31 Aug 2026 09:23:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788168226; cv=none; b=IDY2xilRLQukUNI3ljuhcs3UBdYWvfhCWBsiH5geHw7a/S7YoY+riQhdyrBHD3lqZwVKLnmUYAmoflsOXwy4CYc171WxO1Vu8okifrmSUAwhzQ8xq8p/2UmCtsNm9GKxxgtjr8V53YOa5fkbkCLFkkJBxQjQGVJxqHL3GO3tFQc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788168226; c=relaxed/simple; bh=xspFFxFjYAqvjceAxqadMuKixodXwHOZDcpMBk8R9S8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XXPsy5iL6YcR2v5Oi/+LlghbwLbmcwWbRIqTug7hs5V5v/5pXP8pbMbGKa3GSei+2J5DXGqRxOSWUVXDbbG61s4NlTaQML8ULQ9+YEz+mQplyVzcdP5GWvq8GuFA5Y0sc37ufCP9PkaSdmWth/yrBkzBS5H7/uENTijxy8+Feuk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=IDJgWm9/; arc=none smtp.client-ip=209.85.221.47 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="IDJgWm9/" Received: by mail-wr1-f47.google.com with SMTP id ffacd0b85a97d-48431648f33so1044605f8f.0 for ; Mon, 31 Aug 2026 02:23:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788168221; x=1788773021; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=Gy3RmBcuruQaR7WrIBsKEmLLWdsVsMuBKWJEzmGMhsc=; b=IDJgWm9/RTCQSYlJnx3AujjuI/uDDWAx0223c4yNs/c1oVD1SmQfWxykVZAcPFhzG3 9kVT03GvpHWIdiUp/gw9nI6BNFzN4KjxI7cb8pTOLyjEz8uVrCfrQGCwfWrgcdkqA1Cg 4g1bTnJn1vwHmKhU+Br9Xk/EoL2NdVCEjJQeDjKX4uVcCJHnHQFNigYLNijEwQz9nFTG pn7GCoUFxnvPj+RYQmK9FVD2j+LaCJBUGVbQk2jKFOOIIZD2XSomPnnfce6jZ6EZnwj8 AZadTKZc+7u2NJbdXC+VooaXPW0qxlyHUsULCDhXSZzzk8SnSglEr+QLGO6pb1eS3rgI d+Rg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788168221; x=1788773021; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=Gy3RmBcuruQaR7WrIBsKEmLLWdsVsMuBKWJEzmGMhsc=; b=FVKsr1Od4kMjsA302dHraMkOZptx2dh0s4VMUarWnuyNJ0EocGnGizT0rXdTXXgtbN MoHUZgot6sFo6fag0dUfhGfl1roIFaVb58AYiePdUO0BniKqJVnPKNxCrWO29fyJrnvo F+Ezzf+qu8WkUq1x5q+3SOzDtj1JNyoJ66O+1cJxyy97z2wr1+EW9KGYy7/qRY1xtPnW gZ1idjHsWHGPg06M5xJmvLxcbqDoio6kK2/HeQgr9VwWnqDlELMT2UyvwkYdZs2XmYxU RlTIZkqUIo4NokwD5kgpZiRZ8nYLHIY6KrUJBxEHLsGSiDcJHWGzVmtDTiULxNotb1yt l/fQ== X-Gm-Message-State: AFuF++lR4Vpc020z6jP0NOoxv23Xeeu0hVthL3U9F51muvq0Pfvt1tEC 1J8/9jXdbUkmJru5xz6d4hDztv60X7x7C1XS8jMmLLoDCatf01bzES4/ X-Gm-Gg: AR+sD11nEklPnllcMtG7AdVlll14jPaMONLHhE011zu0dbE1NZAfvlKkHqoWCgjf49a JD+tGjoeHMZF3O9RDurytz909XTEzpNCn5HqfliFo9hQKDOyPNAhrbAxz5qUBwJdNSMEtGisRTz jUIvWApM17iWBegZRKNCoFRWloHhC9Jn9a0NpQvegcvPhd7MrkbRwVESwQV/ofekv4ZyYfup8gR /DrLflHbcDAsSImRVPpoe9aQ+whMVDuaF6Zb/kJcMPKX03SYlNLw2/+/Yf2lRdpVL7zfKro9+A3 bnwcG6AsBf8pBYppwIBh3PWpwjZoJkysur2cXqHFnibHmrzrtd6Gz/MR8WwiSzU2d/sPPFmCeUd nksf1UqM4Nj7N/eb3xXQIjHlblp/hsVnViXBbDTQcNcSkPE5K4hx8L2fHLGf+y1BMXjC1bkv3yA TZd8wdSpyuTbz99SF9C78Fa2HseQ7SPG1xy8RHj866W+H5wtQT3h94N0NN4Y90rTh6eIVDawyMy g== X-Received: by 2002:a05:600c:4f49:b0:495:5d6d:9cc1 with SMTP id 5b1f17b1804b1-49cd52b2c93mr119072045e9.0.1788168221065; Mon, 31 Aug 2026 02:23:41 -0700 (PDT) Received: from dell-desktop ([2a02:587:4b5f:900:ef25:2f4:1792:4e6f]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49b9500c80asm318248155e9.9.2026.08.31.02.23.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 02:23:40 -0700 (PDT) From: Anastasios Papagiannis To: bpf@vger.kernel.org Cc: linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-fsdevel@vger.kernel.org, viro@zeniv.linux.org.uk, brauner@kernel.org, akpm@linux-foundation.org, david@kernel.org, ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, memxor@gmail.com, kpsingh@kernel.org, matt@bobrowski.net, song@kernel.org, utilityemal77@gmail.com, tasos.papagiannnis@gmail.com Subject: [PATCH bpf-next v3 1/7] mm: Add copy_remote_mm_str() Date: Mon, 31 Aug 2026 12:22:59 +0300 Message-ID: <20260831092305.42062-2-tasos.papagiannnis@gmail.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260831092305.42062-1-tasos.papagiannnis@gmail.com> References: <20260831092305.42062-1-tasos.papagiannnis@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" copy_remote_vm_str() gets the target address space from a struct task_struct. This does not work for an address space that exists but is not yet associated with a task_struct, such as the mm held by struct linux_binprm during exec. Add copy_remote_mm_str(), which operates directly on a struct mm_struct. Signed-off-by: Anastasios Papagiannis --- include/linux/mm.h | 2 ++ mm/memory.c | 33 +++++++++++++++++++++++++++++---- mm/nommu.c | 33 +++++++++++++++++++++++++++++---- 3 files changed, 60 insertions(+), 8 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index 485df9c2dbdd..eede435bf4a3 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -3222,6 +3222,8 @@ extern int access_remote_vm(struct mm_struct *mm, uns= igned long addr, void *buf, int len, unsigned int gup_flags); =20 #ifdef CONFIG_BPF_SYSCALL +extern int copy_remote_mm_str(struct mm_struct *mm, unsigned long addr, + void *buf, int len, unsigned int gup_flags); extern int copy_remote_vm_str(struct task_struct *tsk, unsigned long addr, void *buf, int len, unsigned int gup_flags); #endif diff --git a/mm/memory.c b/mm/memory.c index 6b8280cfc1db..4c5f0b629889 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -7141,12 +7141,15 @@ EXPORT_SYMBOL_GPL(access_process_vm); * Copy a string from another process's address space as given in mm. * If there is any error return -EFAULT. */ -static int __copy_remote_vm_str(struct mm_struct *mm, unsigned long addr, +static int __copy_remote_mm_str(struct mm_struct *mm, unsigned long addr, void *buf, int len, unsigned int gup_flags) { void *old_buf =3D buf; int err =3D 0; =20 + if (unlikely(len =3D=3D 0)) + return 0; + *(char *)buf =3D '\0'; =20 if (mmap_read_lock_killable(mm)) @@ -7218,6 +7221,27 @@ static int __copy_remote_vm_str(struct mm_struct *mm= , unsigned long addr, return buf - old_buf; } =20 +/** + * copy_remote_mm_str - copy a string from a remote address space. + * @mm: the remote address space + * @addr: start address to read from + * @buf: destination buffer + * @len: number of bytes to copy + * @gup_flags: flags modifying lookup behaviour + * + * The caller must hold a reference on @mm. + * + * Return: number of bytes copied from @addr (source) to @buf (destination= ), + * not including the trailing NUL. If @len is zero, return 0 without acces= sing + * @buf. Otherwise, @buf is always NUL-terminated. On any error, return + * -EFAULT. + */ +int copy_remote_mm_str(struct mm_struct *mm, unsigned long addr, + void *buf, int len, unsigned int gup_flags) +{ + return __copy_remote_mm_str(mm, addr, buf, len, gup_flags); +} + /** * copy_remote_vm_str - copy a string from another process's address space. * @tsk: the task of the target address space @@ -7229,8 +7253,9 @@ static int __copy_remote_vm_str(struct mm_struct *mm,= unsigned long addr, * The caller must hold a reference on @mm. * * Return: number of bytes copied from @addr (source) to @buf (destination= ); - * not including the trailing NUL. Always guaranteed to leave NUL-terminat= ed - * buffer. On any error, return -EFAULT. + * not including the trailing NUL. If @len is zero, return 0 without acces= sing + * @buf. Otherwise, @buf is always NUL-terminated. On any error, return + * -EFAULT. */ int copy_remote_vm_str(struct task_struct *tsk, unsigned long addr, void *buf, int len, unsigned int gup_flags) @@ -7247,7 +7272,7 @@ int copy_remote_vm_str(struct task_struct *tsk, unsig= ned long addr, return -EFAULT; } =20 - ret =3D __copy_remote_vm_str(mm, addr, buf, len, gup_flags); + ret =3D copy_remote_mm_str(mm, addr, buf, len, gup_flags); =20 mmput(mm); =20 diff --git a/mm/nommu.c b/mm/nommu.c index ed3934bc2de4..94e3709e95fd 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -1716,13 +1716,16 @@ EXPORT_SYMBOL_GPL(access_process_vm); * Copy a string from another process's address space as given in mm. * If there is any error return -EFAULT. */ -static int __copy_remote_vm_str(struct mm_struct *mm, unsigned long addr, +static int __copy_remote_mm_str(struct mm_struct *mm, unsigned long addr, void *buf, int len) { unsigned long addr_end; struct vm_area_struct *vma; int ret =3D -EFAULT; =20 + if (unlikely(len =3D=3D 0)) + return 0; + *(char *)buf =3D '\0'; =20 if (mmap_read_lock_killable(mm)) @@ -1752,6 +1755,27 @@ static int __copy_remote_vm_str(struct mm_struct *mm= , unsigned long addr, return ret; } =20 +/** + * copy_remote_mm_str - copy a string from a remote address space. + * @mm: the remote address space + * @addr: start address to read from + * @buf: destination buffer + * @len: number of bytes to copy + * @gup_flags: flags modifying lookup behaviour (unused) + * + * The caller must hold a reference on @mm. + * + * Return: number of bytes copied from @addr (source) to @buf (destination= ), + * not including the trailing NUL. If @len is zero, return 0 without acces= sing + * @buf. Otherwise, @buf is always NUL-terminated. On any error, return + * -EFAULT. + */ +int copy_remote_mm_str(struct mm_struct *mm, unsigned long addr, + void *buf, int len, unsigned int gup_flags) +{ + return __copy_remote_mm_str(mm, addr, buf, len); +} + /** * copy_remote_vm_str - copy a string from another process's address space. * @tsk: the task of the target address space @@ -1763,8 +1787,9 @@ static int __copy_remote_vm_str(struct mm_struct *mm,= unsigned long addr, * The caller must hold a reference on @mm. * * Return: number of bytes copied from @addr (source) to @buf (destination= ); - * not including the trailing NUL. Always guaranteed to leave NUL-terminat= ed - * buffer. On any error, return -EFAULT. + * not including the trailing NUL. If @len is zero, return 0 without acces= sing + * @buf. Otherwise, @buf is always NUL-terminated. On any error, return + * -EFAULT. */ int copy_remote_vm_str(struct task_struct *tsk, unsigned long addr, void *buf, int len, unsigned int gup_flags) @@ -1781,7 +1806,7 @@ int copy_remote_vm_str(struct task_struct *tsk, unsig= ned long addr, return -EFAULT; } =20 - ret =3D __copy_remote_vm_str(mm, addr, buf, len); + ret =3D copy_remote_mm_str(mm, addr, buf, len, gup_flags); =20 mmput(mm); =20 --=20 2.55.0 From nobody Sat Sep 26 19:35:38 2026 Received: from mail-wr1-f47.google.com (mail-wr1-f47.google.com [209.85.221.47]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CAE0B3E0C4D for ; Mon, 31 Aug 2026 09:23:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788168229; cv=none; b=jNSS0cvLql63BGycxZmV1xEtHaJ6KnP+eal0CtYglA0bM/6rrTl5JSVq+xilLzXZmN7F8arsP+ewgzvurV66A4inHg8WP7KiUldoL906DIrhENAoHrBAlQX9lOdEWLH8Yi5cvg6LMsvoD3AQ2PySHXfUR9bDzPnEHdhedIqW+0g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788168229; c=relaxed/simple; bh=13iQWQ5HVzeaIU5YKMesEX4qv3/IZAsHfcBKEHM64kU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=m9b5lskAhduEk0pvBC2cz/Kmm4MLWApPgHCjJe2YP5UPeftW/8HKP1rceKpGgrQ24Ovd2ez2NgtfjpgoJsDpm2Q05As8SFl2zkMgWgaHt3Dm7YpKQZhEt5ZrPiABveove1UbLyPc4GaGx3A06Dlb5ZG42zWWUHx2Iv4hrPksZbo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=mpKVo+cl; arc=none smtp.client-ip=209.85.221.47 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="mpKVo+cl" Received: by mail-wr1-f47.google.com with SMTP id ffacd0b85a97d-47fd4531020so1967095f8f.3 for ; Mon, 31 Aug 2026 02:23:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788168224; x=1788773024; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=xLhekP1hyFFXIKTW9u0l4JwtljKTcvPghf3/4G/CKiA=; b=mpKVo+clJlm27fzllIEo9zyytJ6gQ6cMAv6T8ddJ2YX3XBOds4sDX0cCr9cIImOh/p XF2wCKzox0Igoal6dw0l1XaJF/LqY9IK67hLD/Uya/vuYjm/HgMes7x0vx1t3pD8PNBg sl3TkffVmxQdpkvo37cW80e4rAfmCOhrXsKleo00jHEdrwQyE9MtQ+EwUYuyAHS1aTQF C6Yb61H4cZRbePy0J0BFCr7VcXmrsaymPKpl1LfoG3wRISyPcfaCuqGwcyEjsJYw1wxw 88FrnY1q0gCtpZg022gdpalX9Zy1A3gjRsyfM5UfrlP3im73WdvsfuhFCgfwQ0PLC8QT Mmfw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788168224; x=1788773024; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=xLhekP1hyFFXIKTW9u0l4JwtljKTcvPghf3/4G/CKiA=; b=X9amzf/gJnuRqMw/hpxMfeCDe3vNPXxxUWm79wyDDPLQ50mOmxI7sHoawOeNi2wbEv w1H8oiBrtioXRUZSFKFyQSnOtpBmDcyUg6lcdeKJV7AMjwlBskQglvIV85RkExGGqGg6 ygKX6AYwceMpXnqSENOH6i2hgYOCO//7cphFKYyEkHexg7DgTXbQOuV0Nus6Wd+zrheW 91UxBjUKD00e1Ltut9pk/Ugt6d2c/4WoGzwfU+SbG25NR/sFq4iYEjKyazpQM16AYt4q cZNUhNoJZLE5VDfoM5NcTulE0MpdGCgP2UfUXZXzPkg2cIaarOkiot8CvHhuB0ht2q0u CQNQ== X-Gm-Message-State: AFuF++lJQ1nIwum9J0H09Jf8a6Zq1okXFZKdMiPH8a6RJznuV7IJQURI Vq6Qs3WPC8V6ukmWVJT5/15EUopw4M2To+TT+hlz6T/+5zQuNBOQhogL X-Gm-Gg: AR+sD12bZPHN7HHf6zTx1eUApqfGwwzIxndA59RZn1UDRDYpZ5EQOJzcLHe3pJE/a1z 5M+68C2GCLdxB7u1zGda5FY9yuNaQ9cYSPZhHWtAiVxuCSr1EjNyi6yFYh0BuNYCwPSyr+6RAOP 6PLX1wO8WR5D8vRWmAB7naeGnBh79LcZWZHam5+6KLvJ07BjjHJ4PrYPTuFTaTDk/Tv1SfwKGyd JGQOcZ6TwASJ2Afs6iX7O8xV5oE7aCFvboCSIppJy1JvtsXi4yZ0aSpwI53sZgZMQwy8IDfIYyQ +sUY1DWJha416KgYU7VslCNsGw9qQxky6W1z4h2Hqm+bumJRQSpm4ZtfVTJ/TOXOGKGAij+FXeB HV9fdAeuMVRRmzZM0QZJ5MZJAo6AF2irI6EE66Jz8iwdKDg65xR6WmOcXaYNTQ6NcZwRHFEm/EM Ix1fVCZJOOvtQ4Z+AgqS0yWxooBIhXnfjeOHlVIXiMb1gyG7sp2hOWs3sHfVVEeIMJkMLUDceDn Q== X-Received: by 2002:a05:600c:3e1a:b0:49c:cee2:a508 with SMTP id 5b1f17b1804b1-49ccee2a541mr225871815e9.16.1788168224225; Mon, 31 Aug 2026 02:23:44 -0700 (PDT) Received: from dell-desktop ([2a02:587:4b5f:900:ef25:2f4:1792:4e6f]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49b9500c80asm318248155e9.9.2026.08.31.02.23.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 02:23:43 -0700 (PDT) From: Anastasios Papagiannis To: bpf@vger.kernel.org Cc: linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-fsdevel@vger.kernel.org, viro@zeniv.linux.org.uk, brauner@kernel.org, akpm@linux-foundation.org, david@kernel.org, ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, memxor@gmail.com, kpsingh@kernel.org, matt@bobrowski.net, song@kernel.org, utilityemal77@gmail.com, tasos.papagiannnis@gmail.com Subject: [PATCH bpf-next v3 2/7] bpf: Add user memory access kfuncs for mm_struct Date: Mon, 31 Aug 2026 12:23:00 +0300 Message-ID: <20260831092305.42062-3-tasos.papagiannnis@gmail.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260831092305.42062-1-tasos.papagiannnis@gmail.com> References: <20260831092305.42062-1-tasos.papagiannnis@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" On CONFIG_MMU kernels, when security_bprm_check() runs, the argument and environment strings for the exec have been copied into bprm->mm. The new address space is not associated with a task_struct until exec_mmap(), so existing BPF user memory helpers can only read from the calling task's old address space. Add bpf_copy_from_user_mm() and bpf_copy_from_user_mm_str() kfuncs. Both take a struct mm_struct pointer directly, allowing callers to access trusted address spaces that are not associated with a task_struct. bpf_copy_from_user_mm() has similar semantics to bpf_copy_from_user_task(). bpf_copy_from_user_mm_str() copies one NUL-terminated string and returns its size including the NUL terminator. It accepts BPF_F_PAD_ZEROS to clear unused destination bytes on success. Register both kfuncs and mark them KF_SLEEPABLE because accessing the remote address space can fault. On !CONFIG_MMU, exec argument and environment strings remain in bprm->page[] until the binary loader transfers them to the new process stack. They are therefore not accessible through bprm->mm at the bprm_check_security hook. The linux_binprm use described above is CONFIG_MMU-only, although the new kfuncs remain available for other address spaces on !CONFIG_MMU. Signed-off-by: Anastasios Papagiannis --- kernel/bpf/helpers.c | 115 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 101 insertions(+), 14 deletions(-) diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index b3cc5c8fc875..19c01aa734ae 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -32,6 +32,13 @@ =20 #include "../../lib/kstrtox.h" =20 +int bpf_copy_from_user_mm(void *dst, u32 dst__sz, + const void __user *unsafe_ptr__ign, + struct mm_struct *mm, u64 flags); +int bpf_copy_from_user_mm_str(void *dst, u32 dst__sz, + const void __user *unsafe_ptr__ign, + struct mm_struct *mm, u64 flags); + /* If kernel subsystem is allowing eBPF programs to call this function, * inside its own verifier_ops->get_func_proto() callback it should return * bpf_map_lookup_elem_proto, so that verifier can properly check the argu= ments @@ -682,22 +689,15 @@ const struct bpf_func_proto bpf_copy_from_user_proto = =3D { BPF_CALL_5(bpf_copy_from_user_task, void *, dst, u32, size, const void __user *, user_ptr, struct task_struct *, tsk, u64, flags) { + struct mm_struct *mm; int ret; =20 - /* flags is not used yet */ - if (unlikely(flags)) - return -EINVAL; - - if (unlikely(!size)) - return 0; - - ret =3D access_process_vm(tsk, (unsigned long)user_ptr, dst, size, 0); - if (ret =3D=3D size) - return 0; + mm =3D get_task_mm(tsk); + ret =3D bpf_copy_from_user_mm(dst, size, user_ptr, mm, flags); + if (mm) + mmput(mm); =20 - memset(dst, 0, size); - /* Return -EFAULT for partial read */ - return ret < 0 ? ret : -EFAULT; + return ret; } =20 const struct bpf_func_proto bpf_copy_from_user_task_proto =3D { @@ -3680,6 +3680,83 @@ __bpf_kfunc int bpf_copy_from_user_str(void *dst, u3= 2 dst__sz, const void __user __bpf_kfunc int bpf_copy_from_user_task_str(void *dst, u32 dst__sz, const void __user *unsafe_ptr__ign, struct task_struct *tsk, u64 flags) +{ + struct mm_struct *mm; + int ret; + + mm =3D get_task_mm(tsk); + ret =3D bpf_copy_from_user_mm_str(dst, dst__sz, unsafe_ptr__ign, + mm, flags); + if (mm) + mmput(mm); + + return ret; +} + +/** + * bpf_copy_from_user_mm() - Copy data from an address space + * @dst: Destination address, in kernel space + * @dst__sz: Number of bytes to copy + * @unsafe_ptr__ign: Source address in the address space + * @mm: Address space to copy from + * @flags: Reserved for future use; must be zero + * + * Copies data from the user address space associated with @mm. The destin= ation + * is zeroed if an attempted copy cannot be completed in full. Unsupported + * flags return -EINVAL without modifying @dst. + * + * Return: 0 on success, -EINVAL if @flags is non-zero, or -EFAULT if the = copy + * fails or is partial. + */ +__bpf_kfunc int bpf_copy_from_user_mm(void *dst, u32 dst__sz, + const void __user *unsafe_ptr__ign, + struct mm_struct *mm, u64 flags) +{ + int ret; + + if (unlikely(flags)) + return -EINVAL; + + if (unlikely(!dst__sz)) + return 0; + + if (unlikely(!mm)) { + memset(dst, 0, dst__sz); + return -EFAULT; + } + + ret =3D access_remote_vm(mm, (unsigned long)unsafe_ptr__ign, + dst, dst__sz, 0); + if (ret =3D=3D dst__sz) + return 0; + + memset(dst, 0, dst__sz); + return ret < 0 ? ret : -EFAULT; +} + +/** + * bpf_copy_from_user_mm_str() - Copy a string from an address space + * @dst: Destination address, in kernel space. This buffer mus= t be + * at least @dst__sz bytes long + * @dst__sz: Maximum number of bytes to copy, including the traili= ng NUL + * @unsafe_ptr__ign: Source address in the address space + * @mm: Address space to copy from + * @flags: The only supported flag is BPF_F_PAD_ZEROS + * + * Copies a NUL-terminated string from the user address space associated w= ith + * @mm. If the string is too long, @dst is still NUL-terminated unless @ds= t__sz + * is zero. + * + * If the flags are valid and BPF_F_PAD_ZEROS is set, the unused portion of + * @dst is cleared on success and all of @dst is cleared on a copy failure. + * Unsupported flags return -EINVAL without modifying @dst. + * + * Return: The number of copied bytes including the NUL terminator on succ= ess, + * or a negative error code on failure. + */ +__bpf_kfunc int bpf_copy_from_user_mm_str(void *dst, u32 dst__sz, + const void __user *unsafe_ptr__ign, + struct mm_struct *mm, u64 flags) { int ret; =20 @@ -3689,7 +3766,15 @@ __bpf_kfunc int bpf_copy_from_user_task_str(void *ds= t, u32 dst__sz, if (unlikely(dst__sz =3D=3D 0)) return 0; =20 - ret =3D copy_remote_vm_str(tsk, (unsigned long)unsafe_ptr__ign, dst, dst_= _sz, 0); + if (unlikely(!mm)) { + if (flags & BPF_F_PAD_ZEROS) + memset(dst, 0, dst__sz); + else + *(char *)dst =3D '\0'; + return -EFAULT; + } + + ret =3D copy_remote_mm_str(mm, (unsigned long)unsafe_ptr__ign, dst, dst__= sz, 0); if (ret < 0) { if (flags & BPF_F_PAD_ZEROS) memset(dst, 0, dst__sz); @@ -4924,6 +5009,8 @@ BTF_ID_FLAGS(func, bpf_iter_bits_new, KF_ITER_NEW) BTF_ID_FLAGS(func, bpf_iter_bits_next, KF_ITER_NEXT | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_iter_bits_destroy, KF_ITER_DESTROY) BTF_ID_FLAGS(func, bpf_copy_from_user_str, KF_SLEEPABLE) +BTF_ID_FLAGS(func, bpf_copy_from_user_mm, KF_SLEEPABLE) +BTF_ID_FLAGS(func, bpf_copy_from_user_mm_str, KF_SLEEPABLE) BTF_ID_FLAGS(func, bpf_copy_from_user_task_str, KF_SLEEPABLE) BTF_ID_FLAGS(func, bpf_get_kmem_cache) BTF_ID_FLAGS(func, bpf_iter_kmem_cache_new, KF_ITER_NEW | KF_SLEEPABLE) --=20 2.55.0 From nobody Sat Sep 26 19:35:38 2026 Received: from mail-wm1-f54.google.com (mail-wm1-f54.google.com [209.85.128.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A422E3E8C4C for ; Mon, 31 Aug 2026 09:23:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788168232; cv=none; b=GUyG9LjWe5VLAkt6v99jlm+3MfOUk6I+xbihspk02Ieb1/DUNNfGruRdVjPOb7YT5x7xnDaba/0VsGsJAHEi9m256ZnTHeN7N3doHIlD2MqFrW4nz8AlMk4IuXXXRnDHC5WQNM8Qj8ojEfgnvO0noJt026n9E9BapolHmTn5pXc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788168232; c=relaxed/simple; bh=fdI9y3sylK76iM9oY9DbpTJU8s7hpw7GE1UUBVIND9c=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Oy1mG5idNiuV/Z8KyebSg6yT5u/cV661YZ5sLyvFZL62Pt507xsW+icRPjxBpk79MqXeDo9zNYSCWoxnB/hj7D+XrphdCvqoa5NLHbx+/2EI8+GvneiipGzGubAy04gCc2TjYT6PEQPj3K3UEDsvOjj73icTPs+5oGu4VEsbHw8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=H/oeyLUJ; arc=none smtp.client-ip=209.85.128.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="H/oeyLUJ" Received: by mail-wm1-f54.google.com with SMTP id 5b1f17b1804b1-49ccfae359fso10639275e9.3 for ; Mon, 31 Aug 2026 02:23:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788168227; x=1788773027; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=zON5T+icRRTPJe/tuLdlxvvWhTEjuOvsxI3luAAgobA=; b=H/oeyLUJpdq9rl4eElOKD4dYR+SUsuEaRqbpQGC3OUERqIuAFjsVE3Y9bUN5c6nwvm Ne7+F2nJu9bYIODsWN+wf51R7x9rG2lCIw837k4WLnYBuZdWf43IsuW73KTdgZDnnIHq dw3I+vcB7jwMAlfFU7thDucKRTFkJMXaHnBbU4ZLQsYHlQnwVfDi3EZELVaFz65VG+aA m8mj/DDeuImB8zq2NEMyhWgjS824ddWL96dXuteEKaMIVnENnPuAmMAA1IdzVLGJjsTR itS5hQkooW4mTZrPkKDxvrTRRgFeD8xp2WncTuUxELDilzqxwKhnaAv5Z+0joyKz9tGs aVew== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788168227; x=1788773027; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=zON5T+icRRTPJe/tuLdlxvvWhTEjuOvsxI3luAAgobA=; b=J4h6WmPSOz4m8xmEHWeXvAC8VUNVGhbjkZS8uTBlc4t+xFoCu+4ZX2T2zWFlocsLgh G3dw+6Hvvkr0nB97VnTGBsFy3QMa9MRZ9nyg/MppLwkcNJIXBOTPF/LMYRofevYSDSIS UA2V5hc1j3FUgAPM+Cg8+cdXuKP8bJ6N1UMZtrTlbsoDlthiYIj6hP4CNjkyUnhrTURs LHk++ufBumGY91jCAZNXq5OXUStJ7RafyGGWIeSioLOoqQDHbEY7uMIxKmtt9p3bJDM8 Fk2XqE6qnYp/rp5hZiAsIclfXafi5atPHnpOWDhSz+UItpXiDOFhzzNPO6FvZFHTXXmj yoIw== X-Gm-Message-State: AFuF++mp0XaeErER5hj7a8fqUu8aKdwJvKNZgtJ9cITjAzYv1bBLGbfA 3eRlgvHXWCIZ5Z89FeA6DlRq+pWu3APi0xzKXv2+2ULGmovrWbwFTkws X-Gm-Gg: AR+sD12KmmXtlXHPB93/BV/XrXsNxIw6K4hbIhMiTRfzYPPmNAKkeBJP+SnwhBwuAwi bKcpiiklHHOPAocq25vsEltCjjFhFjwmCU5THSQ9fveT6jpXHnkbOsO/C2BL6SVsVhzvvRReqRw Od/g53979AGvABXnZmeOOrYiWwOJe6e3U3olTMO4i/nlJmBBbkmSuyTzOEK9mZnyss95NDvJC4k l98y+wuLJj+V1akx85Zwz6Had6HjpCc/lsJiCmyrljUogaiS58tgfnfOrgytlHXJe3pcGfI+cMo RDieKFVybqlH5CrducyJ8TvcsClGkyQZo6ilPndo5T7btU73XUjnsBHUZaFmTJccI21IRIJWcbE pazuOLN7PK+QOHaCIx+1noKjfbYdxd5homQjEQrtQ6GQlZYpZwhdvabByDFP06EOxWDzR8fYpBj fHVi4cIo+9KR7QS/9dcu6mS+9Y9NZo+3DK2CpqGHTZ1bxZynCHEwtOch4uO2m9f8Z5XJ6O8lNLZ g== X-Received: by 2002:a05:600c:b85:b0:49c:c96a:d36b with SMTP id 5b1f17b1804b1-49cc96ad411mr286995945e9.12.1788168227398; Mon, 31 Aug 2026 02:23:47 -0700 (PDT) Received: from dell-desktop ([2a02:587:4b5f:900:ef25:2f4:1792:4e6f]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49b9500c80asm318248155e9.9.2026.08.31.02.23.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 02:23:46 -0700 (PDT) From: Anastasios Papagiannis To: bpf@vger.kernel.org Cc: linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-fsdevel@vger.kernel.org, viro@zeniv.linux.org.uk, brauner@kernel.org, akpm@linux-foundation.org, david@kernel.org, ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, memxor@gmail.com, kpsingh@kernel.org, matt@bobrowski.net, song@kernel.org, utilityemal77@gmail.com, tasos.papagiannnis@gmail.com Subject: [PATCH bpf-next v3 3/7] exec: Clear bprm->mm before dropping its reference Date: Mon, 31 Aug 2026 12:23:01 +0300 Message-ID: <20260831092305.42062-4-tasos.papagiannnis@gmail.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260831092305.42062-1-tasos.papagiannnis@gmail.com> References: <20260831092305.42062-1-tasos.papagiannnis@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Once mmput() drops the final reference to bprm->mm, the pointer must no longer remain accessible through struct linux_binprm. The successful exec path and the bprm initialization error path already clear bprm->mm when ownership is transferred or released. Do the same in free_bprm() before calling mmput(). This is required for eBPF kfuncs where bprm->mm is either NULL or points to a live mm_struct to ensure safe access. Signed-off-by: Anastasios Papagiannis Reviewed-by: Sun Jian --- fs/exec.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/exec.c b/fs/exec.c index a14f28b15607..4b4d8f0627a7 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1453,9 +1453,12 @@ void bprm_drop_loader(struct linux_binprm *bprm) =20 static void free_bprm(struct linux_binprm *bprm) { - if (bprm->mm) { + struct mm_struct *mm =3D bprm->mm; + + if (mm) { acct_arg_size(bprm, 0); - mmput(bprm->mm); + bprm->mm =3D NULL; + mmput(mm); } if (bprm->user_ns) put_user_ns(bprm->user_ns); --=20 2.55.0 From nobody Sat Sep 26 19:35:38 2026 Received: from mail-wm1-f51.google.com (mail-wm1-f51.google.com [209.85.128.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F30263EB7F4 for ; Mon, 31 Aug 2026 09:23:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788168236; cv=none; b=dudavzrpIokbH0xiA4B4jHZXX85VqJoh0NiNN3NkQdOnW8Lq/LdPm8grWawSLSZs5iFqUaodxeDqPZGhc4LONjlUketkVVtU8cTi3yu69Z1TlT+I25WEWWchHYjnKcOx8oJ/qCaYAVlc4PCv4GyT+odn2qZcKeQEZvirQagCZVk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788168236; c=relaxed/simple; bh=bxCCFdUECttyJbXSGfdduUo/DEz+VpTX8wwbkV1YIHo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=JNggFQxmZHX+4xBllLAkBvTqfCtILFBvqbxwBpF/izahklJRUsqvnqrMoxAVNI3Dh5yQ6Nb/wIYAS0ETNv1emvmsogGIPmBaT2VG4X9vy/YWr5Ck6lRhT0uwvsFFzF8C9NbwyvpmbcmAw2f/jZS0hDYYTNqMSdauJ34Dwys13MQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=NHGJO3jA; arc=none smtp.client-ip=209.85.128.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="NHGJO3jA" Received: by mail-wm1-f51.google.com with SMTP id 5b1f17b1804b1-499ac87c92bso29647575e9.1 for ; Mon, 31 Aug 2026 02:23:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788168230; x=1788773030; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=agssTkVjz/VQ+HpJevjjY5hRGfaiF2JHVaDNy5Qphm8=; b=NHGJO3jAzqsuNX0fcpHWCZQRJSPj7bOoNNyTtqReDHDjbsrKBaH7A3WhZZYkmm/O75 ozO3WmVaULMLM9ymdK1vDytunkpSUgmhKbNzvInMtGB0D9xXdspyVXG0bKSigZ3EQgwN PmDbeLsJI9u6QU2NUbrpLuQMMVWPUNSRBTMw9Dx5uU74HTMYDqQEBeBLbBT1QncWSpEC tLWlKa3kiyDJe3Ql/CAFPPMIp1Qii0LVvvjJDIlYart8XGjzziqdhoLq0DEGVyP7zdKr LmR4mWSrf67xctx2j3sxfkCIapkeLrbrU+1WwTDcDIbmMHPE1d2tUWjN6mKJS1AIyon7 CAHg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788168230; x=1788773030; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=agssTkVjz/VQ+HpJevjjY5hRGfaiF2JHVaDNy5Qphm8=; b=N3Ntw5j0jaBLFI9plOrimcop1rgr817rYuPdYWYt47KDzTsI7AjjjAUuIMww751W2X Ye3u4Jw2KoQIsgVIinBMSBV6Y3tOz/AbZNHyL5Sv1rqKS3lHpnA7qRcw33phPN6VrY5f YAOJfuLKWquS27TqqFoGV5ha0+Sumtyulm9ymf1uDKUqAUIFIIlu8rzJtTLOs1ubr9VL aeXqUR743Tc6sNBJb9HdtIdQRxhxNRzvQ6tT/7UpsGsRRbrftbDoN9Yf4JVYnoKuy6bm V85qUSIMav6AGjMkTdaVLeiLGycQPjDXKWMglLB3vTYBbyNh+YsPMWBT9uvwMgP25MTQ 0TMw== X-Gm-Message-State: AFuF++ktZKHm1B4pFaO0CP9+UA6oRcBoVupF038qNf1mDOMMDCb5LBB1 SsJKI5PJWzk2DUg1cgbQScbK+QqAzjwcbG1By7iDcGdSNeXZBj3w8iyK X-Gm-Gg: AR+sD11B/2UpwjACCn3zlCOZ6+DIEIjqWvNVCI1+7/UGLna8yN6l2sImFzx4AXEfJhT FMvVHsbLbGzpVu0UaPQuClpt65uYEOdH6rTQjqpqnHRjKIoNxNFUb1Yx035OsGqScBmfctTUu89 0/kpQ6oR7Tml0n3FgXIm1SRC/+bUGkcTQoQrKqkFKDMdLT5k3iba7y6QQp/OVPFXGZS/onHQyJX afQAtMySu8A6Hu8fwAlu4GqHH4nUOKAZs+2IumDgkijPB+ZqWrxHdvknW8UEqfUjSjrMCpv1pQC /1o9RmK4NAXjuRH0oxcC9IgVIYBl16p2sze4F6qERs2tVC9figHiMWYiu02jt1E6kf9VN9LNIZA s0hA1MK8V1RgAkYddjSLhfvgUya+e4eLskrIH7PcdGS07VjW6f13kmoQ5V+kges2kteUoDGoIRB GvaLX8aMsntqVjCcnhB5YoBjk4FjNw7M+cb+rBadwQz76jRsWA8CPKJI/p7aEqwMkxEoTeN9OFe A== X-Received: by 2002:a05:600c:4585:b0:499:b402:6c0 with SMTP id 5b1f17b1804b1-49b91c265d7mr347797155e9.3.1788168230352; Mon, 31 Aug 2026 02:23:50 -0700 (PDT) Received: from dell-desktop ([2a02:587:4b5f:900:ef25:2f4:1792:4e6f]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49b9500c80asm318248155e9.9.2026.08.31.02.23.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 02:23:49 -0700 (PDT) From: Anastasios Papagiannis To: bpf@vger.kernel.org Cc: linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-fsdevel@vger.kernel.org, viro@zeniv.linux.org.uk, brauner@kernel.org, akpm@linux-foundation.org, david@kernel.org, ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, memxor@gmail.com, kpsingh@kernel.org, matt@bobrowski.net, song@kernel.org, utilityemal77@gmail.com, tasos.papagiannnis@gmail.com Subject: [PATCH bpf-next v3 4/7] bpf: Allow reads through trusted-or-null BTF pointers Date: Mon, 31 Aug 2026 12:23:02 +0300 Message-ID: <20260831092305.42062-5-tasos.papagiannnis@gmail.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260831092305.42062-1-tasos.papagiannnis@gmail.com> References: <20260831092305.42062-1-tasos.papagiannnis@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Currently, a trusted-or-null pointer (i.e. PTR_TO_BTF_ID|PTR_TRUSTED|PTR_MAYBE_NULL) has to be checked for NULL before it can be dereferenced. Marking a field from PTR_TO_BTF_ID typing to trusted-or-null can reject programs that previously dereferenced the pointer directly. This is useful as we need to mark new fields as trusted in order to pass those as arguments to kfuncs. This patch adds support to dereference pointers marked as PTR_TO_BTF_ID|PTR_TRUSTED|PTR_MAYBE_NULL without the need for a NULL check. We define that those pointers can fault on dereference, so reads need to happen through BPF_PROBE_MEM. If the dereferenced pointer is another BTF pointer, we clear trusted flags and mark the result as PTR_UNTRUSTED. This applies only to reads. Other cases still require an explicit NULL check. After such a check, the pointer retains PTR_TRUSTED and can be used normally. To summarise, before that patch, reads to PTR_TO_BTF_ID|PTR_UNTRUSTED are allowed through BPF_PROBE_MEM and reads to PTR_TO_BTF_ID|PTR_TRUSTED are normal loads. This patch allows PTR_TO_BTF_ID|PTR_TRUSTED|PTR_MAYBE_NULL to happen through BPF_PROBE_MEM. The unchecked read path has two consequences: 1. It uses BPF_PROBE_MEM, which is slower than a normal load. An explicit NULL check refines the pointer to PTR_TRUSTED and allows a normal load. 2. A faulting read returns zero, which is indistinguishable from a legitimately zero-valued field. Programs that need to distinguish those cases must check the pointer before reading the field. The next patch updates current tests and also introduces more checks to ensure this change does not break anything. Signed-off-by: Anastasios Papagiannis --- include/linux/bpf_verifier.h | 9 ++++++++- kernel/bpf/verifier.c | 12 +++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index ae9f606539f4..d3d7254cc1f6 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -1326,6 +1326,11 @@ static inline bool bpf_is_ptr_to_mem_or_btf_id(enum = bpf_reg_type type) } } =20 +static inline bool bpf_is_trusted_or_null_btf_ptr(enum bpf_reg_type type) +{ + return type =3D=3D (PTR_TO_BTF_ID | PTR_TRUSTED | PTR_MAYBE_NULL); +} + static inline bool bpf_may_fault_on_deref(enum bpf_reg_type type) { /* @@ -1333,7 +1338,9 @@ static inline bool bpf_may_fault_on_deref(enum bpf_re= g_type type) * protection, that is, the ones bpf_convert_ctx_accesses() has to * turn a BPF_LDX into a BPF_PROBE_MEM one for. */ - return type =3D=3D PTR_TO_BTF_ID || (type_flag(type) & PTR_UNTRUSTED); + return type =3D=3D PTR_TO_BTF_ID || + (type_flag(type) & PTR_UNTRUSTED) || + bpf_is_trusted_or_null_btf_ptr(type); } =20 static inline bool bpf_prog_has_arena_ctx_arg(const struct bpf_prog *prog) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 8f585ceb2cd5..386401fe051d 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -6116,6 +6116,15 @@ static int check_ptr_to_btf_access(struct bpf_verifi= er_env *env, if (ret !=3D PTR_TO_BTF_ID) { /* just mark; */ =20 + } else if (bpf_is_trusted_or_null_btf_ptr(reg->type)) { + /* + * An unchecked load through a trusted-or-NULL pointer is + * fault-protected. Any pointer derived from that load must be + * untrusted, as a fault produces a NULL value. + */ + clear_trusted_flags(&flag); + flag |=3D PTR_UNTRUSTED; + } else if (type_flag(reg->type) & PTR_UNTRUSTED) { /* If this is an untrusted pointer, all pointers formed by walking it * also inherit the untrusted flag. @@ -6591,7 +6600,8 @@ static int check_mem_access(struct bpf_verifier_env *= env, int insn_idx, struct b if (!err && t =3D=3D BPF_READ && value_regno >=3D 0) mark_reg_unknown(env, regs, value_regno); } else if (base_type(reg->type) =3D=3D PTR_TO_BTF_ID && - !type_may_be_null(reg->type)) { + (!type_may_be_null(reg->type) || + (t =3D=3D BPF_READ && bpf_is_trusted_or_null_btf_ptr(reg->type)))) { err =3D check_ptr_to_btf_access(env, regs, reg, argno, off, size, t, value_regno); } else if (reg->type =3D=3D CONST_PTR_TO_MAP) { --=20 2.55.0 From nobody Sat Sep 26 19:35:38 2026 Received: from mail-wm1-f41.google.com (mail-wm1-f41.google.com [209.85.128.41]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A3B4C3ED13F for ; Mon, 31 Aug 2026 09:23:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788168238; cv=none; b=h0wQokXie/gpgBSvkRgRUxMC+yo78VAI0UB1iVDaNwU4Uk6yEMQUqErqfwhHqQtP8Y4qTmlKXVehMGTZkGVhZRACD9ZiTwPn06a7a40mAGigD97xNUKcEPXIbpcM0oSh3LiSPMW//1JRF0AEGV75WDlVfRzGdAJ90hRp3043Ork= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788168238; c=relaxed/simple; bh=T8SdP+2U9F9B0gXX/X2VXAtIfyzSzavCSWhRpOtXWA8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=HcLztxBPn57n7ThYpIKqtiX6RW0T7EOhE1By6UbT7XjVPSMDDWBRqc8ubo3wgg8xi2LRzvI7iIViWJq5mUffaCaZBklaNSTbK8HilO/A+m8fg1+VZbIApC6UAxB+Z/4sORn3vWTAUa8J+KuI8XvCGSYsdVUgx3lSGXeb/DstOYA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=pYzcK3fj; arc=none smtp.client-ip=209.85.128.41 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="pYzcK3fj" Received: by mail-wm1-f41.google.com with SMTP id 5b1f17b1804b1-49b0d8bc2aaso31949455e9.0 for ; Mon, 31 Aug 2026 02:23:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788168233; x=1788773033; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=7aRnsGsof0e8gkLWKg4mNpSLZl74e3wgbbLhSoxhavQ=; b=pYzcK3fj4WwkKtgY+/NkkhsBM2EU0H8r2e+KZB/lnjeMfzCr7rvS0yfO6TCNF/hDOS 8Ef1sOpTnr9BDmekDteMQUUKBmxstAgLNsnDana9grkq+ZNyDecZ682Qz9AtJhoyiwGE /HLuvKy2gZkZ3IahrE+iOl0rRyNWAP9HcI8oRlxlaHvvBkMQGq7qN42sgs2Pe/roTbOr 3rNHqQusC/DDGOzwWToHnS7bI0+k8qwKqw/ziCFvy9FIYnQd9NhztcFzDmUh6w+ry+IW aPVDib0y8UZEM/8UHI99JaDbUFVotcmQJsCK5l8zJklk3taz5dNYw9ybYHzoH/txMa3k rwBA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788168233; x=1788773033; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=7aRnsGsof0e8gkLWKg4mNpSLZl74e3wgbbLhSoxhavQ=; b=VWlJjSaByGd6OiMAXM5tBpVD/RJTWMKoKpNKU+xoWYGRJFKrOI8Lmf17+OkgaagZJ6 1nnh0SxZqnTgo//1jdG3qX7maRPjkt9dE+MVkfiiYJ3SFvqQk5torQrX6NFajKVHAsCh LVK5uk0TSIQfSBxGadByOYI6t80qO4xj2a54AOCyEjgpTCL7M9I/nFJ1EA4OfXAo8p0d +nJ+7cvQ1OaV3SI/8IE7qIBFFTGHJYKPDOb4Uf4RhWzyV9fkdwxW7wI4BFjE6qc9irON wiNweV2KbbnoDwbuC3EKnBW+1hGgDB5Yq2emmPJYHcolh4DVIV25BTnnI/Ojj5GzAsHC ZLoQ== X-Gm-Message-State: AFuF++m20ML8V1dcYEcZZ97+3GVWceWJSlogBp0axU9bHKKbHrFhrZWR DHd4ePtiS17osvDTLY3AJlCpHIo/onob1aMR37uOTSEYBU+ZW3wcCHBv X-Gm-Gg: AR+sD13IGvbi0qOHnxWAa3Tx0Cg7hextBtiU7M6V7x/ZdI8vASlhsLwlrL4CgE0O7CB QZ/iFzx0vIHPRGpRwXq2RmOgN6R9rhJ1T6CQSUTuSASvcxFew+H64UjLK8Y0ek7Cx9G7htr9sEM DDa/BvpUrcy2sx94Tu6CrQIZFxmvuOJtGQxMMaxGZBdAHsMky8lt6cDioUPcHkX/DMe0Q7bdcpt SJmurB4btr/JiYruZe2g9AlmJS4AZkwV9frJxMxoIZN6L8uOz74BZswbR8Y/lhhg3b0S+8jHDs/ W/QLYuGylteA1uZbAoeK0tUOcQmdGzEgy6C2yRnbbmlGExWXqPCR2h1lcIdQW3vxrkG6pqSnMW+ 3gi55Ow7S+d1c7ucaSupcfEIh2kxOhYUDM/y0L5718xjsCtBBV22wtO2iQSJvTMSRoj5HwLe2Qa CnMkU00ve6s3Hx5Vps/uoJM43qSNuVJluxQGKtl2/P5pTgf+OnjaWDtrYx5WDfFM4inZ1P15tss A== X-Received: by 2002:a05:600c:1d1d:b0:49b:d45:703e with SMTP id 5b1f17b1804b1-49b91c2dfecmr369076705e9.8.1788168233348; Mon, 31 Aug 2026 02:23:53 -0700 (PDT) Received: from dell-desktop ([2a02:587:4b5f:900:ef25:2f4:1792:4e6f]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49b9500c80asm318248155e9.9.2026.08.31.02.23.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 02:23:52 -0700 (PDT) From: Anastasios Papagiannis To: bpf@vger.kernel.org Cc: linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-fsdevel@vger.kernel.org, viro@zeniv.linux.org.uk, brauner@kernel.org, akpm@linux-foundation.org, david@kernel.org, ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, memxor@gmail.com, kpsingh@kernel.org, matt@bobrowski.net, song@kernel.org, utilityemal77@gmail.com, tasos.papagiannnis@gmail.com Subject: [PATCH bpf-next v3 5/7] selftests/bpf: Cover trusted-or-null BTF pointer reads Date: Mon, 31 Aug 2026 12:23:03 +0300 Message-ID: <20260831092305.42062-6-tasos.papagiannnis@gmail.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260831092305.42062-1-tasos.papagiannnis@gmail.com> References: <20260831092305.42062-1-tasos.papagiannnis@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Update verifier tests that expected an unchecked trusted-or-null BTF pointer dereference to fail. Cover scalar reads and chained reads through BTF and memory pointers. Add a runtime test which verifies that non-NULL reads return the field value and NULL reads return zero. Verify that pointer arithmetic, stores, atomic RMW operations, and BPF_LOAD_ACQ accesses remain prohibited. Assert that a BTF pointer derived from an unchecked trusted-or-null load is PTR_UNTRUSTED. Verify that attempting to NULL-check the derived pointer remains rejected and that it cannot be passed to a kfunc requiring an RCU pointer. Keep the existing NULL-check tests to verify that an explicit check of the original pointer recovers normal trusted pointer behavior. Signed-off-by: Anastasios Papagiannis --- .../selftests/bpf/prog_tests/bpf_iter.c | 6 +- .../prog_tests/test_struct_ops_maybe_null.c | 12 ++-- .../bpf/prog_tests/tp_btf_nullable.c | 28 +++++++++ .../selftests/bpf/progs/raw_tp_null_fail.c | 63 ++++++++++++++++++- .../bpf/progs/test_tp_btf_nullable.c | 17 ++++- .../bpf/progs/test_tp_btf_nullable_runtime.c | 35 +++++++++++ .../selftests/bpf/progs/verifier_lsm.c | 19 ++++-- .../selftests/bpf/progs/verifier_vfs_accept.c | 13 ++++ .../selftests/bpf/progs/verifier_vfs_reject.c | 14 ----- .../selftests/bpf/test_kmods/bpf_testmod.c | 1 + .../testing/selftests/sched_ext/maybe_null.c | 6 +- 11 files changed, 181 insertions(+), 33 deletions(-) create mode 100644 tools/testing/selftests/bpf/progs/test_tp_btf_nullable_= runtime.c diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c b/tools/test= ing/selftests/bpf/prog_tests/bpf_iter.c index c69080ca14f5..99a16a1add70 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c @@ -39,10 +39,10 @@ static void test_btf_id_or_null(void) struct bpf_iter_test_kern3 *skel; =20 skel =3D bpf_iter_test_kern3__open_and_load(); - if (!ASSERT_ERR_PTR(skel, "bpf_iter_test_kern3__open_and_load")) { - bpf_iter_test_kern3__destroy(skel); + if (!ASSERT_OK_PTR(skel, "bpf_iter_test_kern3__open_and_load")) return; - } + + bpf_iter_test_kern3__destroy(skel); } =20 static void do_dummy_read_opts(struct bpf_program *prog, struct bpf_iter_a= ttach_opts *opts) diff --git a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_maybe_n= ull.c b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_maybe_null.c index 01dc2613c8a5..03670ddc89bd 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_maybe_null.c +++ b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_maybe_null.c @@ -19,15 +19,15 @@ static void maybe_null(void) struct_ops_maybe_null__destroy(skel); } =20 -/* Test that the verifier rejects a program that access a nullable pointer - * without a check beforehand. +/* Test that the verifier accepts a fault-protected read through a nullable + * trusted pointer without an explicit NULL check. */ -static void maybe_null_fail(void) +static void maybe_null_no_check(void) { struct struct_ops_maybe_null_fail *skel; =20 skel =3D struct_ops_maybe_null_fail__open_and_load(); - if (ASSERT_ERR_PTR(skel, "struct_ops_module_fail__open_and_load")) + if (!ASSERT_OK_PTR(skel, "struct_ops_maybe_null_fail__open_and_load")) return; =20 struct_ops_maybe_null_fail__destroy(skel); @@ -41,6 +41,6 @@ void test_struct_ops_maybe_null(void) */ if (test__start_subtest("maybe_null")) maybe_null(); - if (test__start_subtest("maybe_null_fail")) - maybe_null_fail(); + if (test__start_subtest("maybe_null_no_check")) + maybe_null_no_check(); } diff --git a/tools/testing/selftests/bpf/prog_tests/tp_btf_nullable.c b/too= ls/testing/selftests/bpf/prog_tests/tp_btf_nullable.c index accc42e01f8a..825fe7a92d74 100644 --- a/tools/testing/selftests/bpf/prog_tests/tp_btf_nullable.c +++ b/tools/testing/selftests/bpf/prog_tests/tp_btf_nullable.c @@ -2,6 +2,31 @@ =20 #include #include "test_tp_btf_nullable.skel.h" +#include "test_tp_btf_nullable_runtime.skel.h" + +static void test_nullable_runtime(void) +{ + struct test_tp_btf_nullable_runtime *skel; + + skel =3D test_tp_btf_nullable_runtime__open_and_load(); + if (!ASSERT_OK_PTR(skel, "open_and_load")) + return; + + skel->bss->monitored_tid =3D sys_gettid(); + + if (!ASSERT_OK(test_tp_btf_nullable_runtime__attach(skel), "attach")) + goto out; + + if (!ASSERT_OK(trigger_module_test_read(2), "trigger")) + goto out; + + ASSERT_EQ(skel->bss->calls, 2, "calls"); + ASSERT_EQ(skel->bss->nonnull_len, 2, "nonnull_len"); + ASSERT_EQ(skel->bss->null_len, 0, "null_len"); + +out: + test_tp_btf_nullable_runtime__destroy(skel); +} =20 void test_tp_btf_nullable(void) { @@ -11,4 +36,7 @@ void test_tp_btf_nullable(void) } =20 RUN_TESTS(test_tp_btf_nullable); + + if (test__start_subtest("runtime")) + test_nullable_runtime(); } diff --git a/tools/testing/selftests/bpf/progs/raw_tp_null_fail.c b/tools/t= esting/selftests/bpf/progs/raw_tp_null_fail.c index 0d58114a4955..45df28c0a25e 100644 --- a/tools/testing/selftests/bpf/progs/raw_tp_null_fail.c +++ b/tools/testing/selftests/bpf/progs/raw_tp_null_fail.c @@ -2,14 +2,18 @@ /* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */ =20 #include +#include #include #include "bpf_misc.h" =20 char _license[] SEC("license") =3D "GPL"; =20 +extern struct task_struct *bpf_task_acquire(struct task_struct *p) __ksym; +extern void bpf_task_release(struct task_struct *p) __ksym; + /* Ensure module parameter has PTR_MAYBE_NULL */ SEC("tp_btf/bpf_testmod_test_raw_tp_null_tp") -__failure __msg("R1 invalid mem access 'trusted_ptr_or_null_'") +__success int test_raw_tp_null_bpf_testmod_test_raw_tp_null_arg_1(void *ctx) { asm volatile("r1 =3D *(u64 *)(r1 +0); r1 =3D *(u64 *)(r1 +0);" ::: __c= lobber_all); return 0; @@ -17,8 +21,63 @@ int test_raw_tp_null_bpf_testmod_test_raw_tp_null_arg_1(= void *ctx) { =20 /* Check NULL marking */ SEC("tp_btf/sched_pi_setprio") -__failure __msg("R1 invalid mem access 'trusted_ptr_or_null_'") +__success int test_raw_tp_null_sched_pi_setprio_arg_2(void *ctx) { asm volatile("r1 =3D *(u64 *)(r1 +8); r1 =3D *(u64 *)(r1 +0);" ::: __c= lobber_all); return 0; } + +SEC("tp_btf/sched_pi_setprio") +__failure __log_level(2) +__msg("R1=3Duntrusted_ptr_task_struct") +__msg("R1 must be a rcu pointer") +int BPF_PROG(trusted_or_null_walk_is_untrusted, struct task_struct *task, + struct task_struct *pi_task) +{ + struct task_struct *parent, *acquired; + + parent =3D pi_task->real_parent; + acquired =3D bpf_task_acquire(parent); + if (acquired) + bpf_task_release(acquired); + return 0; +} + +SEC("tp_btf/sched_pi_setprio") +__failure __msg("R1 must be a rcu pointer") +int BPF_PROG(derived_ptr_null_check_does_not_restore_trust, + struct task_struct *task, struct task_struct *pi_task) +{ + struct task_struct *parent, *acquired; + + parent =3D pi_task->real_parent; + if (!parent) + return 0; + + acquired =3D bpf_task_acquire(parent); + if (acquired) + bpf_task_release(acquired); + + return 0; +} + +/* + * In contrast, checking the original trusted-or-NULL pointer removes + * PTR_MAYBE_NULL while retaining PTR_TRUSTED. + */ +SEC("tp_btf/sched_pi_setprio") +__success +int BPF_PROG(original_ptr_null_check_retains_trust, + struct task_struct *task, struct task_struct *pi_task) +{ + struct task_struct *acquired; + + if (!pi_task) + return 0; + + acquired =3D bpf_task_acquire(pi_task); + if (acquired) + bpf_task_release(acquired); + + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/test_tp_btf_nullable.c b/too= ls/testing/selftests/bpf/progs/test_tp_btf_nullable.c index cf0547a613ff..a86b526d27f9 100644 --- a/tools/testing/selftests/bpf/progs/test_tp_btf_nullable.c +++ b/tools/testing/selftests/bpf/progs/test_tp_btf_nullable.c @@ -7,7 +7,7 @@ #include "bpf_misc.h" =20 SEC("tp_btf/bpf_testmod_test_nullable_bare_tp") -__failure __msg("R1 invalid mem access 'trusted_ptr_or_null_'") +__success int BPF_PROG(handle_tp_btf_nullable_bare1, struct bpf_testmod_test_read_ct= x *nullable_ctx) { return nullable_ctx->len; @@ -21,4 +21,19 @@ int BPF_PROG(handle_tp_btf_nullable_bare2, struct bpf_te= stmod_test_read_ctx *nul return 0; } =20 +SEC("tp_btf/bpf_testmod_test_nullable_bare_tp") +__success +int BPF_PROG(handle_tp_btf_nullable_mem, struct bpf_testmod_test_read_ctx = *nullable_ctx) +{ + return nullable_ctx->buf[0]; +} + +SEC("tp_btf/bpf_testmod_test_nullable_bare_tp") +__failure __msg("pointer arithmetic on trusted_ptr_or_null_ prohibited") +int BPF_PROG(handle_tp_btf_nullable_arith, struct bpf_testmod_test_read_ct= x *nullable_ctx) +{ + asm volatile("%[ctx] +=3D 1" : [ctx] "+r"(nullable_ctx)); + return nullable_ctx->len; +} + char _license[] SEC("license") =3D "GPL"; diff --git a/tools/testing/selftests/bpf/progs/test_tp_btf_nullable_runtime= .c b/tools/testing/selftests/bpf/progs/test_tp_btf_nullable_runtime.c new file mode 100644 index 000000000000..5c9c7f94040d --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_tp_btf_nullable_runtime.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "vmlinux.h" +#include +#include +#include "../test_kmods/bpf_testmod.h" + +char _license[] SEC("license") =3D "GPL"; + +int monitored_tid; +int calls; +__u64 nonnull_len; +__u64 null_len; + +SEC("tp_btf/bpf_testmod_test_nullable_bare_tp") +int BPF_PROG(handle_nullable_runtime, + struct bpf_testmod_test_read_ctx *nullable_ctx) +{ + __u32 tid =3D bpf_get_current_pid_tgid(); + __u64 len; + int call; + + if (tid !=3D monitored_tid) + return 0; + + len =3D nullable_ctx->len; + call =3D calls++; + + if (call =3D=3D 0) + nonnull_len =3D len; + else if (call =3D=3D 1) + null_len =3D len; + + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/verifier_lsm.c b/tools/testi= ng/selftests/bpf/progs/verifier_lsm.c index c724bf389f5c..c912215579c9 100644 --- a/tools/testing/selftests/bpf/progs/verifier_lsm.c +++ b/tools/testing/selftests/bpf/progs/verifier_lsm.c @@ -162,13 +162,13 @@ __naked int disabled_hook_test3(void *ctx) =20 SEC("lsm/mmap_file") __description("not null checking nullable pointer in bpf_lsm_mmap_file") -__failure __msg("R1 invalid mem access 'trusted_ptr_or_null_'") +__success int BPF_PROG(no_null_check, struct file *file) { - struct inode *inode; + ino_t ino; =20 - inode =3D file->f_inode; - __sink(inode); + ino =3D file->f_inode->i_ino; + __sink(ino); =20 return 0; } @@ -188,6 +188,17 @@ int BPF_PROG(null_check, struct file *file) return 0; } =20 +SEC("lsm.s/bprm_check_security") +__description("store through trusted-or-null bprm->mm is rejected") +__failure +__msg("R{{[0-9]+}} invalid mem access 'trusted_ptr_or_null_'") +int BPF_PROG(store_through_trusted_or_null_bprm_mm, + struct linux_binprm *bprm) +{ + bprm->mm->task_size =3D 0; + return 0; +} + SEC("lsm_cgroup/file_open") __description("sleepable lsm_cgroup program is rejected") __failure __msg("Program of this type cannot be sleepable") diff --git a/tools/testing/selftests/bpf/progs/verifier_vfs_accept.c b/tool= s/testing/selftests/bpf/progs/verifier_vfs_accept.c index 55398c04290a..2827a1650727 100644 --- a/tools/testing/selftests/bpf/progs/verifier_vfs_accept.c +++ b/tools/testing/selftests/bpf/progs/verifier_vfs_accept.c @@ -100,4 +100,17 @@ int BPF_PROG(inode_rename, struct inode *old_dir, stru= ct dentry *old_dentry, return 0; } =20 +SEC("lsm.s/inode_rename") +__success +int BPF_PROG(inode_rename_no_null_check, struct inode *old_dir, + struct dentry *old_dentry, struct inode *new_dir, + struct dentry *new_dentry, unsigned int flags) +{ + ino_t ino =3D new_dentry->d_inode->i_ino; + + if (ino =3D=3D 0) + return -EACCES; + return 0; +} + char _license[] SEC("license") =3D "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c b/tool= s/testing/selftests/bpf/progs/verifier_vfs_reject.c index 8f0c45421f89..2a0813258183 100644 --- a/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c +++ b/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c @@ -159,18 +159,4 @@ int BPF_PROG(path_d_path_kfunc_non_lsm, struct path *p= ath, struct file *f) return 0; } =20 -SEC("lsm.s/inode_rename") -__failure __msg("invalid mem access 'trusted_ptr_or_null_'") -int BPF_PROG(inode_rename, struct inode *old_dir, struct dentry *old_dentr= y, - struct inode *new_dir, struct dentry *new_dentry, - unsigned int flags) -{ - struct inode *inode =3D new_dentry->d_inode; - ino_t ino; - - ino =3D inode->i_ino; - if (ino =3D=3D 0) - return -EACCES; - return 0; -} char _license[] SEC("license") =3D "GPL"; diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/t= esting/selftests/bpf/test_kmods/bpf_testmod.c index 2380b6cbdead..2da9c2464c25 100644 --- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c +++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c @@ -651,6 +651,7 @@ bpf_testmod_test_read(struct file *file, struct kobject= *kobj, if (bpf_testmod_loop_test(101) > 100) trace_bpf_testmod_test_read(current, &ctx); =20 + trace_bpf_testmod_test_nullable_bare_tp(&ctx); trace_bpf_testmod_test_nullable_bare_tp(NULL); =20 /* Magic number to enable writable tp */ diff --git a/tools/testing/selftests/sched_ext/maybe_null.c b/tools/testing= /selftests/sched_ext/maybe_null.c index aacf0c58ca4f..8fec296c31bd 100644 --- a/tools/testing/selftests/sched_ext/maybe_null.c +++ b/tools/testing/selftests/sched_ext/maybe_null.c @@ -25,11 +25,11 @@ static enum scx_test_status run(void *ctx) maybe_null__destroy(skel); =20 fail_dsp =3D maybe_null_fail_dsp__open_and_load(); - if (fail_dsp) { - maybe_null_fail_dsp__destroy(fail_dsp); - SCX_ERR("Should failed to open and load maybe_null_fail_dsp skel"); + if (!fail_dsp) { + SCX_ERR("Failed to open and load maybe_null_fail_dsp skel"); return SCX_TEST_FAIL; } + maybe_null_fail_dsp__destroy(fail_dsp); =20 fail_yld =3D maybe_null_fail_yld__open_and_load(); if (fail_yld) { --=20 2.55.0 From nobody Sat Sep 26 19:35:38 2026 Received: from mail-wm1-f52.google.com (mail-wm1-f52.google.com [209.85.128.52]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 81FF13EE1E0 for ; Mon, 31 Aug 2026 09:23:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788168240; cv=none; b=L9VIIPRc1zbGQCvGGpPu/ZCMrNRDuyDgGmkSOEP1VMIAwcrr2SLUWoQmEnLOJQaJYtWVrWk8hTVVN6ahFwavof6oPrqaKwAiti9KH4DurEZqIKhwETOgz0Fu123W7VNT8/C49zlkAuTnpPnkPfkbcrqA92pDNBo2WONaOtF8OPc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788168240; c=relaxed/simple; bh=ny8/Gs7FMzc8WbloXN305yxNtUC/v7SjWkG2BsK5NEM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=S3tVQGJqrdExfFR+sjeVDKCTGm9/INTu8c9ARK3ViboBB4WwYGb1cyEQcYZuNlir0hj1VgOiZp53NS83mOeAw87Vgtc0/Slq9DRXEynRhl0p82s64G1v3UEhqrnC6j9q0EOVwjAgOV18oP8n1+/RP/vbKio3pO2xolcBWL+QABI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=dkkdncBm; arc=none smtp.client-ip=209.85.128.52 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="dkkdncBm" Received: by mail-wm1-f52.google.com with SMTP id 5b1f17b1804b1-49b8687630fso22923495e9.3 for ; Mon, 31 Aug 2026 02:23:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788168237; x=1788773037; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=7+K9xBUZV2Jmd3T8roo1dKtbVqBUblSh726dOO5UJPI=; b=dkkdncBm1PRV6nFUYMlWgWnENg938zB2PisjOq44ytVBqHRPt/d/0onDsv10OQSa9g mW4vq3AYdRmOmNwzRpMTTQRKUQXdveuXnM1nb1yFEHUR0AnmYAKo9FMvAf38Drq22ACa KTNMqWBOktOZUS9UARVSTStWPpg62AQPzGw6LhgrS8UFR/WIdXSwicpLY7vaSaneNzBy XS5mWG+s3uBOux0nVzxJNgB+MhlpxP0gAcu9DDakaRMUXT8hiT5bFWEbfPM6lxzcdA1G gEU5OAX0PL258LjuXwRkMSvHjawvwpvb5JE4FRfMT3V2PRQecBtj8/d+YeBfV+kRhldm 9qLg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788168237; x=1788773037; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=7+K9xBUZV2Jmd3T8roo1dKtbVqBUblSh726dOO5UJPI=; b=qUMNNi8GiB8Ss9Cvd6A5yl56SbvuLAyY5uU+ED1y6j1tu0Tas5EUvEGsbokPoJt07/ pJylRx5R3ShKn2qaOv+UrHuHJWHb80le410UhwycXvPRpQ0UaYr/MTgeDVt1rkMGBOhe VJUq9lupZSGSv8unSaZFukpVbkR17lyMcDM07QTN3vxUHVntfvsoMr90bFyjh20Ya9V8 LIfyTdlvwxQ+BzAoNTfVXI8aB+M7/Gl5jl/vwvMNOj4YhL/7+iyIzRwz8nfcfpBk66Hc eBhUsGm2TSAt/v7mnK8cy24TDoS1X1B1E5CrJHrFdsvj53V4Ks9uvJB1Yr9L4E1hGweJ NMjA== X-Gm-Message-State: AFuF++ktkXLj3S9YhXd89PEfqNKfOq7wvq/R591lIIabyIC+WxjuaoLD 0/EYnQvqBGCynYZ/uYJmjdZpumw42PwfZokSniBIhERCgi8tGW8deODd X-Gm-Gg: AR+sD13kk8G7aZkj+jpM0BvaiwHsqMVIZclG3dRkrfQElQXs7Ugvjq3dyyC5H0DxTzL LZWzEwO4J9GXbmmMwzbRdVicLqU+h9k6mevbIfDz6nIokv8o8lAoB9l8+kR0OZ5GA1ZciF9OQNs 0i4qserPBh1cZLG8Gu4G9iGs5s9otrVYiTOyF3N7zq0THhVFTGl1cQRd6roR8nvrUAgnu+pkdkA qCbdXARheYB/yMCDztbMtZThDz0W8Wp5ee5gauhQquylkP5FruE2ymR8MXqXLLoxlo807HK4Uvx 66eujaIuiZxjbt1bLOWi9fUxUhl38ge050rhbEbH3dCN70Frr96HMBgNzanWNO+obppaCjZHIgB XKCMjau0cW3oy9avsr9MeoOq4Zp53TV45yQfuO8/gIRAmQj6W2VG1yd2+FzQsxiBef7GwRWwEgW P4GJXEBLVPE9YgewEIBHttuwn4eRqSGG2GWcsbyO/VuWtJzYLEIOkbdbXDLnm+WkVvesueaq1IJ A== X-Received: by 2002:a05:600c:1d08:b0:499:bf0e:95c8 with SMTP id 5b1f17b1804b1-49b91c2e145mr372213505e9.1.1788168236596; Mon, 31 Aug 2026 02:23:56 -0700 (PDT) Received: from dell-desktop ([2a02:587:4b5f:900:ef25:2f4:1792:4e6f]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49b9500c80asm318248155e9.9.2026.08.31.02.23.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 02:23:56 -0700 (PDT) From: Anastasios Papagiannis To: bpf@vger.kernel.org Cc: linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-fsdevel@vger.kernel.org, viro@zeniv.linux.org.uk, brauner@kernel.org, akpm@linux-foundation.org, david@kernel.org, ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, memxor@gmail.com, kpsingh@kernel.org, matt@bobrowski.net, song@kernel.org, utilityemal77@gmail.com, tasos.papagiannnis@gmail.com Subject: [PATCH bpf-next v3 6/7] bpf: Mark linux_binprm->mm as trusted-or-null Date: Mon, 31 Aug 2026 12:23:04 +0300 Message-ID: <20260831092305.42062-7-tasos.papagiannnis@gmail.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260831092305.42062-1-tasos.papagiannnis@gmail.com> References: <20260831092305.42062-1-tasos.papagiannnis@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Mark linux_binprm->mm as a trusted-or-null nested pointer so BPF programs can pass it to kfuncs after a NULL check. The linux_binprm owns this mm throughout the bprm_check_security hook and clears the pointer only after exec_mmap() installs the new address space. Signed-off-by: Anastasios Papagiannis Reviewed-by: Sun Jian --- kernel/bpf/verifier.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 386401fe051d..584c79326b9c 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -5958,6 +5958,10 @@ BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct dentry) { struct inode *d_inode; }; =20 +BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct linux_binprm) { + struct mm_struct *mm; +}; + BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct socket) { struct sock *sk; }; @@ -6012,6 +6016,7 @@ static bool type_is_trusted_or_null(struct bpf_verifi= er_env *env, { BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct socket)); BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct dentry)); + BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct linux_binprm)); BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct vm_area_struct)); =20 return btf_nested_type_is_trusted(&env->log, reg, field_name, btf_id, --=20 2.55.0 From nobody Sat Sep 26 19:35:38 2026 Received: from mail-wr1-f48.google.com (mail-wr1-f48.google.com [209.85.221.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2D8A83F3262 for ; Mon, 31 Aug 2026 09:24:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788168244; cv=none; b=OdbS+rUEAhCZkeSseOf9ypqDA+UPEcLaQAuN3691ieR/QFGcOtV7C+D/SqpYAGO/LcgyjFA9NdUvkBj4Fw3LL6Yi0U3IXXVtT+hslVPX2/rU5au0Q+u1BoUNmljm51+x3hXCBY5Le6TMw8yxRkWPj2318WQaGtd5yUUv9wjJdnc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788168244; c=relaxed/simple; bh=dVds8F/uQaGiVVtA9skFGZdEhgcSv5XJRG8BjPr93wo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=faNNAG2gEwJ9u0qVQJRJuyagWZseJx1+zHj774OY9R4Kb3wd0rF5DSrZ0QNEA4u87TnAB0T50cRHf3P3W1CX5lbnDkaxyLgTxRLTeGfXR+mbmjRvVZ5BNot94Vl1/ePQRLd7qX63yto0y9xRf1ydQD43TLPZcSWFwwbQO19RDN8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=tDgaR5G9; arc=none smtp.client-ip=209.85.221.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="tDgaR5G9" Received: by mail-wr1-f48.google.com with SMTP id ffacd0b85a97d-48436216a98so648185f8f.0 for ; Mon, 31 Aug 2026 02:24:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788168240; x=1788773040; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=i8u8KeUtgM4fV7u/x1/LAbjzMrbujtoGelddq2lUEz0=; b=tDgaR5G9WZXTcbo61RNvKggE11LFan3nuf58fmJlsRFgtXNsWZZSRs9RVO/rYU4XlP 15PL41Q7amCiPWgVIASXyXK2NXX0aWO8ZzJemDmaGp57LmKactVqncgvzKq7d+Num2EB nD/BIOg2GNL9VvA6wkP9eQHX5S31v6Z6DXyH3EiQ4zFuPpd/89ag5p1tPq+wzrrKeklW dvU/zSmOaWm4LX3GsyNCIArCICJ46cAIlE2ABc7nklCaiPUXza+4BCcZHIocuytRfgXD DuqE0nLwRiZE2j2QB8fMwsc80ijLxoFoygpR84ImoGs4L2GpuAtdZaxyPhobaErlcEfC 7RZg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788168240; x=1788773040; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=i8u8KeUtgM4fV7u/x1/LAbjzMrbujtoGelddq2lUEz0=; b=RiP/0o3ejJqxOP7Amwhs/jByW+COzKUpPz7rUg3Apk/CjzngFiftFBoCzvC4X7Y+2S /LY3zG/NWadlaDwbGBKnWugeLa8h/n+cLO63OWP5ksLalN6cz87Bb4k763nH3usAarmp JbXVPqaSvmy1xdIgEJExEmv+4Rmjk7XBL6mRVkG+a63hHhiaUhP+ZyWqUMd84twG/O+T 5ujfjS4qY3A8YXk9Sq8P1zJIWMVLZ7e/nWZPPGf1hRD1NMsOqqstVxP4xEyFLRlL43mF zXbtj8kAqA8XWzYtdwQgV68i/p7zjn8bx2S1/rwSdX4WLKzKlbdHsQxo/jiTb0BTfRoR Kw7Q== X-Gm-Message-State: AFuF++nZNjiwwH3mWCrHNRBz2g6aeaBxX895k9tmsFUKQVs4mURgnCEg t4zdLctJ2NjFJIUczUH/us+EuJEgCu5lBHmjQTqjPdnI30BSIx/c0p93 X-Gm-Gg: AR+sD11bZOMeS4htoL0FPtAm2Vv8zwbxcmSl3HITfuPfTAT6T8/Inxl8PSdw8xsFvi1 xZaS6uqUmGFWn44ooMmdjtDDAToCYFe++krSoeamfMOXvtz1p47Ey8RZ2FDWxJPnC+5BDWXClOE xSg+HySmjBiMgCXNKKcXztG4UXnKohEPeAeHYg/SryvU+pe0tURVQTa7/imonHu+LpXanknVZvo qUWgHBWmm1vkWOliwEAQOlwLUBhk8I2xA7vAIlqKOYOB1ALEej89DlZX5s1efGPQz0vQ1pDLQZX tMw86HUZKeb596ifn+n97C0MnB0ITAltSmPsAZ28ypEqNYYVUBcvyUep1IFHnXKiamVhjoa5nzn ciBAzhL2rcuojcKThUQpjxJTOhmrAARC4QciKG5KnnItgRHbISpJijuphb7u5SGBBNwu1bsHRAR z1e4Njl4tvXIVc4399K3O87nTsfRJWcQK7RD2XWuPq5Jwp/DIy8XJwcL7mi1clH9qyDPKYX61mi g== X-Received: by 2002:a05:600c:c4ac:b0:499:a5fc:2087 with SMTP id 5b1f17b1804b1-49b91c20e36mr367057785e9.6.1788168240083; Mon, 31 Aug 2026 02:24:00 -0700 (PDT) Received: from dell-desktop ([2a02:587:4b5f:900:ef25:2f4:1792:4e6f]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49b9500c80asm318248155e9.9.2026.08.31.02.23.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 02:23:59 -0700 (PDT) From: Anastasios Papagiannis To: bpf@vger.kernel.org Cc: linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-fsdevel@vger.kernel.org, viro@zeniv.linux.org.uk, brauner@kernel.org, akpm@linux-foundation.org, david@kernel.org, ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, memxor@gmail.com, kpsingh@kernel.org, matt@bobrowski.net, song@kernel.org, utilityemal77@gmail.com, tasos.papagiannnis@gmail.com Subject: [PATCH bpf-next v3 7/7] selftests/bpf: Test mm_struct user memory kfuncs with linux_binprm Date: Mon, 31 Aug 2026 12:23:05 +0300 Message-ID: <20260831092305.42062-8-tasos.papagiannnis@gmail.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260831092305.42062-1-tasos.papagiannnis@gmail.com> References: <20260831092305.42062-1-tasos.papagiannnis@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a sleepable BPF LSM program attached to bprm_check_security to test bpf_copy_from_user_mm() and bpf_copy_from_user_mm_str() on CONFIG_MMU kernels. Starting at bprm->p, verify that bpf_copy_from_user_mm() can copy the contiguous NUL-separated argument and environment data. Then use bpf_copy_from_user_mm_str() to read each argument and environment string separately, advancing the offset by the length returned from each call. Skip the test on !CONFIG_MMU. In that configuration, exec argument and environment strings remain in bprm->page[] until the binary loader transfers them to the new process stack, so they are not accessible through bprm->mm at the bprm_check_security hook. Signed-off-by: Anastasios Papagiannis --- .../bpf/prog_tests/copy_from_user_bprm.c | 72 ++++++++++ .../selftests/bpf/progs/copy_from_user_bprm.c | 123 ++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/copy_from_user_b= prm.c create mode 100644 tools/testing/selftests/bpf/progs/copy_from_user_bprm.c diff --git a/tools/testing/selftests/bpf/prog_tests/copy_from_user_bprm.c b= /tools/testing/selftests/bpf/prog_tests/copy_from_user_bprm.c new file mode 100644 index 000000000000..b2325b193576 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/copy_from_user_bprm.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include + +#include + +#include "copy_from_user_bprm.skel.h" + +void test_copy_from_user_bprm(void) +{ + char arg0[] =3D "first"; + char arg1[] =3D "second-argument"; + char env0[] =3D "SOME_ENV=3Da"; + char env1[] =3D "OTHER_ENV=3Dsomething"; + struct copy_from_user_bprm *skel; + pid_t child; + int status; + + skel =3D copy_from_user_bprm__open_and_load(); + if (!ASSERT_OK_PTR(skel, "open_and_load")) + return; + + /* + * On !CONFIG_MMU, exec strings are held in bprm->page[] rather than + * being mapped in bprm->mm. + */ + if (!skel->kconfig->CONFIG_MMU) { + printf("%s:SKIP: test requires CONFIG_MMU\n", __func__); + test__skip(); + goto out; + } + + if (!ASSERT_OK(copy_from_user_bprm__attach(skel), "attach")) + goto out; + + child =3D fork(); + if (!ASSERT_GE(child, 0, "fork")) + goto out; + + if (!child) { + char *const argv[] =3D { arg0, arg1, NULL }; + char *const envp[] =3D { env0, env1, NULL }; + + skel->bss->monitored_pid =3D getpid(); + execve("/bin/true", argv, envp); + _exit(errno); + } + + if (!ASSERT_EQ(waitpid(child, &status, 0), child, "waitpid")) + goto out; + + if (ASSERT_TRUE(WIFEXITED(status), "child_exited")) + ASSERT_EQ(WEXITSTATUS(status), EPERM, "exec_errno"); + + ASSERT_EQ(skel->bss->bprm_argc, 2, "bprm_argc"); + ASSERT_EQ(skel->bss->bprm_envc, 2, "bprm_envc"); + ASSERT_EQ(skel->bss->data_len_match, 1, "data_len_match"); + ASSERT_EQ(skel->bss->invalid_flags_ret, -EINVAL, "invalid_flags_ret"); + ASSERT_EQ(skel->bss->copy_ret, 0, "copy_ret"); + ASSERT_EQ(skel->bss->str_arg0_ret, sizeof(arg0), "str_arg0_ret"); + ASSERT_EQ(skel->bss->str_arg1_ret, sizeof(arg1), "str_arg1_ret"); + ASSERT_EQ(skel->bss->str_env0_ret, sizeof(env0), "str_env0_ret"); + ASSERT_EQ(skel->bss->str_env1_ret, sizeof(env1), "str_env1_ret"); + ASSERT_EQ(skel->bss->data_match, 1, "data_match"); + ASSERT_EQ(skel->bss->str_args_match, 1, "str_args_match"); + ASSERT_EQ(skel->bss->str_envs_match, 1, "str_envs_match"); + +out: + copy_from_user_bprm__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/progs/copy_from_user_bprm.c b/tool= s/testing/selftests/bpf/progs/copy_from_user_bprm.c new file mode 100644 index 000000000000..a0e2d1c86c2c --- /dev/null +++ b/tools/testing/selftests/bpf/progs/copy_from_user_bprm.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "vmlinux.h" + +#include +#include +#include +#include "bpf_misc.h" + +char _license[] SEC("license") =3D "GPL"; + +static const char expected_data[] =3D "first\0second-argument\0" + "SOME_ENV=3Da\0OTHER_ENV=3Dsomething"; +static const char expected_arg0[] =3D "first"; +static const char expected_arg1[] =3D "second-argument"; +static const char expected_env0[] =3D "SOME_ENV=3Da"; +static const char expected_env1[] =3D "OTHER_ENV=3Dsomething"; + +int monitored_pid; +int bprm_argc; +int bprm_envc; +int data_len_match; +int invalid_flags_ret; +int copy_ret; +int str_arg0_ret; +int str_arg1_ret; +int str_env0_ret; +int str_env1_ret; +int data_match; +int str_args_match; +int str_envs_match; + +extern bool CONFIG_MMU __kconfig __weak; + +extern int bpf_copy_from_user_mm(void *dst, u32 dst__sz, + const void *unsafe_ptr__ign, + struct mm_struct *mm, u64 flags) __ksym; + +extern int bpf_copy_from_user_mm_str(void *dst, u32 dst__sz, + const void *unsafe_ptr__ign, + struct mm_struct *mm, u64 flags) __ksym; + +SEC("lsm.s/bprm_check_security") +int BPF_PROG(check_exec_args, struct linux_binprm *bprm) +{ + u32 pid =3D bpf_get_current_pid_tgid() >> 32; + char data[sizeof(expected_data)]; + struct mm_struct *mm; + char arg0[32]; + char arg1[32]; + char env0[32]; + char env1[32]; + u64 offset =3D 0; + u64 data_len; + + if (!CONFIG_MMU) + return 0; + + if (pid !=3D monitored_pid) + return 0; + + mm =3D bprm->mm; + if (!mm) + return 0; + + bprm_argc =3D bprm->argc; + bprm_envc =3D bprm->envc; + + /* this is the total size of args and envs starting from bprm->p */ + data_len =3D bprm->exec - bprm->p; + data_len_match =3D data_len =3D=3D sizeof(expected_data); + + invalid_flags_ret =3D bpf_copy_from_user_mm(data, + sizeof(data), (void *)bprm->p, mm, ~0ULL); + + copy_ret =3D bpf_copy_from_user_mm(data, sizeof(data), (void *)bprm->p, + mm, 0); + if (copy_ret) + return 0; + + data_match =3D + !__builtin_memcmp(data, expected_data, sizeof(expected_data)); + + /* arg0 is at bprm->p */ + str_arg0_ret =3D bpf_copy_from_user_mm_str(arg0, sizeof(arg0), + (void *)(bprm->p + offset), + mm, BPF_F_PAD_ZEROS); + if (str_arg0_ret !=3D sizeof(expected_arg0)) + return 0; + offset +=3D str_arg0_ret; + + /* arg1 is at bprm->p + sizeof(arg0) */ + str_arg1_ret =3D bpf_copy_from_user_mm_str(arg1, sizeof(arg1), + (void *)(bprm->p + offset), + mm, BPF_F_PAD_ZEROS); + if (str_arg1_ret !=3D sizeof(expected_arg1)) + return 0; + offset +=3D str_arg1_ret; + + /* env0 is at bprm->p + sizeof(arg0) + sizeof(arg1) */ + str_env0_ret =3D bpf_copy_from_user_mm_str(env0, sizeof(env0), + (void *)(bprm->p + offset), + mm, BPF_F_PAD_ZEROS); + if (str_env0_ret !=3D sizeof(expected_env0)) + return 0; + offset +=3D str_env0_ret; + + /* env1 is at bprm->p + sizeof(arg0) + sizeof(arg1) + sizeof(env0) */ + str_env1_ret =3D bpf_copy_from_user_mm_str(env1, sizeof(env1), + (void *)(bprm->p + offset), + mm, BPF_F_PAD_ZEROS); + if (str_env1_ret !=3D sizeof(expected_env1)) + return 0; + + str_args_match =3D + !__builtin_memcmp(arg0, expected_arg0, sizeof(expected_arg0)) && + !__builtin_memcmp(arg1, expected_arg1, sizeof(expected_arg1)); + str_envs_match =3D + !__builtin_memcmp(env0, expected_env0, sizeof(expected_env0)) && + !__builtin_memcmp(env1, expected_env1, sizeof(expected_env1)); + + return data_match && str_args_match && str_envs_match ? -EPERM : 0; +} --=20 2.55.0