* Re: [PATCH] pid: use ns_capable_noaudit() when determining net sysctl permissions
From: Paul Moore @ 2025-09-11 20:46 UTC (permalink / raw)
To: Christian Göttsche, linux-kernel
Cc: Christian Göttsche, Christian Brauner, linux-security-module,
selinux
In-Reply-To: <20250910192605.16431-1-cgoettsche@seltendoof.de>
On Sep 10, 2025 =?UTF-8?q?Christian=20G=C3=B6ttsche?= <cgoettsche@seltendoof.de> wrote:
>
> The capability check should not be audited since it is only being used
> to determine the inode permissions. A failed check does not indicate a
> violation of security policy but, when an LSM is enabled, a denial audit
> message was being generated.
>
> The denial audit message can either lead to the capability being
> unnecessarily allowed in a security policy, or being silenced potentially
> masking a legitimate capability check at a later point in time.
>
> Similar to commit d6169b0206db ("net: Use ns_capable_noaudit() when
> determining net sysctl permissions")
>
> Fixes: 7863dcc72d0f ("pid: allow pid_max to be set per pid namespace")
> CC: Christian Brauner <brauner@kernel.org>
> CC: linux-security-module@vger.kernel.org
> CC: selinux@vger.kernel.org
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> ---
> kernel/pid.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
Reviewed-by: Paul Moore <paul@paul-moore.com>
--
paul-moore.com
^ permalink raw reply
* Re: [PATCH 1/6] security_dentry_init_security(): constify qstr argument
From: Paul Moore @ 2025-09-11 20:54 UTC (permalink / raw)
To: Al Viro, linux-fsdevel
Cc: torvalds, brauner, jack, neil, linux-security-module, dhowells,
linkinjeon
In-Reply-To: <20250911050534.3116491-1-viro@zeniv.linux.org.uk>
On Sep 11, 2025 Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> Nothing outside of fs/dcache.c has any business modifying
> dentry names; passing &dentry->d_name as an argument should
> have that argument declared as a const pointer.
>
> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
> Acked-by: Casey Schaufler <casey@schaufler-ca.com>
> Reviewed-by: David Howells <dhowells@redhat.com>
> ---
> include/linux/lsm_hook_defs.h | 2 +-
> include/linux/security.h | 4 ++--
> security/security.c | 2 +-
> security/selinux/hooks.c | 2 +-
> security/smack/smack_lsm.c | 2 +-
> 5 files changed, 6 insertions(+), 6 deletions(-)
LSM and SELinux bits look fine to me. Al, I'm guessing you would
prefer to take this patch as part of the larger patchset, but if you
want me to take this single patch for the upcoming merge window let
me know.
Acked-by: Paul Moore <paul@paul-moore.com>
--
paul-moore.com
^ permalink raw reply
* Re: [RFC PATCH] lsm,selinux: introduce LSM_ATTR_UNSHARE and wire it up for SELinux
From: Paul Moore @ 2025-09-11 22:24 UTC (permalink / raw)
To: Stephen Smalley
Cc: selinux, linux-security-module, omosnace, john.johansen, serge,
casey
In-Reply-To: <CAEjxPJ695zmnpiLLOHkLxt5HVPhFNRZ9Bu+o4khvFwFX3TLpVQ@mail.gmail.com>
On Thu, Sep 11, 2025 at 10:01 AM Stephen Smalley
<stephen.smalley.work@gmail.com> wrote:
> On Thu, Sep 4, 2025 at 10:41 AM Stephen Smalley
> <stephen.smalley.work@gmail.com> wrote:
> > On Wed, Sep 3, 2025 at 3:28 PM Stephen Smalley
> > <stephen.smalley.work@gmail.com> wrote:
> > >
> > > In the hopes of nudging the conversation in [1] in a more focused
> > > direction with a goal of getting SELinux namespaces upstreamed, this
> > > RFC provides a starting point for a concrete discussion. This is RFC
> > > only and has only been build-tested thus far ...
>
> Gentle ping on this RFC, happy to update and re-submit whenever.
It's in my queue at least, but so are many other things at the moment.
--
paul-moore.com
^ permalink raw reply
* Re: [PATCH] pid: use ns_capable_noaudit() when determining net sysctl permissions
From: Serge E. Hallyn @ 2025-09-11 23:05 UTC (permalink / raw)
To: Paul Moore
Cc: Christian Göttsche, linux-kernel, Christian Göttsche,
Christian Brauner, linux-security-module, selinux
In-Reply-To: <23663be0b8dc2a435bcc46a3d52e9e19@paul-moore.com>
On Thu, Sep 11, 2025 at 04:46:20PM -0400, Paul Moore wrote:
> On Sep 10, 2025 =?UTF-8?q?Christian=20G=C3=B6ttsche?= <cgoettsche@seltendoof.de> wrote:
> >
> > The capability check should not be audited since it is only being used
> > to determine the inode permissions. A failed check does not indicate a
> > violation of security policy but, when an LSM is enabled, a denial audit
> > message was being generated.
> >
> > The denial audit message can either lead to the capability being
> > unnecessarily allowed in a security policy, or being silenced potentially
> > masking a legitimate capability check at a later point in time.
> >
> > Similar to commit d6169b0206db ("net: Use ns_capable_noaudit() when
> > determining net sysctl permissions")
> >
> > Fixes: 7863dcc72d0f ("pid: allow pid_max to be set per pid namespace")
> > CC: Christian Brauner <brauner@kernel.org>
> > CC: linux-security-module@vger.kernel.org
> > CC: selinux@vger.kernel.org
> > Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> > ---
> > kernel/pid.c | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
>
> Reviewed-by: Paul Moore <paul@paul-moore.com>
Thanks.
Acked-by: Serge Hallyn <serge@hallyn.com>
I'll queue this up in the capability tree, unless Christian wanted
it in his.
-serge
^ permalink raw reply
* Re: [RFC PATCH] lsm,selinux: introduce LSM_ATTR_UNSHARE and wire it up for SELinux
From: Serge E. Hallyn @ 2025-09-11 23:02 UTC (permalink / raw)
To: Stephen Smalley
Cc: selinux, linux-security-module, paul, omosnace, john.johansen,
serge, casey
In-Reply-To: <20250903192426.215857-2-stephen.smalley.work@gmail.com>
On Wed, Sep 03, 2025 at 03:24:26PM -0400, Stephen Smalley wrote:
> In the hopes of nudging the conversation in [1] in a more focused
Hi Stephen,
what was [1] supposed to be here? I can think of two possibilities,
but I'm not seeing it inline...
> direction with a goal of getting SELinux namespaces upstreamed, this
> RFC provides a starting point for a concrete discussion. This is RFC
> only and has only been build-tested thus far.
>
> This defines a new LSM_ATTR_UNSHARE attribute for the
> lsm_set_self_attr(2) system call and wires it up for SELinux to invoke
> the underlying function for unsharing the SELinux namespace. As with
> the selinuxfs interface, this immediately unshares the SELinux
> namespace of the current process just like an unshare(2) system call
> would do for other namespaces. I have not yet explored the
> alternatives of deferring the unshare to the next unshare(2),
> clone(2), or execve(2) call and would want to first confirm that doing
> so does not introduce any issues in the kernel or make it harder to
> integrate with existing container runtimes.
>
> Differences between this syscall interface and the selinuxfs interface
> that need discussion before moving forward:
>
> 1. The syscall interface does not currently check any Linux capability
> or DAC permissions, whereas the selinuxfs interface can only be set by
> uid-0 or CAP_DAC_OVERRIDE processes. We need to decide what if any
> capability or DAC check should apply to this syscall interface and if
> any, add the checks to either the LSM framework code or to the SELinux
> hook function.
>
> Pros: Checking a capability or DAC permissions prevents misuse of this
> interface by unprivileged processes, particularly on systems with
> policies that do not yet define any of the new SELinux permissions
> introduced for controlling this operation. This is a potential concern
> on Linux distributions that do not tightly coordinate kernel updates
> with policy updates (or where users may choose to deploy upstream
> kernels on their own), but not on Android.
>
> Cons: Checking a capability or DAC permissions requires any process
> that uses this facility to have the corresponding capability or
> permissions, which might otherwise be unnecessary and create
> additional risks. This is less likely if we use a capability already
> required by container runtimes and similar components that might
> leverage this facility for unsharing SELinux namespaces.
>
> 2. The syscall interface checks a new SELinux unshare_selinuxns
> permission in the process2 class between the task SID and itself,
> similar to other checks for setting process attributes. This means
> that:
> allow domain self:process2 *; -or-
> allow domain self:process2 ~anything-other-than-unshare_selinuxns; -or-
> allow domain self:process2 unshare_selinuxns;
> would allow a process to unshare its SELinux namespace.
>
> The selinuxfs interface checks a new unshare permission in the
> security class between the task SID and the security initial SID,
> likewise similar to other checks for setting selinuxfs attributes.
> This means that:
> allow domain security_t:security *; -or-
> allow domain security_t:security ~anything-other-than-unshare; -or-
> allow domain security_t:security unshare;
> would allow a process to unshare its SELinux namespace.
>
> Technically, the selinuxfs interface also currently requires open and
> write access to the selinuxfs node; hence:
> allow domain security_t:file { open write };
> is also required for the selinuxfs interface.
>
> We need to decide what we want the SELinux check(s) to be for the
> syscall and whether it should be more like the former (process
> attributes) or more like the latter (security policy settings). Note
> that the permission name itself is unimportant here and only differs
> because it seemed less evident in the process2 class that we are
> talking about a SELinux namespace otherwise.
>
> Regardless, either form of allow rule can be prohibited in policies
> via neverallow rules on systems that enforce their usage
> (e.g. Android, not necessarily on Linux distributions).
>
> 3. The selinuxfs interface currently offers more functionality than I
> have implemented here for the sycall interface, including:
>
> a) the ability to read the selinuxfs node to see if your namespace has
> been unshared, which should be easily implementable via
> lsm_get_self_attr(2). However, questions remain as to when that
> should return 1 versus 0 (currently returns 1 whenever your namespace
> is NOT the initial SELinux namespace, useful for the testsuite to
> detect it is in a child, but could instead be reset to 0 by a
> subsequent policy load to indicate completion of the setup of the
> namespace, thus hiding from child processes that they are in a child
> namespace once its policy has been loaded).
>
> b) the abilities to get and set the maximum number of SELinux
> namespaces (via a /sys/fs/selinux/maxns node) and to get and set the
> maximum depth for SELinux namespaces (via a /sys/fs/selinux/maxnsdepth
> node). These could be left in selinuxfs or migrated to some other LSM
> management APIs since they are global in scope, not per-process
> attributes.
>
> Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
> ---
> include/uapi/linux/lsm.h | 1 +
> security/selinux/hooks.c | 8 ++++++++
> security/selinux/include/classmap.h | 4 +++-
> 3 files changed, 12 insertions(+), 1 deletion(-)
>
> diff --git a/include/uapi/linux/lsm.h b/include/uapi/linux/lsm.h
> index 938593dfd5da..fb1b4a8aa639 100644
> --- a/include/uapi/linux/lsm.h
> +++ b/include/uapi/linux/lsm.h
> @@ -83,6 +83,7 @@ struct lsm_ctx {
> #define LSM_ATTR_KEYCREATE 103
> #define LSM_ATTR_PREV 104
> #define LSM_ATTR_SOCKCREATE 105
> +#define LSM_ATTR_UNSHARE 106
>
> /*
> * LSM_FLAG_XXX definitions identify special handling instructions
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index c544b3e2fd5c..11b0b3c5b74a 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -6812,6 +6812,10 @@ static int selinux_lsm_setattr(u64 attr, void *value, size_t size)
> error = avc_has_perm(state, mysid, mysid, SECCLASS_PROCESS,
> PROCESS__SETCURRENT, NULL);
> break;
> + case LSM_ATTR_UNSHARE:
> + error = avc_has_perm(state, mysid, mysid, SECCLASS_PROCESS,
> + PROCESS2__UNSHARE_SELINUXNS, NULL);
> + break;
> default:
> error = -EOPNOTSUPP;
> break;
> @@ -6923,6 +6927,10 @@ static int selinux_lsm_setattr(u64 attr, void *value, size_t size)
> }
>
> tsec->sid = sid;
> + } else if (attr == LSM_ATTR_UNSHARE) {
> + error = selinux_state_create(new);
> + if (error)
> + goto abort_change;
> } else {
> error = -EINVAL;
> goto abort_change;
> diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
> index be52ebb6b94a..07fe316308cd 100644
> --- a/security/selinux/include/classmap.h
> +++ b/security/selinux/include/classmap.h
> @@ -60,7 +60,9 @@ const struct security_class_mapping secclass_map[] = {
> "siginh", "setrlimit", "rlimitinh", "dyntransition",
> "setcurrent", "execmem", "execstack", "execheap",
> "setkeycreate", "setsockcreate", "getrlimit", NULL } },
> - { "process2", { "nnp_transition", "nosuid_transition", NULL } },
> + { "process2",
> + { "nnp_transition", "nosuid_transition", "unshare_selinuxns",
> + NULL } },
> { "system",
> { "ipc_info", "syslog_read", "syslog_mod", "syslog_console",
> "module_request", "module_load", "firmware_load",
> --
> 2.51.0
^ permalink raw reply
* Re: [RFC PATCH] lsm,selinux: introduce LSM_ATTR_UNSHARE and wire it up for SELinux
From: Paul Moore @ 2025-09-12 1:48 UTC (permalink / raw)
To: Serge E. Hallyn
Cc: Stephen Smalley, selinux, linux-security-module, omosnace,
john.johansen, casey
In-Reply-To: <aMNVDOCjCBZZE8Kb@mail.hallyn.com>
On Thu, Sep 11, 2025 at 7:02 PM Serge E. Hallyn <serge@hallyn.com> wrote:
> On Wed, Sep 03, 2025 at 03:24:26PM -0400, Stephen Smalley wrote:
> > In the hopes of nudging the conversation in [1] in a more focused
>
> Hi Stephen,
>
> what was [1] supposed to be here? I can think of two possibilities,
> but I'm not seeing it inline...
I'm guessing Stephen was talking about the "LSM namespacing API" thread:
https://lore.kernel.org/linux-security-module/CAHC9VhRGMmhxbajwQNfGFy+ZFF1uN=UEBjqQZQ4UBy7yds3eVQ@mail.gmail.com/
--
paul-moore.com
^ permalink raw reply
* [PATCH] rcu: Remove redundant rcu_read_lock/unlock() in spin_lock critical sections
From: pengdonglin @ 2025-09-12 6:50 UTC (permalink / raw)
To: tj, tony.luck, jani.nikula, ap420073, jv, freude, bcrl, trondmy,
longman, kees
Cc: bigeasy, linux-kernel, linux-rt-devel, linux-nfs, linux-aio,
linux-fsdevel, linux-security-module, netdev, intel-gfx,
linux-acpi, linux-s390, cgroups, pengdonglin, pengdonglin
From: pengdonglin <pengdonglin@xiaomi.com>
When CONFIG_PREEMPT_RT is disabled, spin_lock*() operations implicitly
disable preemption, which provides RCU read-side protection. When
CONFIG_PREEMPT_RT is enabled, spin_lock*() implementations internally
manage RCU read-side critical sections.
Thus, explicit rcu_read_lock()/rcu_read_unlock() calls within spin_lock*()
critical sections are redundant in both configurations. This patch removes
these unnecessary operations, simplifying the locking semantics while
maintaining correct RCU protection.
Signed-off-by: pengdonglin <pengdonglin@xiaomi.com>
Signed-off-by: pengdonglin <dolinux.peng@gmail.com>
---
drivers/acpi/apei/ghes.c | 2 --
drivers/gpu/drm/i915/gt/intel_ring_submission.c | 2 --
drivers/net/amt.c | 6 ------
drivers/net/bonding/bond_3ad.c | 2 --
drivers/net/wireless/ath/ath9k/xmit.c | 2 --
drivers/s390/crypto/pkey_base.c | 3 ---
fs/aio.c | 2 --
fs/nfs/callback_proc.c | 2 --
fs/nfs/nfs4state.c | 2 --
fs/nfs/pnfs.c | 9 ---------
fs/nfs/pnfs_dev.c | 4 ----
ipc/msg.c | 1 -
ipc/sem.c | 1 -
ipc/shm.c | 1 -
ipc/util.c | 2 --
kernel/cgroup/cgroup.c | 2 --
kernel/cgroup/cpuset.c | 6 ------
kernel/cgroup/debug.c | 4 ----
net/mac80211/cfg.c | 2 --
net/mac80211/debugfs.c | 2 --
net/mac80211/debugfs_netdev.c | 2 --
net/mac80211/debugfs_sta.c | 2 --
net/mac80211/sta_info.c | 2 --
net/ncsi/ncsi-manage.c | 2 --
security/yama/yama_lsm.c | 4 ----
25 files changed, 69 deletions(-)
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index a0d54993edb3..97ee19f2cae0 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -1207,12 +1207,10 @@ static int ghes_notify_hed(struct notifier_block *this, unsigned long event,
int ret = NOTIFY_DONE;
spin_lock_irqsave(&ghes_notify_lock_irq, flags);
- rcu_read_lock();
list_for_each_entry_rcu(ghes, &ghes_hed, list) {
if (!ghes_proc(ghes))
ret = NOTIFY_OK;
}
- rcu_read_unlock();
spin_unlock_irqrestore(&ghes_notify_lock_irq, flags);
return ret;
diff --git a/drivers/gpu/drm/i915/gt/intel_ring_submission.c b/drivers/gpu/drm/i915/gt/intel_ring_submission.c
index 2a6d79abf25b..bf73166a1337 100644
--- a/drivers/gpu/drm/i915/gt/intel_ring_submission.c
+++ b/drivers/gpu/drm/i915/gt/intel_ring_submission.c
@@ -392,14 +392,12 @@ static void reset_rewind(struct intel_engine_cs *engine, bool stalled)
rq = NULL;
spin_lock_irqsave(&engine->sched_engine->lock, flags);
- rcu_read_lock();
list_for_each_entry(pos, &engine->sched_engine->requests, sched.link) {
if (!__i915_request_is_complete(pos)) {
rq = pos;
break;
}
}
- rcu_read_unlock();
/*
* The guilty request will get skipped on a hung engine.
diff --git a/drivers/net/amt.c b/drivers/net/amt.c
index ed86537b2f61..e7cce735ce7b 100644
--- a/drivers/net/amt.c
+++ b/drivers/net/amt.c
@@ -295,7 +295,6 @@ static void amt_source_work(struct work_struct *work)
tunnel = gnode->tunnel_list;
spin_lock_bh(&tunnel->lock);
- rcu_read_lock();
if (gnode->filter_mode == MCAST_INCLUDE) {
amt_destroy_source(snode);
if (!gnode->nr_sources)
@@ -306,7 +305,6 @@ static void amt_source_work(struct work_struct *work)
*/
snode->status = AMT_SOURCE_STATUS_D_FWD;
}
- rcu_read_unlock();
spin_unlock_bh(&tunnel->lock);
}
@@ -1327,11 +1325,9 @@ static void amt_clear_groups(struct amt_tunnel_list *tunnel)
int i;
spin_lock_bh(&tunnel->lock);
- rcu_read_lock();
for (i = 0; i < amt->hash_buckets; i++)
hlist_for_each_entry_safe(gnode, t, &tunnel->groups[i], node)
amt_del_group(amt, gnode);
- rcu_read_unlock();
spin_unlock_bh(&tunnel->lock);
}
@@ -1343,11 +1339,9 @@ static void amt_tunnel_expire(struct work_struct *work)
struct amt_dev *amt = tunnel->amt;
spin_lock_bh(&amt->lock);
- rcu_read_lock();
list_del_rcu(&tunnel->list);
amt->nr_tunnels--;
amt_clear_groups(tunnel);
- rcu_read_unlock();
spin_unlock_bh(&amt->lock);
kfree_rcu(tunnel, rcu);
}
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index 4edc8e6b6b64..c53ea73f103a 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -2485,7 +2485,6 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
* concurrently due to incoming LACPDU as well.
*/
spin_lock_bh(&bond->mode_lock);
- rcu_read_lock();
/* check if there are any slaves */
if (!bond_has_slaves(bond))
@@ -2537,7 +2536,6 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
break;
}
}
- rcu_read_unlock();
spin_unlock_bh(&bond->mode_lock);
if (update_slave_arr)
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 0ac9212e42f7..4a0f465aa2fe 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1993,7 +1993,6 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
ieee80211_txq_schedule_start(hw, txq->mac80211_qnum);
spin_lock_bh(&sc->chan_lock);
- rcu_read_lock();
if (sc->cur_chan->stopped)
goto out;
@@ -2011,7 +2010,6 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
}
out:
- rcu_read_unlock();
spin_unlock_bh(&sc->chan_lock);
ieee80211_txq_schedule_end(hw, txq->mac80211_qnum);
}
diff --git a/drivers/s390/crypto/pkey_base.c b/drivers/s390/crypto/pkey_base.c
index b15741461a63..4c4a9feecccc 100644
--- a/drivers/s390/crypto/pkey_base.c
+++ b/drivers/s390/crypto/pkey_base.c
@@ -48,16 +48,13 @@ int pkey_handler_register(struct pkey_handler *handler)
spin_lock(&handler_list_write_lock);
- rcu_read_lock();
list_for_each_entry_rcu(h, &handler_list, list) {
if (h == handler) {
- rcu_read_unlock();
spin_unlock(&handler_list_write_lock);
module_put(handler->module);
return -EEXIST;
}
}
- rcu_read_unlock();
list_add_rcu(&handler->list, &handler_list);
spin_unlock(&handler_list_write_lock);
diff --git a/fs/aio.c b/fs/aio.c
index 7fc7b6221312..9c47f09df19e 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -359,7 +359,6 @@ static int aio_ring_mremap(struct vm_area_struct *vma)
int i, res = -EINVAL;
spin_lock(&mm->ioctx_lock);
- rcu_read_lock();
table = rcu_dereference(mm->ioctx_table);
if (!table)
goto out_unlock;
@@ -378,7 +377,6 @@ static int aio_ring_mremap(struct vm_area_struct *vma)
}
out_unlock:
- rcu_read_unlock();
spin_unlock(&mm->ioctx_lock);
return res;
}
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
index 8397c43358bd..16144db39335 100644
--- a/fs/nfs/callback_proc.c
+++ b/fs/nfs/callback_proc.c
@@ -721,7 +721,6 @@ __be32 nfs4_callback_offload(void *data, void *dummy,
return cpu_to_be32(NFS4ERR_DELAY);
spin_lock(&cps->clp->cl_lock);
- rcu_read_lock();
list_for_each_entry_rcu(server, &cps->clp->cl_superblocks,
client_link) {
list_for_each_entry(tmp_copy, &server->ss_copies, copies) {
@@ -736,7 +735,6 @@ __be32 nfs4_callback_offload(void *data, void *dummy,
}
}
out:
- rcu_read_unlock();
if (!found) {
memcpy(©->stateid, &args->coa_stateid, NFS4_STATEID_SIZE);
nfs4_copy_cb_args(copy, args);
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index 7612e977e80b..598229fc07ed 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -241,13 +241,11 @@ const struct cred *nfs4_get_renew_cred(struct nfs_client *clp)
goto out;
spin_lock(&clp->cl_lock);
- rcu_read_lock();
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
cred = nfs4_get_renew_cred_server_locked(server);
if (cred != NULL)
break;
}
- rcu_read_unlock();
spin_unlock(&clp->cl_lock);
out:
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
index a3135b5af7ee..c9399452bcfd 100644
--- a/fs/nfs/pnfs.c
+++ b/fs/nfs/pnfs.c
@@ -862,16 +862,13 @@ pnfs_layout_bulk_destroy_byserver_locked(struct nfs_client *clp,
if (pnfs_layout_add_bulk_destroy_list(inode,
layout_list))
continue;
- rcu_read_unlock();
spin_unlock(&clp->cl_lock);
iput(inode);
} else {
- rcu_read_unlock();
spin_unlock(&clp->cl_lock);
}
nfs_sb_deactive(server->super);
spin_lock(&clp->cl_lock);
- rcu_read_lock();
return -EAGAIN;
}
return 0;
@@ -922,7 +919,6 @@ int pnfs_layout_destroy_byfsid(struct nfs_client *clp, struct nfs_fsid *fsid,
LIST_HEAD(layout_list);
spin_lock(&clp->cl_lock);
- rcu_read_lock();
restart:
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
if (memcmp(&server->fsid, fsid, sizeof(*fsid)) != 0)
@@ -932,7 +928,6 @@ int pnfs_layout_destroy_byfsid(struct nfs_client *clp, struct nfs_fsid *fsid,
&layout_list) != 0)
goto restart;
}
- rcu_read_unlock();
spin_unlock(&clp->cl_lock);
return pnfs_layout_free_bulk_destroy_list(&layout_list, mode);
@@ -944,14 +939,12 @@ static void pnfs_layout_build_destroy_list_byclient(struct nfs_client *clp,
struct nfs_server *server;
spin_lock(&clp->cl_lock);
- rcu_read_lock();
restart:
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
if (pnfs_layout_bulk_destroy_byserver_locked(clp, server,
list) != 0)
goto restart;
}
- rcu_read_unlock();
spin_unlock(&clp->cl_lock);
}
@@ -990,7 +983,6 @@ static void pnfs_layout_build_recover_list_byclient(struct nfs_client *clp,
struct nfs_server *server;
spin_lock(&clp->cl_lock);
- rcu_read_lock();
restart:
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
if (!(server->caps & NFS_CAP_REBOOT_LAYOUTRETURN))
@@ -999,7 +991,6 @@ static void pnfs_layout_build_recover_list_byclient(struct nfs_client *clp,
list) != 0)
goto restart;
}
- rcu_read_unlock();
spin_unlock(&clp->cl_lock);
}
diff --git a/fs/nfs/pnfs_dev.c b/fs/nfs/pnfs_dev.c
index bf0f2d67e96c..d19752ec1a95 100644
--- a/fs/nfs/pnfs_dev.c
+++ b/fs/nfs/pnfs_dev.c
@@ -231,9 +231,7 @@ nfs4_delete_deviceid(const struct pnfs_layoutdriver_type *ld,
struct nfs4_deviceid_node *d;
spin_lock(&nfs4_deviceid_lock);
- rcu_read_lock();
d = _lookup_deviceid(ld, clp, id, nfs4_deviceid_hash(id));
- rcu_read_unlock();
if (!d) {
spin_unlock(&nfs4_deviceid_lock);
return;
@@ -331,14 +329,12 @@ _deviceid_purge_client(const struct nfs_client *clp, long hash)
HLIST_HEAD(tmp);
spin_lock(&nfs4_deviceid_lock);
- rcu_read_lock();
hlist_for_each_entry_rcu(d, &nfs4_deviceid_cache[hash], node)
if (d->nfs_client == clp && atomic_read(&d->ref)) {
hlist_del_init_rcu(&d->node);
hlist_add_head(&d->tmpnode, &tmp);
clear_bit(NFS_DEVICEID_NOCACHE, &d->flags);
}
- rcu_read_unlock();
spin_unlock(&nfs4_deviceid_lock);
if (hlist_empty(&tmp))
diff --git a/ipc/msg.c b/ipc/msg.c
index ee6af4fe52bf..1e579b57023f 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -179,7 +179,6 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params)
}
ipc_unlock_object(&msq->q_perm);
- rcu_read_unlock();
return msq->q_perm.id;
}
diff --git a/ipc/sem.c b/ipc/sem.c
index a39cdc7bf88f..38ad57b2b558 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -579,7 +579,6 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
ns->used_sems += nsems;
sem_unlock(sma, -1);
- rcu_read_unlock();
return sma->sem_perm.id;
}
diff --git a/ipc/shm.c b/ipc/shm.c
index a9310b6dbbc3..61fae1b6a18e 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -795,7 +795,6 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
error = shp->shm_perm.id;
ipc_unlock_object(&shp->shm_perm);
- rcu_read_unlock();
return error;
no_id:
diff --git a/ipc/util.c b/ipc/util.c
index cae60f11d9c2..1be691b5dcad 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -293,7 +293,6 @@ int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int limit)
idr_preload(GFP_KERNEL);
spin_lock_init(&new->lock);
- rcu_read_lock();
spin_lock(&new->lock);
current_euid_egid(&euid, &egid);
@@ -316,7 +315,6 @@ int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int limit)
if (idx < 0) {
new->deleted = true;
spin_unlock(&new->lock);
- rcu_read_unlock();
return idx;
}
diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
index 312c6a8b55bb..db9e00a559df 100644
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -2944,14 +2944,12 @@ int cgroup_attach_task(struct cgroup *dst_cgrp, struct task_struct *leader,
/* look up all src csets */
spin_lock_irq(&css_set_lock);
- rcu_read_lock();
task = leader;
do {
cgroup_migrate_add_src(task_css_set(task), dst_cgrp, &mgctx);
if (!threadgroup)
break;
} while_each_thread(leader, task);
- rcu_read_unlock();
spin_unlock_irq(&css_set_lock);
/* prepare dst csets and commit */
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 27adb04df675..9b7e8e8e9411 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -4073,7 +4073,6 @@ void cpuset_cpus_allowed(struct task_struct *tsk, struct cpumask *pmask)
struct cpuset *cs;
spin_lock_irqsave(&callback_lock, flags);
- rcu_read_lock();
cs = task_cs(tsk);
if (cs != &top_cpuset)
@@ -4095,7 +4094,6 @@ void cpuset_cpus_allowed(struct task_struct *tsk, struct cpumask *pmask)
cpumask_copy(pmask, possible_mask);
}
- rcu_read_unlock();
spin_unlock_irqrestore(&callback_lock, flags);
}
@@ -4168,9 +4166,7 @@ nodemask_t cpuset_mems_allowed(struct task_struct *tsk)
unsigned long flags;
spin_lock_irqsave(&callback_lock, flags);
- rcu_read_lock();
guarantee_online_mems(task_cs(tsk), &mask);
- rcu_read_unlock();
spin_unlock_irqrestore(&callback_lock, flags);
return mask;
@@ -4265,10 +4261,8 @@ bool cpuset_current_node_allowed(int node, gfp_t gfp_mask)
/* Not hardwall and node outside mems_allowed: scan up cpusets */
spin_lock_irqsave(&callback_lock, flags);
- rcu_read_lock();
cs = nearest_hardwall_ancestor(task_cs(current));
allowed = node_isset(node, cs->mems_allowed);
- rcu_read_unlock();
spin_unlock_irqrestore(&callback_lock, flags);
return allowed;
diff --git a/kernel/cgroup/debug.c b/kernel/cgroup/debug.c
index 80aa3f027ac3..81ea38dd6f9d 100644
--- a/kernel/cgroup/debug.c
+++ b/kernel/cgroup/debug.c
@@ -49,7 +49,6 @@ static int current_css_set_read(struct seq_file *seq, void *v)
return -ENODEV;
spin_lock_irq(&css_set_lock);
- rcu_read_lock();
cset = task_css_set(current);
refcnt = refcount_read(&cset->refcount);
seq_printf(seq, "css_set %pK %d", cset, refcnt);
@@ -67,7 +66,6 @@ static int current_css_set_read(struct seq_file *seq, void *v)
seq_printf(seq, "%2d: %-4s\t- %p[%d]\n", ss->id, ss->name,
css, css->id);
}
- rcu_read_unlock();
spin_unlock_irq(&css_set_lock);
cgroup_kn_unlock(of->kn);
return 0;
@@ -95,7 +93,6 @@ static int current_css_set_cg_links_read(struct seq_file *seq, void *v)
return -ENOMEM;
spin_lock_irq(&css_set_lock);
- rcu_read_lock();
cset = task_css_set(current);
list_for_each_entry(link, &cset->cgrp_links, cgrp_link) {
struct cgroup *c = link->cgrp;
@@ -104,7 +101,6 @@ static int current_css_set_cg_links_read(struct seq_file *seq, void *v)
seq_printf(seq, "Root %d group %s\n",
c->root->hierarchy_id, name_buf);
}
- rcu_read_unlock();
spin_unlock_irq(&css_set_lock);
kfree(name_buf);
return 0;
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 2ed07fa121ab..4fe50d4c461d 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -4825,7 +4825,6 @@ static int ieee80211_get_txq_stats(struct wiphy *wiphy,
int ret = 0;
spin_lock_bh(&local->fq.lock);
- rcu_read_lock();
if (wdev) {
sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
@@ -4851,7 +4850,6 @@ static int ieee80211_get_txq_stats(struct wiphy *wiphy,
}
out:
- rcu_read_unlock();
spin_unlock_bh(&local->fq.lock);
return ret;
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index e8b78ec682da..82099f4cedbe 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -82,7 +82,6 @@ static ssize_t aqm_read(struct file *file,
int len = 0;
spin_lock_bh(&local->fq.lock);
- rcu_read_lock();
len = scnprintf(buf, sizeof(buf),
"access name value\n"
@@ -105,7 +104,6 @@ static ssize_t aqm_read(struct file *file,
fq->limit,
fq->quantum);
- rcu_read_unlock();
spin_unlock_bh(&local->fq.lock);
return simple_read_from_buffer(user_buf, count, ppos,
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index 1dac78271045..30a5a978a678 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -625,7 +625,6 @@ static ssize_t ieee80211_if_fmt_aqm(
txqi = to_txq_info(sdata->vif.txq);
spin_lock_bh(&local->fq.lock);
- rcu_read_lock();
len = scnprintf(buf,
buflen,
@@ -642,7 +641,6 @@ static ssize_t ieee80211_if_fmt_aqm(
txqi->tin.tx_bytes,
txqi->tin.tx_packets);
- rcu_read_unlock();
spin_unlock_bh(&local->fq.lock);
return len;
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 49061bd4151b..ef75255d47d5 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -148,7 +148,6 @@ static ssize_t sta_aqm_read(struct file *file, char __user *userbuf,
return -ENOMEM;
spin_lock_bh(&local->fq.lock);
- rcu_read_lock();
p += scnprintf(p,
bufsz + buf - p,
@@ -178,7 +177,6 @@ static ssize_t sta_aqm_read(struct file *file, char __user *userbuf,
test_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) ? " DIRTY" : "");
}
- rcu_read_unlock();
spin_unlock_bh(&local->fq.lock);
rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 8c550aab9bdc..663318a75d7f 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -2637,13 +2637,11 @@ static void sta_set_tidstats(struct sta_info *sta,
if (link_id < 0 && tid < IEEE80211_NUM_TIDS) {
spin_lock_bh(&local->fq.lock);
- rcu_read_lock();
tidstats->filled |= BIT(NL80211_TID_STATS_TXQ_STATS);
ieee80211_fill_txq_stats(&tidstats->txq_stats,
to_txq_info(sta->sta.txq[tid]));
- rcu_read_unlock();
spin_unlock_bh(&local->fq.lock);
}
}
diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c
index 446e4e3b9553..6e36cd64a31e 100644
--- a/net/ncsi/ncsi-manage.c
+++ b/net/ncsi/ncsi-manage.c
@@ -650,7 +650,6 @@ static int set_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
spin_lock_irqsave(&nc->lock, flags);
- rcu_read_lock();
list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) {
vid = vlan->vid;
for (i = 0; i < ncf->n_vids; i++)
@@ -661,7 +660,6 @@ static int set_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
if (vid)
break;
}
- rcu_read_unlock();
if (!vid) {
/* No VLAN ID is not set */
diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c
index 3d064dd4e03f..60d38deb181b 100644
--- a/security/yama/yama_lsm.c
+++ b/security/yama/yama_lsm.c
@@ -117,14 +117,12 @@ static void yama_relation_cleanup(struct work_struct *work)
struct ptrace_relation *relation;
spin_lock(&ptracer_relations_lock);
- rcu_read_lock();
list_for_each_entry_rcu(relation, &ptracer_relations, node) {
if (relation->invalid) {
list_del_rcu(&relation->node);
kfree_rcu(relation, rcu);
}
}
- rcu_read_unlock();
spin_unlock(&ptracer_relations_lock);
}
@@ -152,7 +150,6 @@ static int yama_ptracer_add(struct task_struct *tracer,
added->invalid = false;
spin_lock(&ptracer_relations_lock);
- rcu_read_lock();
list_for_each_entry_rcu(relation, &ptracer_relations, node) {
if (relation->invalid)
continue;
@@ -166,7 +163,6 @@ static int yama_ptracer_add(struct task_struct *tracer,
list_add_rcu(&added->node, &ptracer_relations);
out:
- rcu_read_unlock();
spin_unlock(&ptracer_relations_lock);
return 0;
}
--
2.34.1
^ permalink raw reply related
* Re: [RFC PATCH] lsm,selinux: introduce LSM_ATTR_UNSHARE and wire it up for SELinux
From: Stephen Smalley @ 2025-09-12 12:23 UTC (permalink / raw)
To: Paul Moore
Cc: Serge E. Hallyn, selinux, linux-security-module, omosnace,
john.johansen, casey
In-Reply-To: <CAHC9VhQYr_3WzG__RYs_mPtMqFCQz4wbrUnyGZeyKjCuEO93_Q@mail.gmail.com>
On Thu, Sep 11, 2025 at 9:48 PM Paul Moore <paul@paul-moore.com> wrote:
>
> On Thu, Sep 11, 2025 at 7:02 PM Serge E. Hallyn <serge@hallyn.com> wrote:
> > On Wed, Sep 03, 2025 at 03:24:26PM -0400, Stephen Smalley wrote:
> > > In the hopes of nudging the conversation in [1] in a more focused
> >
> > Hi Stephen,
> >
> > what was [1] supposed to be here? I can think of two possibilities,
> > but I'm not seeing it inline...
>
> I'm guessing Stephen was talking about the "LSM namespacing API" thread:
>
> https://lore.kernel.org/linux-security-module/CAHC9VhRGMmhxbajwQNfGFy+ZFF1uN=UEBjqQZQ4UBy7yds3eVQ@mail.gmail.com/
Yes, that was the link I intended to include in my message.
Most of that thread isn't necessary though to reviewing this RFC.
^ permalink raw reply
* Re: [PATCH v3 06/12] bpf: Return hashes of maps in BPF_OBJ_GET_INFO_BY_FD
From: KP Singh @ 2025-09-12 13:36 UTC (permalink / raw)
To: Andrii Nakryiko
Cc: bpf, linux-security-module, bboscaccy, paul, kys, ast, daniel,
andrii
In-Reply-To: <CAEf4BzaXA9R4_tJtA6jsVc3im9LJWhzRGQoVyGjFnH89ohZbcw@mail.gmail.com>
On Thu, Aug 14, 2025 at 8:46 PM Andrii Nakryiko
<andrii.nakryiko@gmail.com> wrote:
>
> On Wed, Aug 13, 2025 at 1:55 PM KP Singh <kpsingh@kernel.org> wrote:
> >
> > Currently only array maps are supported, but the implementation can be
> > extended for other maps and objects. The hash is memoized only for
> > exclusive and frozen maps as their content is stable until the exclusive
> > program modifies the map.
> >
> > This is required for BPF signing, enabling a trusted loader program to
> > verify a map's integrity. The loader retrieves
> > the map's runtime hash from the kernel and compares it against an
> > expected hash computed at build time.
> >
> > Signed-off-by: KP Singh <kpsingh@kernel.org>
> > ---
> > include/linux/bpf.h | 3 +++
> > include/uapi/linux/bpf.h | 2 ++
> > kernel/bpf/arraymap.c | 13 +++++++++++
> > kernel/bpf/syscall.c | 23 +++++++++++++++++++
> > tools/include/uapi/linux/bpf.h | 2 ++
> > .../selftests/bpf/progs/verifier_map_ptr.c | 7 ++++--
> > 6 files changed, 48 insertions(+), 2 deletions(-)
> >
>
> [...]
>
> > struct bpf_btf_info {
> > diff --git a/tools/testing/selftests/bpf/progs/verifier_map_ptr.c b/tools/testing/selftests/bpf/progs/verifier_map_ptr.c
> > index 11a079145966..e2767d27d8aa 100644
> > --- a/tools/testing/selftests/bpf/progs/verifier_map_ptr.c
> > +++ b/tools/testing/selftests/bpf/progs/verifier_map_ptr.c
> > @@ -70,10 +70,13 @@ __naked void bpf_map_ptr_write_rejected(void)
> > : __clobber_all);
> > }
> >
> > +/* The first element of struct bpf_map is a SHA256 hash of 32 bytes, accessing
> > + * into this array is valid. The opts field is now at offset 33.
> > + */
>
> Does hash have to be at the beginning of struct bpf_map? why not just
> put it at the end and not have to adjust any tests?.. (which now will
> fail on older kernel for no good reason, unless I miss something)
It has to be on the top, see the explanation / the code we generate
for verifying the hash it reads from the const_ptr_to_map.
- KP
>
>
> > SEC("socket")
> > __description("bpf_map_ptr: read non-existent field rejected")
> > __failure
> > -__msg("cannot access ptr member ops with moff 0 in struct bpf_map with off 1 size 4")
> > +__msg("cannot access ptr member ops with moff 32 in struct bpf_map with off 33 size 4")
> > __failure_unpriv
> > __msg_unpriv("access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN")
> > __flag(BPF_F_ANY_ALIGNMENT)
> > @@ -82,7 +85,7 @@ __naked void read_non_existent_field_rejected(void)
> > asm volatile (" \
> > r6 = 0; \
> > r1 = %[map_array_48b] ll; \
> > - r6 = *(u32*)(r1 + 1); \
> > + r6 = *(u32*)(r1 + 33); \
> > r0 = 1; \
> > exit; \
> > " :
> > --
> > 2.43.0
> >
^ permalink raw reply
* Re: [PATCH v3 31/34] ima,evm: move initcalls to the LSM framework
From: Mimi Zohar @ 2025-09-12 16:37 UTC (permalink / raw)
To: Paul Moore
Cc: Roberto Sassu, linux-security-module, linux-integrity, selinux,
John Johansen, Fan Wu, Mickaël Salaün,
Günther Noack, Kees Cook, Micah Morton, Casey Schaufler,
Tetsuo Handa, Nicolas Bouchinet, Xiu Jianfeng
In-Reply-To: <CAHC9VhQT8X8UDt2ZbKhA8bVcaNj06sVyTLG0+WyevrTVFpJwtA@mail.gmail.com>
On Thu, 2025-09-11 at 15:30 -0400, Paul Moore wrote:
> On Mon, Sep 8, 2025 at 6:34 PM Mimi Zohar <zohar@linux.ibm.com> wrote:
> > On Sun, 2025-09-07 at 21:05 -0400, Paul Moore wrote:
> > > > The "unrelated to IMA/EVM" wording misses the point. An exception was made to
> > > > load the pre-boot keys onto the .platform keyring in order for IMA/EVM to verify
> > > > the kexec kernel image appended signature. This exception was subsequently
> > > > extended to verifying the pesigned kexec kernel image signature. (Other
> > > > subsystems are abusing the keys on the .platform keyring to verify other
> > > > signatures.)
> > > >
> > > > Instead of saying "unrelated to IMA/EVM", how about saying something along the
> > > > lines of "IMA has a dependency on the platform and machine keyrings, but this
> > > > dependency isn't limited to IMA/EVM."
> > > >
> > > > Paul, this patch set doesn't apply to cleanly to Linus's tree. What is the base
> > > > commit?
> > >
> > > It would have been based on the lsm/dev branch since the LSM tree is
> > > the target, however, given the scope of the patchset and the fact that
> > > it has been several weeks since it was originally posted, I wouldn't
> > > be surprised it if needs some fuzzing when applied on top of lsm/dev
> > > too.
> >
> > Thanks, Paul. I was able to apply the patches and run some regression tests.
>
> Mimi, I know you already tagged Roberto's patch with a 'Reviewed-by'
> tag, but I wanted to follow up and see if you were comfortable
> converting that into an ACK, or if you wanted more time to review
> Roberto's patch? No wrong answers, just trying to understand where
> you are at with this patch.
Please don't convert the Reviewed-by tag quite yet to an Ack. I'd really like
to review the entire patch set and do some additional testing.
thanks,
Mimi
^ permalink raw reply
* Re: [PATCH v3 04/12] libbpf: Support exclusive map creation
From: KP Singh @ 2025-09-12 18:22 UTC (permalink / raw)
To: Andrii Nakryiko
Cc: bpf, linux-security-module, bboscaccy, paul, kys, ast, daniel,
andrii
In-Reply-To: <CAEf4BzbV0UtzrsGm3jbfTWJKRQyu2fqzpcKg_xG8R4+1uEMqnQ@mail.gmail.com>
On Thu, Aug 14, 2025 at 8:46 PM Andrii Nakryiko
<andrii.nakryiko@gmail.com> wrote:
>
> On Wed, Aug 13, 2025 at 1:55 PM KP Singh <kpsingh@kernel.org> wrote:
> >
> > Implement setters and getters that allow map to be registers as
>
> typo: registered
>
> > exclusive to the specified program. The registration should be done
> > before the exclusive program is loaded.
> >
> > Signed-off-by: KP Singh <kpsingh@kernel.org>
> > ---
> > tools/lib/bpf/bpf.c | 4 ++-
> > tools/lib/bpf/bpf.h | 4 ++-
> > tools/lib/bpf/libbpf.c | 66 ++++++++++++++++++++++++++++++++++++++++
> > tools/lib/bpf/libbpf.h | 18 +++++++++++
> > tools/lib/bpf/libbpf.map | 2 ++
> > 5 files changed, 92 insertions(+), 2 deletions(-)
> >
> > diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
> > index ab40dbf9f020..6a08a1559237 100644
> > --- a/tools/lib/bpf/bpf.c
> > +++ b/tools/lib/bpf/bpf.c
> > @@ -172,7 +172,7 @@ int bpf_map_create(enum bpf_map_type map_type,
> > __u32 max_entries,
> > const struct bpf_map_create_opts *opts)
> > {
> > - const size_t attr_sz = offsetofend(union bpf_attr, map_token_fd);
> > + const size_t attr_sz = offsetofend(union bpf_attr, excl_prog_hash);
> > union bpf_attr attr;
> > int fd;
> >
> > @@ -203,6 +203,8 @@ int bpf_map_create(enum bpf_map_type map_type,
> > attr.map_ifindex = OPTS_GET(opts, map_ifindex, 0);
> >
> > attr.map_token_fd = OPTS_GET(opts, token_fd, 0);
> > + attr.excl_prog_hash = ptr_to_u64(OPTS_GET(opts, excl_prog_hash, NULL));
> > + attr.excl_prog_hash_size = OPTS_GET(opts, excl_prog_hash_size, 0);
> >
> > fd = sys_bpf_fd(BPF_MAP_CREATE, &attr, attr_sz);
> > return libbpf_err_errno(fd);
> > diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
> > index 7252150e7ad3..675a09bb7d2f 100644
> > --- a/tools/lib/bpf/bpf.h
> > +++ b/tools/lib/bpf/bpf.h
> > @@ -54,9 +54,11 @@ struct bpf_map_create_opts {
> > __s32 value_type_btf_obj_fd;
> >
> > __u32 token_fd;
> > + __u32 excl_prog_hash_size;
>
> leaving a gap here, can you please reorder and have hash first,
> followed by size?
done
>
> > + const void *excl_prog_hash;
> > size_t :0;
> > };
> > -#define bpf_map_create_opts__last_field token_fd
> > +#define bpf_map_create_opts__last_field excl_prog_hash
> >
> > LIBBPF_API int bpf_map_create(enum bpf_map_type map_type,
> > const char *map_name,
> > diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> > index 0bb3d71dcd9f..ed3294f69271 100644
> > --- a/tools/lib/bpf/libbpf.c
> > +++ b/tools/lib/bpf/libbpf.c
> > @@ -499,6 +499,7 @@ struct bpf_program {
> > __u32 line_info_rec_size;
> > __u32 line_info_cnt;
> > __u32 prog_flags;
> > + __u8 hash[SHA256_DIGEST_LENGTH];
> > };
> >
> > struct bpf_struct_ops {
> > @@ -578,6 +579,7 @@ struct bpf_map {
> > bool autocreate;
> > bool autoattach;
> > __u64 map_extra;
> > + struct bpf_program *excl_prog;
> > };
> >
> > enum extern_type {
> > @@ -4488,6 +4490,43 @@ bpf_object__section_to_libbpf_map_type(const struct bpf_object *obj, int shndx)
> > }
> > }
> >
> > +static int bpf_program__compute_hash(struct bpf_program *prog)
>
> nit: this is not an API, so please don't use double underscores.
> Something like bpf_prog_compute_hash() should do.
ack.
>
> > +{
> > + struct bpf_insn *purged;
> > + int i, err;
> > +
> > + purged = calloc(1, BPF_INSN_SZ * prog->insns_cnt);
>
> we had some patch fixing similar argument misuse issue, so I'd rather
> have calloc(prog->insns_cnt, BPF_INSN_SZ), if you don't mind
>
I don't mind :)
> > + if (!purged)
> > + return -ENOMEM;
> > +
> > + /* If relocations have been done, the map_fd needs to be
> > + * discarded for the digest calculation.
> > + */
> > + for (i = 0; i < prog->insns_cnt; i++) {
> > + purged[i] = prog->insns[i];
> > + if (purged[i].code == (BPF_LD | BPF_IMM | BPF_DW) &&
> > + (purged[i].src_reg == BPF_PSEUDO_MAP_FD ||
> > + purged[i].src_reg == BPF_PSEUDO_MAP_VALUE)) {
> > + purged[i].imm = 0;
> > + i++;
> > + if (i >= prog->insns_cnt ||
> > + prog->insns[i].code != 0 ||
> > + prog->insns[i].dst_reg != 0 ||
> > + prog->insns[i].src_reg != 0 ||
> > + prog->insns[i].off != 0) {
> > + err = -EINVAL;
> > + goto out;
> > + }
> > + purged[i] = prog->insns[i];
> > + purged[i].imm = 0;
> > + }
> > + }
> > + err = libbpf_sha256(purged, prog->insns_cnt * sizeof(struct bpf_insn), prog->hash, SHA256_DIGEST_LENGTH);
>
> too long, wrap before prog->hash?
done
>
> > +out:
> > + free(purged);
> > + return err;
> > +}
> > +
> > static int bpf_program__record_reloc(struct bpf_program *prog,
> > struct reloc_desc *reloc_desc,
> > __u32 insn_idx, const char *sym_name,
> > @@ -5227,6 +5266,18 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
> > create_attr.token_fd = obj->token_fd;
> > if (obj->token_fd)
> > create_attr.map_flags |= BPF_F_TOKEN_FD;
> > + if (map->excl_prog) {
> > + if (map->excl_prog->obj->state == OBJ_LOADED) {
> > + pr_warn("exclusive program already loaded\n");
> > + return libbpf_err(-EINVAL);
> > + }
>
> unnecessary check, maps are always created before programs, so if
> map->excl_prog belongs to the same bpf_object (and it should), then we
> implicitly have a guarantee it's not yet created. So please drop.
Removed.
>
> > + err = bpf_program__compute_hash(map->excl_prog);
> > + if (err)
> > + return err;
> > +
> > + create_attr.excl_prog_hash = map->excl_prog->hash;
> > + create_attr.excl_prog_hash_size = SHA256_DIGEST_LENGTH;
> > + }
> >
> > if (bpf_map__is_struct_ops(map)) {
> > create_attr.btf_vmlinux_value_type_id = map->btf_vmlinux_value_type_id;
> > @@ -10517,6 +10568,21 @@ int bpf_map__set_inner_map_fd(struct bpf_map *map, int fd)
> > return 0;
> > }
> >
> > +int bpf_map__set_exclusive_program(struct bpf_map *map, struct bpf_program *prog)
> > +{
> > + if (map_is_created(map)) {
> > + pr_warn("exclusive programs must be set before map creation\n");
> > + return libbpf_err(-EINVAL);
> > + }
>
> should we worry about someone providing a bpf_program that doesn't
> belong to the same bpf_object that map belongs to? it's easy to check,
> just compare map->obj and prog->obj
Seems logical, added the check.
> > + map->excl_prog = prog;
> > + return 0;
> > +}
> > +
> > +struct bpf_program *bpf_map__get_exclusive_program(struct bpf_map *map)
>
> libbpf getters don't have "get_" prefix, so just bpf_map__exclusive_program()
updated.
>
> > +{
> > + return map->excl_prog;
> > +}
> > +
> > static struct bpf_map *
> > __bpf_map__iter(const struct bpf_map *m, const struct bpf_object *obj, int i)
> > {
> > diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> > index 455a957cb702..ddaf58c8a298 100644
> > --- a/tools/lib/bpf/libbpf.h
> > +++ b/tools/lib/bpf/libbpf.h
> > @@ -1266,7 +1266,25 @@ LIBBPF_API int bpf_map__lookup_and_delete_elem(const struct bpf_map *map,
> > */
> > LIBBPF_API int bpf_map__get_next_key(const struct bpf_map *map,
> > const void *cur_key, void *next_key, size_t key_sz);
> > +/**
> > + * @brief **bpf_map__set_exclusive_program()** sets map to be exclusive to the
> > + * to the specified program. The program must not be loaded yet.
>
> typo: "to the" duplicated
fixed.
>
> Also, I think the more important restriction is that the map should
> not have been created yet (so this has to be called between opening
> and prepare/load steps, just like setting read-only global variables).
> This by implication will mean that the program is not loaded either,
> as we'll restrict bpf_program to be from the same bpf_object (which
> you can mention as well for clarity).
>
How about?
/**
* @brief **bpf_map__set_exclusive_program()** sets a map to be exclusive to the
* specified program. This must be called *before* the map is created.
*
* @param map BPF map to make exclusive.
* @param prog BPF program to be the exclusive user of the map. Must belong
* to the same bpf_object as the map.
* @return 0 on success; a negative error code otherwise.
*
* This function must be called after the BPF object is opened but before
* it is loaded. Once the object is loaded, only the specified program
* will be able to access the map's contents.
*/
> > + * @param map BPF map to make exclusive.
> > + * @param prog BPF program to be the exclusive user of the map.
> > + * @return 0 on success; a negative error code otherwise.
> > + *
> > + * Once a map is made exclusive, only the specified program can access its
> > + * contents.
> > + */
> > +LIBBPF_API int bpf_map__set_exclusive_program(struct bpf_map *map, struct bpf_program *prog);
> >
> > +/**
> > + * @brief **bpf_map__get_exclusive_program()** returns the exclusive program
> > + * that is registered with the map (if any).
> > + * @param map BPF map to which the exclusive program is registered.
> > + * @return the registered exclusive program.
> > + */
> > +LIBBPF_API struct bpf_program *bpf_map__get_exclusive_program(struct bpf_map *map);
> > struct bpf_xdp_set_link_opts {
> > size_t sz;
> > int old_fd;
> > diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> > index d7bd463e7017..a5c5d0f2db5c 100644
> > --- a/tools/lib/bpf/libbpf.map
> > +++ b/tools/lib/bpf/libbpf.map
> > @@ -436,6 +436,8 @@ LIBBPF_1.6.0 {
> > bpf_linker__add_buf;
> > bpf_linker__add_fd;
> > bpf_linker__new_fd;
> > + bpf_map__set_exclusive_program;
> > + bpf_map__get_exclusive_program;
>
> we are in LIBBPF_1.7.0 now, so please move
done
>
> pw-bot: cr
>
>
> > bpf_object__prepare;
> > bpf_prog_stream_read;
> > bpf_program__attach_cgroup_opts;
> > --
> > 2.43.0
> >
^ permalink raw reply
* Re: [PATCH v3 31/34] ima,evm: move initcalls to the LSM framework
From: Paul Moore @ 2025-09-12 18:34 UTC (permalink / raw)
To: Mimi Zohar
Cc: Roberto Sassu, linux-security-module, linux-integrity, selinux,
John Johansen, Fan Wu, Mickaël Salaün,
Günther Noack, Kees Cook, Micah Morton, Casey Schaufler,
Tetsuo Handa, Nicolas Bouchinet, Xiu Jianfeng
In-Reply-To: <f9aceb873648bcc8ba6c07b9b9bd269800f03c14.camel@linux.ibm.com>
On Fri, Sep 12, 2025 at 12:38 PM Mimi Zohar <zohar@linux.ibm.com> wrote:
> On Thu, 2025-09-11 at 15:30 -0400, Paul Moore wrote:
> > On Mon, Sep 8, 2025 at 6:34 PM Mimi Zohar <zohar@linux.ibm.com> wrote:
> > > On Sun, 2025-09-07 at 21:05 -0400, Paul Moore wrote:
> > > > > The "unrelated to IMA/EVM" wording misses the point. An exception was made to
> > > > > load the pre-boot keys onto the .platform keyring in order for IMA/EVM to verify
> > > > > the kexec kernel image appended signature. This exception was subsequently
> > > > > extended to verifying the pesigned kexec kernel image signature. (Other
> > > > > subsystems are abusing the keys on the .platform keyring to verify other
> > > > > signatures.)
> > > > >
> > > > > Instead of saying "unrelated to IMA/EVM", how about saying something along the
> > > > > lines of "IMA has a dependency on the platform and machine keyrings, but this
> > > > > dependency isn't limited to IMA/EVM."
> > > > >
> > > > > Paul, this patch set doesn't apply to cleanly to Linus's tree. What is the base
> > > > > commit?
> > > >
> > > > It would have been based on the lsm/dev branch since the LSM tree is
> > > > the target, however, given the scope of the patchset and the fact that
> > > > it has been several weeks since it was originally posted, I wouldn't
> > > > be surprised it if needs some fuzzing when applied on top of lsm/dev
> > > > too.
> > >
> > > Thanks, Paul. I was able to apply the patches and run some regression tests.
> >
> > Mimi, I know you already tagged Roberto's patch with a 'Reviewed-by'
> > tag, but I wanted to follow up and see if you were comfortable
> > converting that into an ACK, or if you wanted more time to review
> > Roberto's patch? No wrong answers, just trying to understand where
> > you are at with this patch.
>
> Please don't convert the Reviewed-by tag quite yet to an Ack. I'd really like
> to review the entire patch set and do some additional testing.
Yep, no problem, I was waiting on your reply to repost. I have
limited network connectivity for the next several days, so depending
on how things go I may not be able to get a new revision until next
week sometime.
--
paul-moore.com
^ permalink raw reply
* Re: [PATCH v3 09/12] libbpf: Update light skeleton for signing
From: KP Singh @ 2025-09-12 18:39 UTC (permalink / raw)
To: Andrii Nakryiko
Cc: bpf, linux-security-module, bboscaccy, paul, kys, ast, daniel,
andrii
In-Reply-To: <CAEf4BzbYUPLH97fkFQ3oHqKok=OEREyd1VVkmqfVh0rUvX_1sQ@mail.gmail.com>
On Thu, Aug 14, 2025 at 8:46 PM Andrii Nakryiko
<andrii.nakryiko@gmail.com> wrote:
>
> On Wed, Aug 13, 2025 at 1:55 PM KP Singh <kpsingh@kernel.org> wrote:
> >
> > * The metadata map is created with as an exclusive map (with an
> > excl_prog_hash) This restricts map access exclusively to the signed
> > loader program, preventing tampering by other processes.
> >
> > * The map is then frozen, making it read-only from userspace.
> >
> > * BPF_OBJ_GET_INFO_BY_ID instructs the kernel to compute the hash of the
> > metadata map (H') and store it in bpf_map->sha.
> >
> > * The loader is then loaded with the signature which is then verified by
> > the kernel.
> >
> > The sekeleton currently uses the session keyring
> > (KEY_SPEC_SESSION_KEYRING) by default but this can
> > be overridden by the user of the skeleton.
> >
> > loading signed programs prebuilt into the kernel are not currently
> > supported. These can supported by enabling BPF_OBJ_GET_INFO_BY_ID to be
> > called from the kernel.
> >
> > Signed-off-by: KP Singh <kpsingh@kernel.org>
> > ---
> > tools/lib/bpf/skel_internal.h | 75 +++++++++++++++++++++++++++++++++--
> > 1 file changed, 71 insertions(+), 4 deletions(-)
> >
>
> [...]
>
> > +static inline int skel_obj_get_info_by_fd(int fd)
> > +{
> > + const size_t attr_sz = offsetofend(union bpf_attr, info);
> > + __u8 sha[SHA256_DIGEST_LENGTH];
> > + struct bpf_map_info info = {};
>
> memset(0) this instead of relying on = {}
done.
^ permalink raw reply
* Re: [PATCH] pid: use ns_capable_noaudit() when determining net sysctl permissions
From: Serge E. Hallyn @ 2025-09-12 19:35 UTC (permalink / raw)
To: Serge E. Hallyn
Cc: Paul Moore, Christian Göttsche, linux-kernel,
Christian Göttsche, Christian Brauner, linux-security-module,
selinux
In-Reply-To: <aMNVoSM7PauOrCPF@mail.hallyn.com>
On Thu, Sep 11, 2025 at 06:05:05PM -0500, Serge E. Hallyn wrote:
> On Thu, Sep 11, 2025 at 04:46:20PM -0400, Paul Moore wrote:
> > On Sep 10, 2025 =?UTF-8?q?Christian=20G=C3=B6ttsche?= <cgoettsche@seltendoof.de> wrote:
> > >
> > > The capability check should not be audited since it is only being used
> > > to determine the inode permissions. A failed check does not indicate a
> > > violation of security policy but, when an LSM is enabled, a denial audit
> > > message was being generated.
> > >
> > > The denial audit message can either lead to the capability being
> > > unnecessarily allowed in a security policy, or being silenced potentially
> > > masking a legitimate capability check at a later point in time.
> > >
> > > Similar to commit d6169b0206db ("net: Use ns_capable_noaudit() when
> > > determining net sysctl permissions")
> > >
> > > Fixes: 7863dcc72d0f ("pid: allow pid_max to be set per pid namespace")
> > > CC: Christian Brauner <brauner@kernel.org>
> > > CC: linux-security-module@vger.kernel.org
> > > CC: selinux@vger.kernel.org
> > > Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> > > ---
> > > kernel/pid.c | 2 +-
> > > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > Reviewed-by: Paul Moore <paul@paul-moore.com>
>
> Thanks.
>
> Acked-by: Serge Hallyn <serge@hallyn.com>
>
> I'll queue this up in the capability tree, unless Christian wanted
> it in his.
>
> -serge
I've included this in git://git.kernel.org/pub/scm/linux/kernel/git/sergeh/linux.git#caps-next
thanks,
-serge
^ permalink raw reply
* Re: [PATCH] rcu: Remove redundant rcu_read_lock/unlock() in spin_lock critical sections
From: Waiman Long @ 2025-09-12 21:13 UTC (permalink / raw)
To: pengdonglin, tj, tony.luck, jani.nikula, ap420073, jv, freude,
bcrl, trondmy, kees
Cc: bigeasy, linux-kernel, linux-rt-devel, linux-nfs, linux-aio,
linux-fsdevel, linux-security-module, netdev, intel-gfx,
linux-acpi, linux-s390, cgroups, pengdonglin, Paul E . McKenney
In-Reply-To: <20250912065050.460718-1-dolinux.peng@gmail.com>
On 9/12/25 2:50 AM, pengdonglin wrote:
> From: pengdonglin <pengdonglin@xiaomi.com>
>
> When CONFIG_PREEMPT_RT is disabled, spin_lock*() operations implicitly
> disable preemption, which provides RCU read-side protection. When
> CONFIG_PREEMPT_RT is enabled, spin_lock*() implementations internally
> manage RCU read-side critical sections.
I have some doubt about your claim that disabling preemption provides
RCU read-side protection. It is true for some flavors but probably not
all. I do know that disabling interrupt will provide RCU read-side
protection. So for spin_lock_irq*() calls, that is valid. I am not sure
about spin_lock_bh(), maybe it applies there too. we need some RCU
people to confirm.
When CONFIG_PREEMPT_RT is enabled, rt_spin_lock/unlock() will call
rcu_read_lock/_unlock() internally. So eliminating explicit
rcu_read_lock/unlock() in critical sections should be fine.
Cheers,
Longman
^ permalink raw reply
* Re: [PATCH] rcu: Remove redundant rcu_read_lock/unlock() in spin_lock critical sections
From: Sebastian Andrzej Siewior @ 2025-09-12 21:35 UTC (permalink / raw)
To: Waiman Long
Cc: pengdonglin, tj, tony.luck, jani.nikula, ap420073, jv, freude,
bcrl, trondmy, kees, linux-kernel, linux-rt-devel, linux-nfs,
linux-aio, linux-fsdevel, linux-security-module, netdev,
intel-gfx, linux-acpi, linux-s390, cgroups, pengdonglin,
Paul E . McKenney
In-Reply-To: <6831b9fe-402f-40a6-84e6-b723dd006b90@redhat.com>
On 2025-09-12 17:13:09 [-0400], Waiman Long wrote:
> On 9/12/25 2:50 AM, pengdonglin wrote:
> > From: pengdonglin <pengdonglin@xiaomi.com>
> >
> > When CONFIG_PREEMPT_RT is disabled, spin_lock*() operations implicitly
> > disable preemption, which provides RCU read-side protection. When
> > CONFIG_PREEMPT_RT is enabled, spin_lock*() implementations internally
> > manage RCU read-side critical sections.
>
> I have some doubt about your claim that disabling preemption provides RCU
> read-side protection. It is true for some flavors but probably not all. I do
> know that disabling interrupt will provide RCU read-side protection. So for
> spin_lock_irq*() calls, that is valid. I am not sure about spin_lock_bh(),
> maybe it applies there too. we need some RCU people to confirm.
The claim is valid since Paul merged the three flavours we had. Before
that preempt_disable() (and disabling irqs) would match
rcu_read_lock_sched(). rcu_read_lock() and rcu_read_lock_bh() were
different in terms of grace period and clean up.
So _now_ we could remove it if it makes things easier.
> Cheers,
> Longman
Sebastian
^ permalink raw reply
* Re: [syzbot ci] Re: Signed BPF programs
From: KP Singh @ 2025-09-12 22:28 UTC (permalink / raw)
To: syzbot ci
Cc: andrii, ast, bboscaccy, bpf, daniel, kys, linux-security-module,
paul, syzbot, syzkaller-bugs
In-Reply-To: <689eef40.050a0220.e29e5.0010.GAE@google.com>
On Fri, Aug 15, 2025 at 10:26 AM syzbot ci
<syzbot+cic1938c6466797c55@syzkaller.appspotmail.com> wrote:
>
> syzbot ci has tested the following series
>
> [v3] Signed BPF programs
> https://lore.kernel.org/all/20250813205526.2992911-1-kpsingh@kernel.org
> * [PATCH v3 01/12] bpf: Update the bpf_prog_calc_tag to use SHA256
> * [PATCH v3 02/12] bpf: Implement exclusive map creation
> * [PATCH v3 03/12] libbpf: Implement SHA256 internal helper
> * [PATCH v3 04/12] libbpf: Support exclusive map creation
> * [PATCH v3 05/12] selftests/bpf: Add tests for exclusive maps
> * [PATCH v3 06/12] bpf: Return hashes of maps in BPF_OBJ_GET_INFO_BY_FD
> * [PATCH v3 07/12] bpf: Move the signature kfuncs to helpers.c
> * [PATCH v3 08/12] bpf: Implement signature verification for BPF programs
> * [PATCH v3 09/12] libbpf: Update light skeleton for signing
> * [PATCH v3 10/12] libbpf: Embed and verify the metadata hash in the loader
> * [PATCH v3 11/12] bpftool: Add support for signing BPF programs
> * [PATCH v3 12/12] selftests/bpf: Enable signature verification for some lskel tests
>
> and found the following issue:
> general protection fault in bpf_verify_pkcs7_signature
>
> Full report is available here:
> https://ci.syzbot.org/series/67d9a289-da5c-4051-8c3c-cc32b6ccd77d
>
> ***
>
> general protection fault in bpf_verify_pkcs7_signature
>
> tree: bpf-next
> URL: https://kernel.googlesource.com/pub/scm/linux/kernel/git/bpf/bpf-next.git
> base: 07866544e410e4c895a729971e4164861b41fad5
> arch: amd64
> compiler: Debian clang version 20.1.7 (++20250616065708+6146a88f6049-1~exp1~20250616065826.132), Debian LLD 20.1.7
> config: https://ci.syzbot.org/builds/1e87aafb-11dc-48f1-a980-c91551ba52de/config
> C repro: https://ci.syzbot.org/findings/0c329233-09a8-4e8b-9e6e-72f234dd85ab/c_repro
> syz repro: https://ci.syzbot.org/findings/0c329233-09a8-4e8b-9e6e-72f234dd85ab/syz_repro
>
> Oops: general protection fault, probably for non-canonical address 0xdffffc0000000001: 0000 [#1] SMP KASAN PTI
> KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f]
> CPU: 1 UID: 0 PID: 6001 Comm: syz.0.17 Not tainted 6.17.0-rc1-syzkaller-00022-g07866544e410-dirty #0 PREEMPT(full)
> Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014
> RIP: 0010:bpf_verify_pkcs7_signature+0x31/0x190 kernel/bpf/helpers.c:3835
> Code: 41 56 41 55 41 54 53 48 89 d3 49 89 f6 49 89 ff 48 bd 00 00 00 00 00 fc ff df e8 aa b0 e0 ff 4c 8d 63 08 4c 89 e0 48 c1 e8 03 <0f> b6 04 28 84 c0 0f 85 01 01 00 00 41 80 3c 24 00 74 3d 48 89 d8
> RSP: 0018:ffffc90002f7fa08 EFLAGS: 00010202
> RAX: 0000000000000001 RBX: 0000000000000000 RCX: ffff888020c51cc0
> RDX: 0000000000000000 RSI: ffffc90002f7faa0 RDI: ffffc90002f7fac0
> RBP: dffffc0000000000 R08: 0000000000000018 R09: ffffffff820b8a70
> R10: ffffc90002f7fac0 R11: fffff520005eff5a R12: 0000000000000008
> R13: 0000000000000010 R14: ffffc90002f7faa0 R15: ffffc90002f7fac0
> FS: 00005555895fe500(0000) GS:ffff8881a3c1c000(0000) knlGS:0000000000000000
> CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> CR2: 0000001b30b63fff CR3: 0000000028898000 CR4: 00000000000006f0
> Call Trace:
> <TASK>
> bpf_prog_verify_signature+0x2da/0x3b0 kernel/bpf/syscall.c:2815
> bpf_prog_load+0xcc4/0x19e0 kernel/bpf/syscall.c:2989
> __sys_bpf+0x507/0x860 kernel/bpf/syscall.c:6116
> __do_sys_bpf kernel/bpf/syscall.c:6226 [inline]
> __se_sys_bpf kernel/bpf/syscall.c:6224 [inline]
> __x64_sys_bpf+0x7c/0x90 kernel/bpf/syscall.c:6224
> do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
> do_syscall_64+0xfa/0x3b0 arch/x86/entry/syscall_64.c:94
> entry_SYSCALL_64_after_hwframe+0x77/0x7f
> RIP: 0033:0x7f0a4558ebe9
> Code: ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 a8 ff ff ff f7 d8 64 89 01 48
> RSP: 002b:00007fff940250b8 EFLAGS: 00000246 ORIG_RAX: 0000000000000141
> RAX: ffffffffffffffda RBX: 00007f0a457b5fa0 RCX: 00007f0a4558ebe9
> RDX: 00000000000000a8 RSI: 0000200000000140 RDI: 0000000000000005
> RBP: 00007f0a45611e19 R08: 0000000000000000 R09: 0000000000000000
> R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
> R13: 00007f0a457b5fa0 R14: 00007f0a457b5fa0 R15: 0000000000000003
> </TASK>
> Modules linked in:
> ---[ end trace 0000000000000000 ]---
> RIP: 0010:bpf_verify_pkcs7_signature+0x31/0x190 kernel/bpf/helpers.c:3835
> Code: 41 56 41 55 41 54 53 48 89 d3 49 89 f6 49 89 ff 48 bd 00 00 00 00 00 fc ff df e8 aa b0 e0 ff 4c 8d 63 08 4c 89 e0 48 c1 e8 03 <0f> b6 04 28 84 c0 0f 85 01 01 00 00 41 80 3c 24 00 74 3d 48 89 d8
> RSP: 0018:ffffc90002f7fa08 EFLAGS: 00010202
> RAX: 0000000000000001 RBX: 0000000000000000 RCX: ffff888020c51cc0
> RDX: 0000000000000000 RSI: ffffc90002f7faa0 RDI: ffffc90002f7fac0
> RBP: dffffc0000000000 R08: 0000000000000018 R09: ffffffff820b8a70
> R10: ffffc90002f7fac0 R11: fffff520005eff5a R12: 0000000000000008
> R13: 0000000000000010 R14: ffffc90002f7faa0 R15: ffffc90002f7fac0
> FS: 00005555895fe500(0000) GS:ffff8881a3c1c000(0000) knlGS:0000000000000000
> CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> CR2: 0000001b30b63fff CR3: 0000000028898000 CR4: 00000000000006f0
> ----------------
> Code disassembly (best guess):
> 0: 41 56 push %r14
> 2: 41 55 push %r13
> 4: 41 54 push %r12
> 6: 53 push %rbx
> 7: 48 89 d3 mov %rdx,%rbx
> a: 49 89 f6 mov %rsi,%r14
> d: 49 89 ff mov %rdi,%r15
> 10: 48 bd 00 00 00 00 00 movabs $0xdffffc0000000000,%rbp
> 17: fc ff df
> 1a: e8 aa b0 e0 ff call 0xffe0b0c9
> 1f: 4c 8d 63 08 lea 0x8(%rbx),%r12
> 23: 4c 89 e0 mov %r12,%rax
> 26: 48 c1 e8 03 shr $0x3,%rax
> * 2a: 0f b6 04 28 movzbl (%rax,%rbp,1),%eax <-- trapping instruction
> 2e: 84 c0 test %al,%al
> 30: 0f 85 01 01 00 00 jne 0x137
> 36: 41 80 3c 24 00 cmpb $0x0,(%r12)
> 3b: 74 3d je 0x7a
> 3d: 48 89 d8 mov %rbx,%rax
>
>
> ***
>
> If these findings have caused you to resend the series or submit a
> separate fix, please add the following tag to your commit message:
> Tested-by: syzbot@syzkaller.appspotmail.com
>
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 596c0fa7fc90..d0d437f38a70 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -2801,6 +2801,9 @@ static int bpf_prog_verify_signature(struct
bpf_prog *prog, union bpf_attr *attr
else
key = bpf_lookup_user_key(attr->keyring_id, 0);
+ if (!key)
+ return -EINVAL;
+
sig = kvmemdup_bpfptr(usig, attr->signature_size);
if (IS_ERR(sig)) {
bpf_key_put(key);
Thanks syzbot!
- KP
> ---
> This report is generated by a bot. It may contain errors.
> syzbot ci engineers can be reached at syzkaller@googlegroups.com.
^ permalink raw reply related
* Re: [PATCH] rcu: Remove redundant rcu_read_lock/unlock() in spin_lock critical sections
From: Waiman Long @ 2025-09-13 0:33 UTC (permalink / raw)
To: Sebastian Andrzej Siewior, Waiman Long
Cc: pengdonglin, tj, tony.luck, jani.nikula, ap420073, jv, freude,
bcrl, trondmy, kees, linux-kernel, linux-rt-devel, linux-nfs,
linux-aio, linux-fsdevel, linux-security-module, netdev,
intel-gfx, linux-acpi, linux-s390, cgroups, pengdonglin,
Paul E . McKenney
In-Reply-To: <20250912213531.7-YeRBeD@linutronix.de>
On 9/12/25 5:35 PM, Sebastian Andrzej Siewior wrote:
> On 2025-09-12 17:13:09 [-0400], Waiman Long wrote:
>> On 9/12/25 2:50 AM, pengdonglin wrote:
>>> From: pengdonglin <pengdonglin@xiaomi.com>
>>>
>>> When CONFIG_PREEMPT_RT is disabled, spin_lock*() operations implicitly
>>> disable preemption, which provides RCU read-side protection. When
>>> CONFIG_PREEMPT_RT is enabled, spin_lock*() implementations internally
>>> manage RCU read-side critical sections.
>> I have some doubt about your claim that disabling preemption provides RCU
>> read-side protection. It is true for some flavors but probably not all. I do
>> know that disabling interrupt will provide RCU read-side protection. So for
>> spin_lock_irq*() calls, that is valid. I am not sure about spin_lock_bh(),
>> maybe it applies there too. we need some RCU people to confirm.
> The claim is valid since Paul merged the three flavours we had. Before
> that preempt_disable() (and disabling irqs) would match
> rcu_read_lock_sched(). rcu_read_lock() and rcu_read_lock_bh() were
> different in terms of grace period and clean up.
> So _now_ we could remove it if it makes things easier.
Thanks for the clarification.
In this case, I think the patch description should mention the commit
that unify the 3 RCU flavors to make sure that this patch won't be
accidentally backport'ed to an older kernel without the necessary
prerequisite commit(s).
Cheers,
Longman
^ permalink raw reply
* [bug report] [regression?] bpf lsm breaks /proc/*/attr/current with security= on commandline
From: Filip Hejsek @ 2025-09-13 17:01 UTC (permalink / raw)
To: linux-security-module, Paul Moore, James Morris, Serge E. Hallyn
Cc: bpf, linux-kernel, regressions
Hello,
TLDR: because of bpf lsm, putting security=selinux on commandline
results in /proc/*/attr/current returning errors.
When the legacy security= commandline option is used, the specified lsm
is added to the end of the lsm list. For example, security=apparmor
results in the following order of security modules:
capability,landlock,lockdown,yama,bpf,apparmor
In particular, the bpf lsm will be ordered before the chosen major lsm.
This causes reads and writes of /proc/*/attr/current to fail, because
the bpf hook overrides the apparmor/selinux hook.
As you can see in the code below, only the first registered hook is
called (when reading attr/current, lsmid is 0):
int security_getprocattr(struct task_struct *p, int lsmid, const char *name,
char **value)
{
struct lsm_static_call *scall;
lsm_for_each_hook(scall, getprocattr) {
if (lsmid != 0 && lsmid != scall->hl->lsmid->id)
continue;
return scall->hl->hook.getprocattr(p, name, value);
}
return LSM_RET_DEFAULT(getprocattr);
}
Even though the bpf lsm doesn't allow attaching bpf programs to this
hook, it still prevents the other hooks from being called.
This is maybe a regression, because with the same commandline, reading
from /proc/*/attr/current probably worked before the introduction of
bpf lsm.
Regards,
Filip Hejsek
^ permalink raw reply
* Re: [PATCH] security: keys: use menuconfig for KEYS symbol
From: Jarkko Sakkinen @ 2025-09-14 17:14 UTC (permalink / raw)
To: Paul Moore, David Howells
Cc: Randy Dunlap, linux-kernel, David Howells, keyrings,
linux-security-module, James Morris, Serge E. Hallyn
In-Reply-To: <CAHC9VhQsVMKN6YyHFF81rPvxirtM7UwwAJSpOZdeybGCuO1c2g@mail.gmail.com>
On Thu, Sep 11, 2025 at 04:34:15PM -0400, Paul Moore wrote:
> On Mon, Aug 25, 2025 at 6:50 PM Jarkko Sakkinen <jarkko@kernel.org> wrote:
> > On Sun, Aug 24, 2025 at 03:28:13PM -0700, Randy Dunlap wrote:
> > > Give the KEYS kconfig symbol and its associated symbols a separate
> > > menu space under Security options by using "menuconfig" instead of
> > > "config".
> > >
> > > This also makes it easier to find the security and LSM options.
> > >
> > > Signed-off-by: Randy Dunlap <rdunlap@infradead.org>
> > > ---
> > > Cc: David Howells <dhowells@redhat.com>
> > > Cc: Jarkko Sakkinen <jarkko@kernel.org>
> > > Cc: keyrings@vger.kernel.org
> > > Cc: linux-security-module@vger.kernel.org
> > > Cc: Paul Moore <paul@paul-moore.com>
> > > Cc: James Morris <jmorris@namei.org>
> > > Cc: "Serge E. Hallyn" <serge@hallyn.com>
> > >
> > > security/keys/Kconfig | 14 ++++++--------
> > > 1 file changed, 6 insertions(+), 8 deletions(-)
>
> ...
>
> > I wote for this at least. Definitely an improvement:
> >
> > Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
>
> I'm guessing you're planning to take this patch Jarkko?
I'm doing PRs early week to this can easily go to my queue
(neither mind if David picks it).
David?
>
> --
> paul-moore.com
BR, Jarkko
^ permalink raw reply
* Re: [PATCH v2 0/7] fs/9p: Reuse inode based on path (in addition to qid)
From: Tingmao Wang @ 2025-09-14 21:25 UTC (permalink / raw)
To: Dominique Martinet, Mickaël Salaün
Cc: Eric Van Hensbergen, Latchesar Ionkov, Christian Schoenebeck,
v9fs, Günther Noack, linux-security-module, Jan Kara,
Amir Goldstein, Matthew Bobrowski, Al Viro, Christian Brauner,
linux-fsdevel
In-Reply-To: <cover.1756935780.git.m@maowtm.org>
Hi Dominique and others,
I had a chat with Mickaël earlier this week and some discussion following
that, and we thought of a potential alternative to what I was proposing
here that might work for Landlock: using the inode number (or more
correctly, qid.path) directly as the keys for Landlock rules when
accessing 9p files. I'm not sure how sound this is from the perspective
of 9pfs (there are pros and caveats), and I would like to gather some
thoughts on this idea.
Technically a 9pfs server is not supposed to return colliding qid.paths
for different files. In fact, according to [1], the qid must not be the
same even for files which are deleted then recreated using the same name
(whereas for e.g. ext4, inode number is reused if a file is deleted and
recreated, possibly with a different name, in the same directory).
However, this is in practice not the case for many actual 9pfs server
implementations (thus the reason for this patch series in the first
place).
This is a bad problem for the 9pfs client in Linux as it can lead to data
corruption if the wrong inode is used, but for Landlock, the only effect
of this is allowing access to more files then the sandboxing application
intended (and only in the presence of an "erroneous" 9pfs server). Any
other alternative, including this patch series, has the opposite risk -
files that should be allowed might be denied (even if the server
implementation is fully correct in terms of no reusing of qids). In
particular, this patch cannot correctly handle server-side renames of an
allowed file, or rename of a directory with children in it from the client
(although this might be solved, with the expense of adding more
complicated code in the rename path to rewrite all the struct ino_paths).
In discussion with Mickaël he thought that it would be acceptable for
Landlock to assume that the server is well-behaved, and Landlock could
specialize for 9pfs to allow access if the qid matches what's previously
seen when creating the Landlock ruleset (by using the qid as the key of
the rule, instead of a pointer to the inode).
There are, however, several immediate issues with this approach:
1. The qid is 9pfs internal data, and we may need extra API for 9pfs to
expose this to Landlock. On 64bit, this is easy as it's just the inode
number (offset by 2), which we can already get from the struct inode.
But perhaps on 32bit we need a way to expose the full 64bit server-sent
qid to Landlock (or other kernel subsystems), if we're going to do
this.
2. Even though qids are supposed to be unique across the lifetime of a
filesystem (including deleted files), this is not the case even for
QEMU in multidevs=remap mode, when running on ext4, as tested on QEMU
10.1.0. And thus in practice a Landlock ruleset would need to hold a
reference to the file to keep it open, so that the server will not
re-use the qid for other files (having a reference to the struct inode
alone doesn't seem to do that).
Unfortunately, holding a dentry in Landlock prevents the filesystem
from being unmounted (causes WARNs), with no (proper) chance for
Landlock to release those dentries. We might do it in
security_sb_umount, but then at that point it is not guaranteed that
the unmount will happen - perhaps we would need a new security_ hooks
in the umount path?
Alternatively, I think if we could somehow tell 9pfs to keep a fid open
(until either the Landlock domain is closed, or the filesystem is
unmounted), it could also work.
I'm not sure what's the best way to do this, it seems like unless we
can get a new pre_umount / pre_sb_delete hook in which we can free
dentries, 9pfs would need to expose some new API, or alternatively, in
uncached mode, have the v9fs inode itself hold a (strong) reference to
the fid, so that if Landlock has a reference to the inode, the file is
kept open server-side.
The advantage of doing this is that, for a server with reasonable
behaviour, Landlock users would not get incorrect denials (i.e. things
"just work"), while still maintaining security if the 9p server is
"reasonable" (in particular, an application sandboxed under Landlock would
not get access to unrelated files if it does not have a way to somehow get
those files to be recreated with an allowed inode number), whereas the
current patch has the problem with server side renames and directory
renames (server or client side), and also can't deal with hard links.
I'm not sure how attractive this solution is to various people here -
Mickaël is happy with special-casing 9pfs in Landlock, and in fact he
suggested this idea in the first place, but I think this has the potential
to be quite complicated (but technically more correct). It would also
only work for Landlock, and if e.g. fsnotify wants to have the same
behaviour, that would need its own changes too.
Apologies for the long-winded explanation, any thoughts on this?
[1]: https://ericvh.github.io/9p-rfc/rfc9p2000.html#msgs
"If a file is deleted and recreated with the same name in the same
directory, the old and new path components of the qids should be
different."
---
Note: Even with the above, there's another potential problem - QEMU does
not, for some reason (I've not really investigated this very deeply, but
it's observation from /proc/.../fd), keep a directory open when the guest
has a fid to it. This means that if a directory is deleted while we have
an active Landlock rule on it, a new file or directory may get the same
qid. (However, at least this still correctly handles directory renames,
and the only effect is Landlock allowing more files than intended in the
presence of a buggy server.)
(The Hyper-V 9p server, used by WSL, seems to have the same problem, and a
bit worse since even client-side renames breaks opened dir fds on the
WSL-to-Windows 9pfs (/mnt/c/...))
(Another challenge is that Landlock would have to know when a file is on a
9pfs in uncached mode - we probably don't need this behaviour for cached
mode filesystems, as we assume no server changes in that case and the
inode is reused already. We can certainly determine the FS of a file, but
not sure about specific 9pfs cache options)
^ permalink raw reply
* [PATCH v4 00/12] Signed BPF programs
From: KP Singh @ 2025-09-14 21:51 UTC (permalink / raw)
To: bpf, linux-security-module; +Cc: bboscaccy, paul, kys, ast, daniel, andrii
# v3 -> v4
* Dropped the use of session keyring by default from skeletons.
* Andrii's feedback on exclusive map creation libbpf changes.
* Cleaned up some more typos I found.
# v2 -> v3
* Dropped unstable test where function can be inlined and only select few
LSKEL tests are using signing per Alexei's request
* Some other feedback incorporated.
#v1 -> v2
* Addressed feedback on excl maps and their implementation
* libbpf feedback
* fixed s390x and other tests that were failing in the CI
* using the kernel's sha256 API since it now uses acceleration if available
* simple signing test case, this can be extended to inject a false SHA into
the loader
BPF Signing has gone over multiple discussions in various conferences with the
kernel and BPF community and the following patch series is a culmination
of the current of discussion and signed BPF programs. Once signing is
implemented, the next focus would be to implement the right security policies
for all BPF use-cases (dynamically generated bpf programs, simple non CO-RE
programs).
Signing also paves the way for allowing unrivileged users to
load vetted BPF programs and helps in adhering to the principle of least
privlege by avoiding unnecessary elevation of privileges to CAP_BPF and
CAP_SYS_ADMIN (ofcourse, with the appropriate security policy active).
A early version of this design was proposed in [1]:
# General Idea: Trusted Hash Chain
The key idea of the design is to use a signing algorithm that allows
us to integrity-protect a number of future payloads, including their
order, by creating a chain of trust.
Consider that Alice needs to send messages M_1, M_2, ..., M_n to Bob.
We define blocks of data such that:
B_n = M_n || H(termination_marker)
(Each block contains its corresponding message and the hash of the
*next* block in the chain.)
B_{n-1} = M_{n-1} || H(B_n)
B_{n-2} = M_{n-2} || H(B_{n-1})
...
B_2 = M_2 || H(B_3)
B_1 = M_1 || H(B_2)
Alice does the following (e.g., on a build system where all payloads
are available):
* Assembles the blocks B_1, B_2, ..., B_n.
* Calculates H(B_1) and signs it, yielding Sig(H(B_1)).
Alice sends the following to Bob:
M_1, H(B_2), Sig(H(B_1))
Bob receives this payload and does the following:
* Reconstructs B_1 as B_1' using the received M_1 and H(B_2)
(i.e., B_1' = M_1 || H(B_2)).
* Recomputes H(B_1') and verifies the signature against the
received Sig(H(B_1)).
* If the signature verifies, it establishes the integrity of M_1
and H(B_2) (and transitively, the integrity of the entire chain). Bob
now stores the verified H(B_2) until it receives the next message.
* When Bob receives M_2 (and H(B_3) if n > 2), it reconstructs
B_2' (e.g., B_2' = M_2 || H(B_3), or if n=2, B_2' = M_2 ||
H(termination_marker)). Bob then computes H(B_2') and compares it
against the stored H(B_2) that was verified in the previous step.
This process continues until the last block is received and verified.
Now, applying this to the BPF signing use-case, we simplify to two messages:
M_1 = I_loader (the instructions of the loader program)
M_2 = M_metadata (the metadata for the loader program, passed in a
map, which includes the programs to be loaded and other context)
For this specific BPF case, we will directly sign a composite of the
first message and the hash of the second. Let H_meta = H(M_metadata).
The block to be signed is effectively:
B_signed = I_loader || H_meta
The signature generated is Sig(B_signed).
The process then follows a similar pattern to the Alice and Bob model,
where the kernel (Bob) verifies I_loader and H_meta using the
signature. Then, the trusted I_loader is responsible for verifying
M_metadata against the trusted H_meta.
From an implementation standpoint:
# Build
bpftool (or some other tool in a trusted build environment) knows
about the metadata (M_metadata) and the loader program (I_loader). It
first calculates H_meta = H(M_metadata). Then it constructs the object
to be signed and computes the signature:
Sig(I_loader || H_meta)
# Loader
The loader program and the metadata are a hermetic representation of the source
of the eBPF program, its maps and context. The loader program is generated by
libbpf as a part of a standard API i.e. bpf_object__gen_loader.
## Supply chain
While users can use light skeletons as a convenient method to use signing
support, they can directly use the loader program generation using libbpf
(bpf_object__gen_loader) into their own trusted toolchains.
libbpf, which has access to the program's instruction buffer is a key part of
the TCB of the build environment
An advanced threat model that does not intend to depend on libbpf (or any provenant
userspace BPF libraries) due to supply chain risks despite it being developed
in the kernel source and by the kernel community will require reimplmenting a
lot of the core BPF userspace support (like instruction relocation, map handling).
Such an advanced user would also need to integrate the generation of the loader
into their toolchain.
Given that many use-cases (e.g. Cilium) generate trusted BPF programs,
trusted loaders are an inevitability and a requirement for signing support, a
entrusting loader programs will be a fundamental requirement for an security
policy.
The initial instructions of the loader program verify the SHA256 hash
of the metadata (M_metadata) that will be passed in a map. These instructions
effectively embed the precomputed H_meta as immediate values.
ld_imm64 r1, const_ptr_to_map // insn[0].src_reg == BPF_PSEUDO_MAP_IDX
r2 = *(u64 *)(r1 + 0);
ld_imm64 r3, sha256_of_map_part1 // precomputed by bpf_object__gen_load/libbpf (H_meta_1)
if r2 != r3 goto out;
r2 = *(u64 *)(r1 + 8);
ld_imm64 r3, sha256_of_map_part2 // precomputed by bpf_object__gen_load/libbpf (H_meta_2)
if r2 != r3 goto out;
r2 = *(u64 *)(r1 + 16);
ld_imm64 r3, sha256_of_map_part3 // precomputed by bpf_object__gen_load/libbpf (H_meta_3)
if r2 != r3 goto out;
r2 = *(u64 *)(r1 + 24);
ld_imm64 r3, sha256_of_map_part4 // precomputed by bpf_object__gen_load/libbpf (H_meta_4)
if r2 != r3 goto out;
...
This implicitly makes the payload equivalent to the signed block (B_signed)
I_loader || H_meta
bpftool then generates the signature of this I_loader payload (which
now contains the expected H_meta) using a key and an identity:
This signature is stored in bpf_attr, which is extended as follows for
the BPF_PROG_LOAD command:
__aligned_u64 signature;
__u32 signature_size;
__u32 keyring_id;
The reasons for a simpler UAPI is that it's more future proof (e.g.) with more
stable instruction buffers, loader programs being directly into the compilers.
A simple API also allows simple programs e.g. for networking that don't need
loader programs to directly use signing.
# Extending OBJ_GET_INFO_BY_FD for hashes
OBJ_GET_INFO_BY_FD is used to get information about BPF objects (maps, programs, links) and
returning the hash of the map is a natural extension of the UAPI as it can be
helpful for debugging, fingerprinting etc.
Currently, it's only implemented for BPF_MAP_TYPE_ARRAY. It can be trivially
extended for BPF programs to return the complete SHA256 along with the tag.
The SHA is stored in struct bpf_map for exclusive and frozen maps
struct bpf_map {
+ u64 sha[4];
const struct bpf_map_ops *ops;
struct bpf_map *inner_map_meta;
};
## Exclusive BPF maps
Exclusivity ensures that the map can only be used by a future BPF
program whose SHA256 hash matches sha256_of_future_prog.
First, bpf_prog_calc_tag() is updated to compute the SHA256 instead of
SHA1, and this hash is stored in struct bpf_prog_aux:
@@ -1588,6 +1588,7 @@ struct bpf_prog_aux {
int cgroup_atype; /* enum cgroup_bpf_attach_type */
struct bpf_map *cgroup_storage[MAX_BPF_CGROUP_STORAGE_TYPE];
char name[BPF_OBJ_NAME_LEN];
+ u64 sha[4];
u64 (*bpf_exception_cb)(u64 cookie, u64 sp, u64 bp, u64, u64);
// ...
};
An exclusive is created by passing an excl_prog_hash
(and excl_prog_hash_size) in the BPF_MAP_CREATE command.
When a BPF program is subsequently loaded and it attempts to use this map,
the kernel will compare the program's own SHA256 hash against the one
registered with the map, if matching, it will be added to prog->used_maps[].
The program load will fail if the hashes do not match or if the map is
already in use by another (non-matching) exclusive program.
Exclusive maps ensure that no other BPF programs and compromise the intergity of
the map post the signature verification.
NOTE: Exclusive maps cannot be added as inner maps.
# Light Skeleton Sequence (Userspace Example)
err = map_fd = skel_map_create(BPF_MAP_TYPE_ARRAY, "__loader.map",
opts->excl_prog_hash,
opts->excl_prog_hash_sz, 4,
opts->data_sz, 1);
err = skel_map_update_elem(map_fd, &key, opts->data, 0);
err = skel_map_freeze(map_fd);
// Kernel computes the hash of the map.
err = skel_obj_get_info_by_fd(map_fd);
memset(&attr, 0, prog_load_attr_sz);
attr.prog_type = BPF_PROG_TYPE_SYSCALL;
attr.insns = (long) opts->insns;
attr.insn_cnt = opts->insns_sz / sizeof(struct bpf_insn);
attr.signature = (long) opts->signature;
attr.signature_size = opts->signature_sz;
attr.keyring_id = opts->keyring_id;
attr.license = (long) "Dual BSD/GPL";
The kernel will:
* Compute the hash of the provided I_loader bytecode.
* Verify the signature against this computed hash.
* Check if the metadata map (now exclusive) is intended for this
program's hash.
The signature check happens in BPF_PROG_LOAD before the security_bpf_prog
LSM hook.
This ensures that the loaded loader program (I_loader), including the
embedded expected hash of the metadata (H_meta), is trusted.
Since the loader program is now trusted, it can be entrusted to verify
the actual metadata (M_metadata) read from the (now exclusive and
frozen) map against the embedded (and trusted) H_meta. There is no
Time-of-Check-Time-of-Use (TOCTOU) vulnerability here because:
* The signature covers the I_loader and its embedded H_meta.
* The metadata map M_metadata is frozen before the loader program is loaded
and associated with it.
* The map is made exclusive to the specific (signed and verified)
loader program.
[1] https://lore.kernel.org/bpf/CACYkzJ6VQUExfyt0=-FmXz46GHJh3d=FXh5j4KfexcEFbHV-vg@mail.gmail.com/#t
KP Singh (12):
bpf: Update the bpf_prog_calc_tag to use SHA256
bpf: Implement exclusive map creation
libbpf: Implement SHA256 internal helper
libbpf: Support exclusive map creation
selftests/bpf: Add tests for exclusive maps
bpf: Return hashes of maps in BPF_OBJ_GET_INFO_BY_FD
bpf: Move the signature kfuncs to helpers.c
bpf: Implement signature verification for BPF programs
libbpf: Update light skeleton for signing
libbpf: Embed and verify the metadata hash in the loader
bpftool: Add support for signing BPF programs
selftests/bpf: Enable signature verification for some lskel tests
crypto/asymmetric_keys/pkcs7_verify.c | 1 +
include/linux/bpf.h | 42 +++-
include/linux/verification.h | 1 +
include/uapi/linux/bpf.h | 18 ++
kernel/bpf/Kconfig | 2 +-
kernel/bpf/arraymap.c | 13 ++
kernel/bpf/core.c | 13 +-
kernel/bpf/helpers.c | 166 ++++++++++++++
kernel/bpf/syscall.c | 99 +++++++-
kernel/bpf/verifier.c | 6 +
kernel/trace/bpf_trace.c | 183 ---------------
.../bpf/bpftool/Documentation/bpftool-gen.rst | 16 +-
.../bpftool/Documentation/bpftool-prog.rst | 18 +-
tools/bpf/bpftool/Makefile | 6 +-
tools/bpf/bpftool/cgroup.c | 4 +
tools/bpf/bpftool/gen.c | 66 +++++-
tools/bpf/bpftool/main.c | 26 ++-
tools/bpf/bpftool/main.h | 11 +
tools/bpf/bpftool/prog.c | 27 ++-
tools/bpf/bpftool/sign.c | 212 ++++++++++++++++++
tools/include/uapi/linux/bpf.h | 18 ++
tools/lib/bpf/bpf.c | 6 +-
tools/lib/bpf/bpf.h | 5 +-
tools/lib/bpf/bpf_gen_internal.h | 2 +
tools/lib/bpf/gen_loader.c | 55 +++++
tools/lib/bpf/libbpf.c | 128 +++++++++++
tools/lib/bpf/libbpf.h | 25 ++-
tools/lib/bpf/libbpf.map | 3 +
tools/lib/bpf/libbpf_internal.h | 4 +
tools/lib/bpf/skel_internal.h | 76 ++++++-
tools/testing/selftests/bpf/.gitignore | 1 +
tools/testing/selftests/bpf/Makefile | 35 ++-
.../selftests/bpf/prog_tests/atomics.c | 10 +-
.../selftests/bpf/prog_tests/fentry_fexit.c | 15 +-
.../selftests/bpf/prog_tests/fentry_test.c | 9 +-
.../selftests/bpf/prog_tests/fexit_test.c | 9 +-
.../selftests/bpf/prog_tests/map_excl.c | 54 +++++
tools/testing/selftests/bpf/progs/map_excl.c | 34 +++
.../selftests/bpf/progs/verifier_map_ptr.c | 7 +-
tools/testing/selftests/bpf/test_progs.c | 13 ++
.../testing/selftests/bpf/verify_sig_setup.sh | 11 +-
41 files changed, 1216 insertions(+), 234 deletions(-)
create mode 100644 tools/bpf/bpftool/sign.c
create mode 100644 tools/testing/selftests/bpf/prog_tests/map_excl.c
create mode 100644 tools/testing/selftests/bpf/progs/map_excl.c
--
2.43.0
^ permalink raw reply
* [PATCH v4 01/12] bpf: Update the bpf_prog_calc_tag to use SHA256
From: KP Singh @ 2025-09-14 21:51 UTC (permalink / raw)
To: bpf, linux-security-module
Cc: bboscaccy, paul, kys, ast, daniel, andrii, KP Singh
In-Reply-To: <20250914215141.15144-1-kpsingh@kernel.org>
Exclusive maps restrict map access to specific programs using a hash.
The current hash used for this is SHA1, which is prone to collisions.
This patch uses SHA256, which is more resilient against
collisions. This new hash is stored in bpf_prog and used by the verifier
to determine if a program can access a given exclusive map.
The original 64-bit tags are kept, as they are used by users as a short,
possibly colliding program identifier for non-security purposes.
Signed-off-by: KP Singh <kpsingh@kernel.org>
---
include/linux/bpf.h | 6 +++++-
kernel/bpf/Kconfig | 2 +-
kernel/bpf/core.c | 13 ++++++-------
3 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 41f776071ff5..d75902074bd1 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -31,6 +31,7 @@
#include <linux/memcontrol.h>
#include <linux/cfi.h>
#include <asm/rqspinlock.h>
+#include <crypto/sha2.h>
struct bpf_verifier_env;
struct bpf_verifier_log;
@@ -1717,7 +1718,10 @@ struct bpf_prog {
enum bpf_attach_type expected_attach_type; /* For some prog types */
u32 len; /* Number of filter blocks */
u32 jited_len; /* Size of jited insns in bytes */
- u8 tag[BPF_TAG_SIZE];
+ union {
+ u8 digest[SHA256_DIGEST_SIZE];
+ u8 tag[BPF_TAG_SIZE];
+ };
struct bpf_prog_stats __percpu *stats;
int __percpu *active;
unsigned int (*bpf_func)(const void *ctx,
diff --git a/kernel/bpf/Kconfig b/kernel/bpf/Kconfig
index 17067dcb4386..eb3de35734f0 100644
--- a/kernel/bpf/Kconfig
+++ b/kernel/bpf/Kconfig
@@ -3,7 +3,7 @@
# BPF interpreter that, for example, classic socket filters depend on.
config BPF
bool
- select CRYPTO_LIB_SHA1
+ select CRYPTO_LIB_SHA256
# Used by archs to tell that they support BPF JIT compiler plus which
# flavour. Only one of the two can be selected for a specific arch since
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 1cda2589d4b3..0b3b88084f88 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -39,6 +39,7 @@
#include <linux/bpf_mem_alloc.h>
#include <linux/memcontrol.h>
#include <linux/execmem.h>
+#include <crypto/sha2.h>
#include <asm/barrier.h>
#include <linux/unaligned.h>
@@ -295,13 +296,12 @@ void __bpf_prog_free(struct bpf_prog *fp)
int bpf_prog_calc_tag(struct bpf_prog *fp)
{
- size_t size = bpf_prog_insn_size(fp);
- u8 digest[SHA1_DIGEST_SIZE];
+ u32 insn_size = bpf_prog_insn_size(fp);
struct bpf_insn *dst;
bool was_ld_map;
- u32 i;
+ int i, ret = 0;
- dst = vmalloc(size);
+ dst = vmalloc(insn_size);
if (!dst)
return -ENOMEM;
@@ -327,10 +327,9 @@ int bpf_prog_calc_tag(struct bpf_prog *fp)
was_ld_map = false;
}
}
- sha1((const u8 *)dst, size, digest);
- memcpy(fp->tag, digest, sizeof(fp->tag));
+ sha256((u8 *)dst, insn_size, fp->digest);
vfree(dst);
- return 0;
+ return ret;
}
static int bpf_adj_delta_to_imm(struct bpf_insn *insn, u32 pos, s32 end_old,
--
2.43.0
^ permalink raw reply related
* [PATCH v4 02/12] bpf: Implement exclusive map creation
From: KP Singh @ 2025-09-14 21:51 UTC (permalink / raw)
To: bpf, linux-security-module
Cc: bboscaccy, paul, kys, ast, daniel, andrii, KP Singh
In-Reply-To: <20250914215141.15144-1-kpsingh@kernel.org>
Exclusive maps allow maps to only be accessed by program with a
program with a matching hash which is specified in the excl_prog_hash
attr.
For the signing use-case, this allows the trusted loader program
to load the map and verify the integrity
Signed-off-by: KP Singh <kpsingh@kernel.org>
---
include/linux/bpf.h | 1 +
include/uapi/linux/bpf.h | 6 ++++++
kernel/bpf/syscall.c | 31 +++++++++++++++++++++++++++----
kernel/bpf/verifier.c | 6 ++++++
tools/include/uapi/linux/bpf.h | 6 ++++++
5 files changed, 46 insertions(+), 4 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index d75902074bd1..c6a6ee1b2938 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -329,6 +329,7 @@ struct bpf_map {
atomic64_t sleepable_refcnt;
s64 __percpu *elem_count;
u64 cookie; /* write-once */
+ char *excl_prog_sha;
};
static inline const char *btf_field_type_name(enum btf_field_type type)
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 233de8677382..57687b2e1c47 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -1522,6 +1522,12 @@ union bpf_attr {
* If provided, map_flags should have BPF_F_TOKEN_FD flag set.
*/
__s32 map_token_fd;
+
+ /* Hash of the program that has exclusive access to the map.
+ */
+ __aligned_u64 excl_prog_hash;
+ /* Size of the passed excl_prog_hash. */
+ __u32 excl_prog_hash_size;
};
struct { /* anonymous struct used by BPF_MAP_*_ELEM and BPF_MAP_FREEZE commands */
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 3f178a0f8eb1..c8ef91acfe98 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -860,6 +860,7 @@ static void bpf_map_free(struct bpf_map *map)
* the free of values or special fields allocated from bpf memory
* allocator.
*/
+ kfree(map->excl_prog_sha);
migrate_disable();
map->ops->map_free(map);
migrate_enable();
@@ -1338,9 +1339,9 @@ static bool bpf_net_capable(void)
return capable(CAP_NET_ADMIN) || capable(CAP_SYS_ADMIN);
}
-#define BPF_MAP_CREATE_LAST_FIELD map_token_fd
+#define BPF_MAP_CREATE_LAST_FIELD excl_prog_hash_size
/* called via syscall */
-static int map_create(union bpf_attr *attr, bool kernel)
+static int map_create(union bpf_attr *attr, bpfptr_t uattr)
{
const struct bpf_map_ops *ops;
struct bpf_token *token = NULL;
@@ -1534,7 +1535,29 @@ static int map_create(union bpf_attr *attr, bool kernel)
attr->btf_vmlinux_value_type_id;
}
- err = security_bpf_map_create(map, attr, token, kernel);
+ if (attr->excl_prog_hash) {
+ bpfptr_t uprog_hash = make_bpfptr(attr->excl_prog_hash, uattr.is_kernel);
+
+ if (attr->excl_prog_hash_size != SHA256_DIGEST_SIZE) {
+ err = -EINVAL;
+ goto free_map;
+ }
+
+ map->excl_prog_sha = kzalloc(SHA256_DIGEST_SIZE, GFP_KERNEL);
+ if (!map->excl_prog_sha) {
+ err = -ENOMEM;
+ goto free_map;
+ }
+
+ if (copy_from_bpfptr(map->excl_prog_sha, uprog_hash, SHA256_DIGEST_SIZE)) {
+ err = -EFAULT;
+ goto free_map;
+ }
+ } else if (attr->excl_prog_hash_size) {
+ return -EINVAL;
+ }
+
+ err = security_bpf_map_create(map, attr, token, uattr.is_kernel);
if (err)
goto free_map_sec;
@@ -6008,7 +6031,7 @@ static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size)
switch (cmd) {
case BPF_MAP_CREATE:
- err = map_create(&attr, uattr.is_kernel);
+ err = map_create(&attr, uattr);
break;
case BPF_MAP_LOOKUP_ELEM:
err = map_lookup_elem(&attr);
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 17fe623400a5..9c5a88ec5abe 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -20382,6 +20382,12 @@ static int check_map_prog_compatibility(struct bpf_verifier_env *env,
{
enum bpf_prog_type prog_type = resolve_prog_type(prog);
+ if (map->excl_prog_sha &&
+ memcmp(map->excl_prog_sha, prog->digest, SHA256_DIGEST_SIZE)) {
+ verbose(env, "program's hash doesn't match map's excl_prog_hash\n");
+ return -EACCES;
+ }
+
if (btf_record_has_field(map->record, BPF_LIST_HEAD) ||
btf_record_has_field(map->record, BPF_RB_ROOT)) {
if (is_tracing_prog_type(prog_type)) {
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 233de8677382..57687b2e1c47 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -1522,6 +1522,12 @@ union bpf_attr {
* If provided, map_flags should have BPF_F_TOKEN_FD flag set.
*/
__s32 map_token_fd;
+
+ /* Hash of the program that has exclusive access to the map.
+ */
+ __aligned_u64 excl_prog_hash;
+ /* Size of the passed excl_prog_hash. */
+ __u32 excl_prog_hash_size;
};
struct { /* anonymous struct used by BPF_MAP_*_ELEM and BPF_MAP_FREEZE commands */
--
2.43.0
^ permalink raw reply related
* [PATCH v4 03/12] libbpf: Implement SHA256 internal helper
From: KP Singh @ 2025-09-14 21:51 UTC (permalink / raw)
To: bpf, linux-security-module
Cc: bboscaccy, paul, kys, ast, daniel, andrii, KP Singh
In-Reply-To: <20250914215141.15144-1-kpsingh@kernel.org>
Use AF_ALG sockets to not have libbpf depend on OpenSSL. The helper is
used for the loader generation code to embed the metadata hash in the
loader program and also by the bpf_map__make_exclusive API to calculate
the hash of the program the map is exclusive to.
Acked-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: KP Singh <kpsingh@kernel.org>
---
tools/lib/bpf/libbpf.c | 59 +++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf_internal.h | 4 +++
2 files changed, 63 insertions(+)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index fe4fc5438678..a39640bd5448 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -43,6 +43,9 @@
#include <sys/vfs.h>
#include <sys/utsname.h>
#include <sys/resource.h>
+#include <sys/socket.h>
+#include <linux/if_alg.h>
+#include <linux/socket.h>
#include <libelf.h>
#include <gelf.h>
#include <zlib.h>
@@ -14217,3 +14220,59 @@ void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s)
free(s->progs);
free(s);
}
+
+int libbpf_sha256(const void *data, size_t data_sz, void *sha_out, size_t sha_out_sz)
+{
+ struct sockaddr_alg sa = {
+ .salg_family = AF_ALG,
+ .salg_type = "hash",
+ .salg_name = "sha256"
+ };
+ int sock_fd = -1;
+ int op_fd = -1;
+ int err = 0;
+
+ if (sha_out_sz != SHA256_DIGEST_LENGTH) {
+ pr_warn("sha_out_sz should be exactly 32 bytes for a SHA256 digest");
+ return -EINVAL;
+ }
+
+ sock_fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
+ if (sock_fd < 0) {
+ err = -errno;
+ pr_warn("failed to create AF_ALG socket for SHA256: %s\n", errstr(err));
+ return err;
+ }
+
+ if (bind(sock_fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
+ err = -errno;
+ pr_warn("failed to bind to AF_ALG socket for SHA256: %s\n", errstr(err));
+ goto out;
+ }
+
+ op_fd = accept(sock_fd, NULL, 0);
+ if (op_fd < 0) {
+ err = -errno;
+ pr_warn("failed to accept from AF_ALG socket for SHA256: %s\n", errstr(err));
+ goto out;
+ }
+
+ if (write(op_fd, data, data_sz) != data_sz) {
+ err = -errno;
+ pr_warn("failed to write data to AF_ALG socket for SHA256: %s\n", errstr(err));
+ goto out;
+ }
+
+ if (read(op_fd, sha_out, SHA256_DIGEST_LENGTH) != SHA256_DIGEST_LENGTH) {
+ err = -errno;
+ pr_warn("failed to read SHA256 from AF_ALG socket: %s\n", errstr(err));
+ goto out;
+ }
+
+out:
+ if (op_fd >= 0)
+ close(op_fd);
+ if (sock_fd >= 0)
+ close(sock_fd);
+ return err;
+}
diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h
index 477a3b3389a0..8a055de0d324 100644
--- a/tools/lib/bpf/libbpf_internal.h
+++ b/tools/lib/bpf/libbpf_internal.h
@@ -736,4 +736,8 @@ int elf_resolve_pattern_offsets(const char *binary_path, const char *pattern,
int probe_fd(int fd);
+#define SHA256_DIGEST_LENGTH 32
+#define SHA256_DWORD_SIZE SHA256_DIGEST_LENGTH / sizeof(__u64)
+
+int libbpf_sha256(const void *data, size_t data_sz, void *sha_out, size_t sha_out_sz);
#endif /* __LIBBPF_LIBBPF_INTERNAL_H */
--
2.43.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox