* [PATCH v2 01/15] VFS: note error returns is documentation for various lookup functions
From: NeilBrown @ 2026-02-23 1:06 UTC (permalink / raw)
To: Christian Brauner, Alexander Viro, David Howells, Jan Kara,
Chuck Lever, Jeff Layton, Miklos Szeredi, Amir Goldstein,
John Johansen, Paul Moore, James Morris, Serge E. Hallyn,
Stephen Smalley, Darrick J. Wong
Cc: linux-kernel, netfs, linux-fsdevel, linux-nfs, linux-unionfs,
apparmor, linux-security-module, selinux
In-Reply-To: <20260223011210.3853517-1-neilb@ownmail.net>
From: NeilBrown <neil@brown.name>
Darrick recently noted that try_lookup_noperm() is documented as
"Look up a dentry by name in the dcache, returning NULL if it does not
currently exist." but it can in fact return an error.
So update the documentation for that and related function.
Link: https://lore.kernel.org/all/20260218234917.GA6490@frogsfrogsfrogs/
Cc: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: NeilBrown <neil@brown.name>
---
fs/namei.c | 29 ++++++++++++++++++++++++++++-
1 file changed, 28 insertions(+), 1 deletion(-)
diff --git a/fs/namei.c b/fs/namei.c
index 58f715f7657e..e4ac07a4090e 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -3124,7 +3124,8 @@ static int lookup_one_common(struct mnt_idmap *idmap,
* @base: base directory to lookup from
*
* Look up a dentry by name in the dcache, returning NULL if it does not
- * currently exist. The function does not try to create a dentry and if one
+ * currently exist or an error is there is a problem with the name.
+ * The function does not try to create a dentry and if one
* is found it doesn't try to revalidate it.
*
* Note that this routine is purely a helper for filesystem usage and should
@@ -3132,6 +3133,11 @@ static int lookup_one_common(struct mnt_idmap *idmap,
*
* No locks need be held - only a counted reference to @base is needed.
*
+ * Returns:
+ * - ref-counted dentry on success, or
+ * - %NULL if name could not be found, or
+ * - ERR_PTR(-EACCES) if name is dot or dotdot or contains a slash or nul, or
+ * - ERR_PTR() if fs provide ->d_hash, and this returned an error.
*/
struct dentry *try_lookup_noperm(struct qstr *name, struct dentry *base)
{
@@ -3208,6 +3214,11 @@ EXPORT_SYMBOL(lookup_one);
*
* Unlike lookup_one, it should be called without the parent
* i_rwsem held, and will take the i_rwsem itself if necessary.
+ *
+ * Returns:= A dentry, possibly negative, or
+ * - same errors as try_lookup_noperm() or
+ * - ERR_PTR(-ENOENT) if parent has been removed, or
+ * - ERR_PTR(-EACCES) if parent directory is not searchable.
*/
struct dentry *lookup_one_unlocked(struct mnt_idmap *idmap, struct qstr *name,
struct dentry *base)
@@ -3244,6 +3255,10 @@ EXPORT_SYMBOL(lookup_one_unlocked);
* It should be called without the parent i_rwsem held, and will take
* the i_rwsem itself if necessary. If a fatal signal is pending or
* delivered, it will return %-EINTR if the lock is needed.
+ *
+ * Returns: A dentry, possibly negative, or
+ * - same errors as lookup_one_unlocked() or
+ * - ERR_PTR(-EINTR) is a fatal signal is pending.
*/
struct dentry *lookup_one_positive_killable(struct mnt_idmap *idmap,
struct qstr *name,
@@ -3283,6 +3298,10 @@ EXPORT_SYMBOL(lookup_one_positive_killable);
* This can be used for in-kernel filesystem clients such as file servers.
*
* The helper should be called without i_rwsem held.
+ *
+ * Returns: A positive dentry, or
+ * - ERR_PTR(-ENOENT) if the name could not be found, or
+ * - same errors as lookup_one_unlocked().
*/
struct dentry *lookup_one_positive_unlocked(struct mnt_idmap *idmap,
struct qstr *name,
@@ -3311,6 +3330,10 @@ EXPORT_SYMBOL(lookup_one_positive_unlocked);
*
* Unlike try_lookup_noperm() it *does* revalidate the dentry if it already
* existed.
+ *
+ * Returns: A dentry, possibly negative, or
+ * - ERR_PTR(-ENOENT) if parent has been removed, or
+ * - same errors as try_lookup_noperm()
*/
struct dentry *lookup_noperm_unlocked(struct qstr *name, struct dentry *base)
{
@@ -3335,6 +3358,10 @@ EXPORT_SYMBOL(lookup_noperm_unlocked);
* _can_ become positive at any time, so callers of lookup_noperm_unlocked()
* need to be very careful; pinned positives have ->d_inode stable, so
* this one avoids such problems.
+ *
+ * Returns: A positive dentry, or
+ * - ERR_PTR(-ENOENT) if name cannot be found or parent has been removed, or
+ * - same errors as try_lookup_noperm()
*/
struct dentry *lookup_noperm_positive_unlocked(struct qstr *name,
struct dentry *base)
--
2.50.0.107.gf914562f5916.dirty
^ permalink raw reply related
* [PATCH v2 00/15] Further centralising of directory locking for name ops.
From: NeilBrown @ 2026-02-23 1:06 UTC (permalink / raw)
To: Christian Brauner, Alexander Viro, David Howells, Jan Kara,
Chuck Lever, Jeff Layton, Miklos Szeredi, Amir Goldstein,
John Johansen, Paul Moore, James Morris, Serge E. Hallyn,
Stephen Smalley, Darrick J. Wong
Cc: linux-kernel, netfs, linux-fsdevel, linux-nfs, linux-unionfs,
apparmor, linux-security-module, selinux
This is v2 of a series I sent shortly before the merge-window opened,
and now that it has closed ....
I have added:
01/15 to improve documentation as suggested by Darrick
11/15 as discussed with Amir to simplify the following patch
various RB and AB (thanks Jeff in particular)
I'm hoping this could land in vfs/ shortly (tihs month?). I will then have another
series of patches which make a small start in changing the locking
rules, which hopefully can also land in the next merge window.
Original patch description below.
Thanks,
NeilBrown
I am working towards changing the locking rules for name-operations: locking
the name rather than the whole directory.
The current part of this process is centralising all the locking so that
it can be changed in one place.
Recently "start_creating", "start_removing", "start_renaming" and related
interaces were added which combine the locking and the lookup. At that time
many callers were changed to use the new interfaces. However there are still
an assortment of places out side of fs/namei.c where the directory is locked
explictly, whether with inode_lock() or lock_rename() or similar. These were
missed in the first pass for an assortment of uninteresting reasons.
This series addresses the remaining places where explicit locking is
used, and changes them to use the new interfaces, or otherwise removes
the explicit locking.
The biggest changes are in overlayfs. The other changes are quite
simple, though maybe the cachefiles changes is the least simple of those.
I'm running the --overlay tests in xfstests and nothing has popped yet.
I'll continue with this and run some NFS tests too.
Thanks for your review of these patches!
NeilBrown
[PATCH v2 01/15] VFS: note error returns is documentation for various
[PATCH v2 02/15] fs/proc: Don't lock root inode when creating "self"
[PATCH v2 03/15] VFS: move the start_dirop() kerndoc comment to
[PATCH v2 04/15] libfs: change simple_done_creating() to use
[PATCH v2 05/15] Apparmor: Use simple_start_creating() /
[PATCH v2 06/15] selinux: Use simple_start_creating() /
[PATCH v2 07/15] nfsd: switch purge_old() to use
[PATCH v2 08/15] VFS: make lookup_one_qstr_excl() static.
[PATCH v2 09/15] ovl: Simplify ovl_lookup_real_one()
[PATCH v2 10/15] cachefiles: change cachefiles_bury_object to use
[PATCH v2 11/15] ovl: pass name buffer to ovl_start_creating_temp()
[PATCH v2 12/15] ovl: change ovl_create_real() to get a new lock when
[PATCH v2 13/15] ovl: use is_subdir() for testing if one thing is a
[PATCH v2 14/15] ovl: remove ovl_lock_rename_workdir()
[PATCH v2 15/15] VFS: unexport lock_rename(), lock_rename_child(),
^ permalink raw reply
* Re: [PATCH 05/13] selinux: Use simple_start_creating() / simple_done_creating()
From: NeilBrown @ 2026-02-23 0:58 UTC (permalink / raw)
To: Paul Moore
Cc: Christian Brauner, Alexander Viro, David Howells, Jan Kara,
Chuck Lever, Jeff Layton, Miklos Szeredi, Amir Goldstein,
John Johansen, James Morris, Serge E. Hallyn, Stephen Smalley,
linux-kernel, netfs, linux-fsdevel, linux-nfs, linux-unionfs,
apparmor, linux-security-module, selinux
In-Reply-To: <CAHC9VhTv+K44q7+5d17jS8h9fJY_JfQVUw5NPNvPzjkHDpqp=g@mail.gmail.com>
On Mon, 23 Feb 2026, Paul Moore wrote:
> On Sat, Feb 21, 2026 at 5:28 PM NeilBrown <neilb@ownmail.net> wrote:
> > On Sat, 21 Feb 2026, Paul Moore wrote:
> > > On Wed, Feb 4, 2026 at 12:08 AM NeilBrown <neilb@ownmail.net> wrote:
> > > >
> > > > From: NeilBrown <neil@brown.name>
> > > >
> > > > Instead of explicitly locking the parent and performing a lookup in
> > > > selinux, use simple_start_creating(), and then use
> > > > simple_done_creating() to unlock.
> > > >
> > > > This extends the region that the directory is locked for, and also
> > > > performs a lookup.
> > > > The lock extension is of no real consequence.
> > > > The lookup uses simple_lookup() and so always succeeds. Thus when
> > > > d_make_persistent() is called the dentry will already be hashed.
> > > > d_make_persistent() handles this case.
> > > >
> > > > Signed-off-by: NeilBrown <neil@brown.name>
> > > > ---
> > > > security/selinux/selinuxfs.c | 15 +++++++--------
> > > > 1 file changed, 7 insertions(+), 8 deletions(-)
> > >
> > > Unless I'm missing something, there is no reason why I couldn't take
> > > just this patch into the SELinux tree once the merge window closes,
> > > yes?
> >
> > Yes - but ...
> >
> > Once this series lands (hopefully soon - I will resend after -rc1 is
> > out) I have another batch which depends on the new start_creating etc
> > API being used everywhere ...
>
> Okay, thanks for letting me know. I was curious about something like
> that based on the cover letter, but the timing wasn't clear.
>
> Acked-by: Paul Moore <paul@paul-moore.com>
Thank!
NeilBrown
^ permalink raw reply
* [PATCH] apparmor: Use sysfs_emit in param_get_{audit,mode}
From: Thorsten Blum @ 2026-02-22 21:40 UTC (permalink / raw)
To: John Johansen, Paul Moore, James Morris, Serge E. Hallyn
Cc: Thorsten Blum, apparmor, linux-security-module, linux-kernel
Replace sprintf() with sysfs_emit() in param_get_audit() and
param_get_mode(). sysfs_emit() is preferred for formatting sysfs output
because it provides safer bounds checking. Add terminating newlines as
suggested by checkpatch.
Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
---
security/apparmor/lsm.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index a87cd60ed206..1250192f7b12 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -17,6 +17,7 @@
#include <linux/ptrace.h>
#include <linux/ctype.h>
#include <linux/sysctl.h>
+#include <linux/sysfs.h>
#include <linux/audit.h>
#include <linux/user_namespace.h>
#include <linux/netfilter_ipv4.h>
@@ -2081,7 +2082,7 @@ static int param_get_audit(char *buffer, const struct kernel_param *kp)
return -EINVAL;
if (apparmor_initialized && !aa_current_policy_view_capable(NULL))
return -EPERM;
- return sprintf(buffer, "%s", audit_mode_names[aa_g_audit]);
+ return sysfs_emit(buffer, "%s\n", audit_mode_names[aa_g_audit]);
}
static int param_set_audit(const char *val, const struct kernel_param *kp)
@@ -2109,8 +2110,7 @@ static int param_get_mode(char *buffer, const struct kernel_param *kp)
return -EINVAL;
if (apparmor_initialized && !aa_current_policy_view_capable(NULL))
return -EPERM;
-
- return sprintf(buffer, "%s", aa_profile_mode_names[aa_g_profile_mode]);
+ return sysfs_emit(buffer, "%s\n", aa_profile_mode_names[aa_g_profile_mode]);
}
static int param_set_mode(const char *val, const struct kernel_param *kp)
--
Thorsten Blum <thorsten.blum@linux.dev>
GPG: 1D60 735E 8AEF 3BE4 73B6 9D84 7336 78FD 8DFE EAD4
^ permalink raw reply related
* Re: [PATCH RESEND] apparmor: Replace memcpy + NUL termination with kmemdup_nul in do_setattr
From: Serge E. Hallyn @ 2026-02-22 20:53 UTC (permalink / raw)
To: Thorsten Blum
Cc: John Johansen, Paul Moore, James Morris, Serge E. Hallyn,
apparmor, linux-security-module, linux-kernel
In-Reply-To: <20260222204645.285727-1-thorsten.blum@linux.dev>
On Sun, Feb 22, 2026 at 09:46:44PM +0100, Thorsten Blum wrote:
> Use kmemdup_nul() to copy 'value' instead of using memcpy() followed by
> a manual NUL termination. No functional changes.
>
> Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
Looks correct, thanks.
Reviewed-by: Serge Hallyn <serge@hallyn.com>
> ---
> security/apparmor/lsm.c | 5 +----
> 1 file changed, 1 insertion(+), 4 deletions(-)
>
> diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
> index c1d42fc72fdb..49aa6ad68838 100644
> --- a/security/apparmor/lsm.c
> +++ b/security/apparmor/lsm.c
> @@ -858,12 +858,9 @@ static int do_setattr(u64 attr, void *value, size_t size)
>
> /* AppArmor requires that the buffer must be null terminated atm */
> if (args[size - 1] != '\0') {
> - /* null terminate */
> - largs = args = kmalloc(size + 1, GFP_KERNEL);
> + largs = args = kmemdup_nul(value, size, GFP_KERNEL);
> if (!args)
> return -ENOMEM;
> - memcpy(args, value, size);
> - args[size] = '\0';
> }
>
> error = -EINVAL;
> --
> Thorsten Blum <thorsten.blum@linux.dev>
> GPG: 1D60 735E 8AEF 3BE4 73B6 9D84 7336 78FD 8DFE EAD4
^ permalink raw reply
* [PATCH RESEND] apparmor: Replace memcpy + NUL termination with kmemdup_nul in do_setattr
From: Thorsten Blum @ 2026-02-22 20:46 UTC (permalink / raw)
To: John Johansen, Paul Moore, James Morris, Serge E. Hallyn
Cc: Thorsten Blum, apparmor, linux-security-module, linux-kernel
Use kmemdup_nul() to copy 'value' instead of using memcpy() followed by
a manual NUL termination. No functional changes.
Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
---
security/apparmor/lsm.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index c1d42fc72fdb..49aa6ad68838 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -858,12 +858,9 @@ static int do_setattr(u64 attr, void *value, size_t size)
/* AppArmor requires that the buffer must be null terminated atm */
if (args[size - 1] != '\0') {
- /* null terminate */
- largs = args = kmalloc(size + 1, GFP_KERNEL);
+ largs = args = kmemdup_nul(value, size, GFP_KERNEL);
if (!args)
return -ENOMEM;
- memcpy(args, value, size);
- args[size] = '\0';
}
error = -EINVAL;
--
Thorsten Blum <thorsten.blum@linux.dev>
GPG: 1D60 735E 8AEF 3BE4 73B6 9D84 7336 78FD 8DFE EAD4
^ permalink raw reply related
* Re: [PATCH] ucount: check for CAP_SYS_RESOURCE using ns_capable_noaudit()
From: Paul Moore @ 2026-02-22 20:44 UTC (permalink / raw)
To: Andrew Morton
Cc: Ondrej Mosnacek, Serge E. Hallyn, Eric W . Biederman,
linux-kernel, linux-security-module, selinux
In-Reply-To: <20260222115824.37ae0de1eb183997a23669ae@linux-foundation.org>
On Sun, Feb 22, 2026 at 2:58 PM Andrew Morton <akpm@linux-foundation.org> wrote:
>
> On Tue, 27 Jan 2026 17:09:40 -0500 Paul Moore <paul@paul-moore.com> wrote:
>
> > > > > > Fixes: dbec28460a89 ("userns: Add per user namespace sysctls.")
> > > > > > Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
> > > > > > ---
> > > > > > kernel/ucount.c | 2 +-
> > > > > > 1 file changed, 1 insertion(+), 1 deletion(-)
> > > > >
> > > > > Reviewed-by: Paul Moore <paul@paul-moore.com>
> > > >
> > > > Acked-by: Serge Hallyn <serge@hallyn.com>
> > > >
> > > > Looks good to me. What tree should this go through? Network?
> > >
> > > Andrew has already applied the two patches I posted into his
> > > mm-nonmm-unstable branch, so I assume they are set to go through his
> > > tree.
> >
> > Andrew, any chance we can get a reply to these threads when you merge
> > a patch into your tree?
>
> I already spray out soooo much email.
>
> A better approach would be for me to become more chatty. Reply
> "thanks, applied <reasons> <caveats> <etc>". Shall do this.
Appreciated, thank you.
--
paul-moore.com
^ permalink raw reply
* Re: [PATCH] ucount: check for CAP_SYS_RESOURCE using ns_capable_noaudit()
From: Andrew Morton @ 2026-02-22 19:58 UTC (permalink / raw)
To: Paul Moore
Cc: Ondrej Mosnacek, Serge E. Hallyn, Eric W . Biederman,
linux-kernel, linux-security-module, selinux
In-Reply-To: <CAHC9VhQOW4BQR+UCvrcXa_yiSqADN0HC2Xp0pMKs3FtnjcVaOQ@mail.gmail.com>
On Tue, 27 Jan 2026 17:09:40 -0500 Paul Moore <paul@paul-moore.com> wrote:
> > > > > Fixes: dbec28460a89 ("userns: Add per user namespace sysctls.")
> > > > > Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
> > > > > ---
> > > > > kernel/ucount.c | 2 +-
> > > > > 1 file changed, 1 insertion(+), 1 deletion(-)
> > > >
> > > > Reviewed-by: Paul Moore <paul@paul-moore.com>
> > >
> > > Acked-by: Serge Hallyn <serge@hallyn.com>
> > >
> > > Looks good to me. What tree should this go through? Network?
> >
> > Andrew has already applied the two patches I posted into his
> > mm-nonmm-unstable branch, so I assume they are set to go through his
> > tree.
>
> Andrew, any chance we can get a reply to these threads when you merge
> a patch into your tree?
I already spray out soooo much email.
A better approach would be for me to become more chatty. Reply
"thanks, applied <reasons> <caveats> <etc>". Shall do this.
^ permalink raw reply
* Re: [RFC] Landlock: mutable domains (and supervisor notification uAPI options)
From: Tingmao Wang @ 2026-02-22 18:04 UTC (permalink / raw)
To: Justin Suess, Günther Noack, Mickaël Salaün
Cc: amir73il, jack, jannh, linux-security-module, penguin-kernel,
song
In-Reply-To: <20260216212753.4159224-1-utilityemal77@gmail.com>
On 2/16/26 21:27, Justin Suess wrote:
> On Sun, Feb 15, 2026 at 02:54:08AM +0000, Tingmao Wang wrote:
>> [...]
>> The next stage is to introduce "mutable domains". The motivation for this
>> is two fold:
>>
>> 1. This allows the supervisor to allow access to (large) file hierarchies
>> without needing to be woken up again for each access.
>> 2. Because we cannot block within security_path_mknod and other
>> directory-modification related hooks [6], the proposal was to return
>> immediately from those hooks after queuing the supervisor notification,
>> then wait in a separate task_work. This however means that we cannot
>> directly "allow" access (and even if we can, it may introduce TOCTOU
>> problems). In order to allow access to requested files, the supervisor
>> has to add additional rules to the (now mutable) domain which will
>> allow the required access.
>
> Is blocking during connect(2) allowed either if the socket is non-blocking?
>
> This may be another example case that needs to be handled differently than calls
> we can block in safely.
I think the non-blocking socket case is worth considering but is
orthogonal to the above discussion - even when the socket is marked
non-blocking, we can still "safely" block in the connect hook (as long as
there isn't another reason, like locks, that prevents us from doing so),
because it only affects the calling process. Although if we're doing the
-ERESTARTNOINTR approach for fs, we might as well do the same here and
return -ERESTARTNOINTR from the connect hook (but that would still "block"
the syscall). Also, even for the file create/delete case, while we don't
block in the hook, from the user-space perspective, we're still blocking
(i.e. in the task_work), since the syscall doesn't immediately return (but
instead waits for a response from the supervisor).
However we could consider implementing some special case for
non-blocking-capable operations (like socket connect) such that we return
from the syscall without waiting for a supervisor response (and we return
a value that a "try again later" response would normally return), but then
we have to figure out how to cause a "you can retry now" notification that
would normally be sent for the non-blocking operation.
Related, there is the question of how we want to handle "non-blocking
open()s". Technically there isn't a non-blocking open() for regular files
from the user-space perspective, but ideally, for example, a wait on a
Landlock supervisor would not prevent an io_uring_enter() processing
multiple openat()s from continuing on to other requests. But I suspect
this would be very hard to implement.
> [...]
>> (Disallowing) self-supervision
>> ------------------------------
>>
>> We should figure out a way to ensure that a process cannot call
>> landlock_restrict_self() with a ruleset that has a supervisor for which it
>> has access to (i.e. via a supervisor ruleset fd). This prevents
>> accidental misuse, and also prevents deadlocks as discussed in [11]. I'm
>> not sure if this will be easy to implement, however.
>
> This seems like a graph acyclicity problem.
>
> Here are a couple cases to consider:
>
> 1. LANDLOCK_RESTRICT_SELF_TSYNC misuse:
>
> In the case where a user wants to use this supervisor to supervise other
> threads within the same process, a user could naively call
> LANDLOCK_RESTRICT_SELF_TSYNC (merged into 7.0) when enforcing the
> supervisee_fd. This would enforce the same policy on the thread running
> the supervisor and the supervisee.
Yes, but if the intention is to supervise threads within the same process
(which IMO is already a slightly questionable use case, maybe it can be
used as a tracing method, but certainly not for security), calling
restrict with LANDLOCK_RESTRICT_SELF_TSYNC is itself a mistake. There is
a case for us to try to detect and prevent this, but I'm thinking it would
more be to prevent "useless sandboxing" rather than to prevent deadlocks
(after all, it might not deadlock if someone else also has a fd for that
supervisor).
>
> 2. Transfer of the supervisee_fd (SCM_RIGHTS)
>
> It's possible to transfer file descriptors over unix domain sockets. If
> we had a supervisor daemon that used this form of IPC to send precooked
> supervisee_fds to other threads, and one of those ended up in a parent
> process of the supervisor, we could inadvertently end up with problems.
Note that being a parent process of the supervisor itself is not a
problem, what matters here is the Landlock domain relationships. It will
only be a problem if the fd is then used to make the supervisor be
supervised by itself (but if it's an unrelated parent process that is
supervised, it should still be fine).
>
> 3. Blocking in other LSMs (pointed out in your source [11])
>
> The hardest case to deal with, other LSMs like TOMOYO can also block and
> cause dependency cycles.
>
> ---
>
> This gets tricky, and I don't know if just checking parent / child
> relationships would work. Because the supervisor and supervisee rulesets
> are just file descriptors, and there are potentially unlimited number of
> ways these FDs could be transfered or instantiated.
>
> I think the best way to deal with this is constraining the problem space:
>
> An idea (binding supervisors/supervisees to domains on first use)
>
> Whenever landlock_restrict_self(supervisee_fd,...) is called, check the
> current domain credentials and verify that the domain is a *proper
> subset* of the supervisors domain. Then permanently close the
> supervisee_fd and never allow reenforcement. Similarly, once a
> supervisor_fd is created, never allow commiting from a context with
> "current landlock domain != original landlock domain at creation"
>
> This prevents post-enforcement usage of the supervisee_fd by a parent
> domain, and post-commit usage of a supervisee_fd by any subdomain.
It's not the supervisee fd that matters right? For example, a supervisor
can still call landlock_restrict_self() with the supervisee_fd it acquired
from the ioctl() (after all, this call itself is fine, it's only the fact
that there is no other process holding the supervisor fd that means this
gets us into a deadlock situation). I would think that if we
automatically close any fds, it would be the supervisor fd that we would
close. In this particular case this then means that there are no longer
any process holding the supervisor fd, which is a case we can detect, and
just deny any access request by the supervisee from that point forward
(preventing deadlock).
>
> I'm not sure if it's possible to check whether one domain is a
> proper subset of another (ie supervisor domain includes but *doesn't equal*
> supervisee domain), but I think that's one way do do it.
>
> This idea would help, but doesn't address case 3 above.
>
>>
>> [11]: https://lore.kernel.org/all/cc3e131f-f9a3-417b-9267-907b45083dc3@maowtm.org/
>>
>>
>> Supervisor notification
>> -----------------------
>>
>> The above RFC only covers mutable domains. The natural next stage of this
>> work is to send notification to the supervisor on access denials, so that
>> it can decide whether to allow the access or not. For that, there are
>> also lots of questions at this stage:
>>
>>
>> - Should we in fact implement that first, before mutable domains? This
>> means that the supervisor would only be able to find out about denials,
>> but not allow them without a sandbox restart. We still eventually want
>> the mutable domains, since that makes this a lot more useful, but I can
>> see some use cases for just the notification part (e.g. island denial
>> log), and I can't see a likely use case for just mutable domains, aside
>> from live reload of landlock-config (maybe that _is_ useful on its own,
>> considering that you can also find out about denials from the kernel
>> audit log, and add missing rules based on that).
>>
>>
>> - Earlier when implementing the Landlock supervise v1 RFC, I basically
>> came up with an ad-hoc uAPI for the notification [12], and the PoC code
>> linked to above also uses this uAPI. There are of course many problems
>> with this as it stands, e.g. it only having one destname, which means
>> that for rename, the fd1 needs to be the child being moved, which does
>> not align with the vfs semantic and how Landlock treat it (i.e. the
>> thing being updated here is the parent directory, not the child itself).
>> Same for delete, which currently sends the child as fd1.
>>
>> But also, in discussion with Mickaël last year, he mentioned that we
>> could reuse the fsnotify infrastructure, and perhaps additionally, use
>> fanotify to deliver these notifications. I do think there is some
>> potential here, as fanotify already implements an event header, a
>> mechanism for receiving and replying to events, etc. We could possibly
>> extend it to send Landlock specific notifications via a new kind of mark
>> (FAN_MARK_LANDLOCK_DOMAIN ??) and add one or more new corresponding
>> event types. Mickaël mentioned mount notifications [13] as an example
>> of using fanotify to send notifications other than file/dir
>> modifications.
>>
>> I'm not sure if directly extending the fanotify uAPI is a good idea tho,
>> considering that Landlock is not a feature specific to the filesystem -
>> we will also have denial events for net_port rules, and perhaps more in
>> the future. However, Mickaël mentioned that there might be some
>> internal infrastructure which we can re-use (even if we have our own
>> notification uAPI).
> I think that a new FAN_MARK would be required to use fanotify uAPI.
>
> There are a couple questions I have with this: (if we extend fanotify)
>
> 1. What FAN_CLASS_* would notifications use?
>
> FAN_CLASS_* specifies the type of notification, when the notification is
> triggered.
>
> See [1] for the current classes.
>
> If we want interactive, pre-access blocking, that would correspond to
> FAN_CLASS_PRE_CONTENT or FAN_CLASS_CONTENT. Both of which currently
> require CAP_SYS_ADMIN regardless of FAN_MARK. Which requiring that
> would require that supervisors have CAP_SYS_ADMIN, if the current
> CAP_SYS_ADMIN requirements remain in place.
>
> (If we don't have interactive blocking denials, we could just use
> FAN_CLASS_NOTIF)
I'm not sure, using fanotify is just a vague idea at this point and I
don't have any concrete design to offer, and this is indeed a good point -
we definitely don't want to require CAP_SYS_ADMIN (since being
unprivileged is one advantage of Landlock)
>
> 2. How would fanotify events be encoded?
>
> Events in fanotify use this structure for event data (one or more of the
> following must be recieved in a notification) [2]
>
> struct fanotify_event_metadata {
> __u32 event_len;
> __u8 vers;
> __u8 reserved;
> __u16 metadata_len;
> __aligned_u64 mask;
> __s32 fd;
> __s32 pid;
> };
>
> There are access classes landlock restricts that might not have an fd at
> all, like abstract unix sockets, tcp ports, signals etc.
This was also one of my concerns, I guess we might still need a cookie to
identify such events. This is one of the reasons why I think extending
seccomp-unotify makes slightly more sense (if we have to choose one of
these two, but of course we can still create a standalone uAPI).
>
> Good news is fanotify supports multiple types of additional information
> records, and we could potentially extend fanotify to support new ones as
> you alluded to.
>
> For examples of this, see struct fanotify_event_info_mnt,
> fanotify_event_info_pidfd.
>
> These records get attached to the event so they could be used to pass
> landlock access data.
>
> 3. If we support interactive permission decisions (even for a
> subset of landlock access rights only), do we use the response code?
> (question might be moot if we don't do blocking/responses at all)
Regardless of uAPI choice, I would really like to support blocking (even
though it makes this whole thing much more difficult).
>
> From [2]:
>
> For permission events, the application must write(2) a structure
> of the following form to the fanotify file descriptor:
>
> struct fanotify_response {
> __s32 fd;
> __u32 response;
> };
>
>
> response is a FAN_ALLOW or FAN_DENY. This is used by fanotify as a
> one-time access decision. Would this be used to do one-off exceptions to
> policy, or would we require policy decisions to go through the
> supervisor_fd and ignore the response code?
I don't think we would be able to do one-off allow decisions (but we
should still be able to do deny) regardless of the uAPI we use, due to the
problem with inode locks.
>
> 4. How would we reconcile the disparity between fanotify access rights
> and landlock access rights?
>
> There's no clean 1:1 mapping between fanotify access rights and landlock
> access rights as Mickaël pointed out. [2] [3]
>
> Many fs rights (creation, deletion, rename, linking) are not handled or
> implemented, (not even considering network/unix/signal scoping), so we'd
> be adding all these landlock specific rights.
>
> We could make a "catch-all" FAN_LANDLOCK_ACCESS or similar and ignore
> all the existing rights, and put the actual access data in the event
> record. It's awkward either way.
> ---
>
> In conclusion, I think extending fanotify is more viable than seccomp,
> from a purely technical standpoint. because it seems extensible,
> and because it runs post-lsm hooks.
>
> That being said, it's awkward, requires large extensions to the API, and
> definition of permissions that are specific to landlock.
>
> Whether or not landlock makes sense in fanotify from a semantic point of
> view is an entirely different question. There's no precedent for
> non-filesystem access controls in fanotify, so it's a little... out-of-place
> for an LSM to expose features on a filesystem access notification api?
>
> Curious on what people think.
>
> [1]: https://man7.org/linux/man-pages/man2/fanotify_init.2.html
> [2]: https://man7.org/linux/man-pages/man7/fanotify.7.html
> [3]: https://lore.kernel.org/all/20250304.Choo7foe2eoj@digikod.net/
>>
>>
>> - The other uAPI alternative which I have been thinking of is to extend
>> seccomp-unotify. For example, a Landlock denial could result in the
>> syscall being trapped and a `struct seccomp_notif` being sent to the
>> seccomp supervisor (via the existing mechanism), with additional
>> information (mostly, the file(s) / net ports being accessed and access
>> rights requested) attached to the notification _somehow_. Then the
>> supervisor can use the same kind of responses one would use for
>> seccomp-unotify to cause the syscall to either be retried (possibly via
>> `SECCOMP_USER_NOTIF_FLAG_CONTINUE`) or return with an error code of its
>> choice (or alternatively, carry out the operation on behalf of the
>> child, and pretend that the syscall succeed, which might be useful to
>> implement an "allow file creation but only this file" / "allow `mktemp
>> -d` but not arbitrary create on anything under /tmp").
>>
>> Looking at `struct seccomp_notif` and `struct seccomp_data` however, I'm
>> not sure how feasible / doable this extension would be. Also,
>> seccomp-unotify is supposed to trigger before a syscall is actually
>> executed, whereas if we use it this way, we will want it to trigger
>> after we're already midway through the syscall (in the LSM hook). This
>> might make it hard to implement (and also twists a bit the uAPI
>> semantics of seccomp-unotify).
>>
>
> (Some of the stuff discussed with seccomp below is derived from a side
> conversation with Tingmao over this proposal)
>
> There are some problems with extending seccomp unotify. Passing the
> full context needed through this api to the supervisor is problematic.
> seccomp unotify notifications look like this [4]:
>
> struct seccomp_notif {
> __u64 id; /* Cookie */
> __u32 pid; /* TID of target thread */
> __u32 flags; /* Currently unused (0) */
> struct seccomp_data data; /* See seccomp(2) */
> };
>
> And struct seccomp_data [5]:
>
> struct seccomp_data {
> int nr; /* System call number */
> __u32 arch; /* AUDIT_ARCH_* value
> (see <linux/audit.h>) */
> __u64 instruction_pointer; /* CPU instruction pointer */
> __u64 args[6]; /* Up to 6 system call arguments */
> };
>
> Even if we pass the syscall data, for the userspace to actually decode
> the arguments to figure out what the access is doing we have two
> critical problems (1,2) and one annoyance (3):
>
> 1. The syscall itself doesn't necessarily contain the full context of the access.
>
> 2. We cannot decode the pointer-based arguments from userspace for a syscall
> in seccomp without TOCTOU. It also requires reaching into userspace
> memory. [6]
>
> 3. Decoding the syscall number is an arch-specific operation that we now have
> to expect userspace to deal with.
We would probably need to attach additional (potentially variable length,
like file names for mknod / link / rename requests) Landlock-specific
information. The supervisor should not have to do any syscall decoding
(otherwise this partially defeats the point of Landlock supervise - they
can just use seccomp-unotify instead, and handling all "monitored"
syscalls on behalf of the supervisee to prevent TOCTOU).
Also, if we do not allow one-off allows (but require the supervisor to
modify the ruleset), this cannot lead to TOCTOU because if the supervised
process tries to change the argument, it will cause another Landlock
denial and notification.
Thanks for the review!
Tingmao
^ permalink raw reply
* Re: [RFC] Landlock: mutable domains (and supervisor notification uAPI options)
From: Tingmao Wang @ 2026-02-22 18:04 UTC (permalink / raw)
To: Justin Suess, Günther Noack, Mickaël Salaün
Cc: amir73il, jack, jannh, linux-security-module, penguin-kernel,
song
In-Reply-To: <20260215212353.3549464-1-utilityemal77@gmail.com>
On 2/15/26 21:23, Justin Suess wrote:
> On Sun, Feb 15, 2026 at 02:54:08AM +0000, Tingmao Wang wrote:
> [...]
>> Discussion on LANDLOCK_ADD_RULE_INTERSECT
>> -----------------------------------------
>>
>> This was initially proposed by Mickaël, although now after writing some
>> example code against it [7], I'm not 100% sure that it is the most useful
>> uAPI. For a supervisor based on some sort of config file, it already has
>> to track which rules are added to know what to remove, and thus I feel
>> that it would be easier (both to use and to implement) to have an API that
>> simply "replaces" a rule, rather than do a bitwise AND on the access.
>>
> Instead of intersection being done at the rule level via
> landlock_add_rule, would it be better for intersection to be done at the
> ruleset_fd/ruleset level?
>
> So instead of intersecting individual rules, you can intersect entire
> rulesets, with the added benefit of being able to intersect handled
> accesses as well. (so you could handle an access initially, and not
> handle it later).
Personally I don't think making the list of handled accesses mutable would
add a lot of value (after all, a sandbox would usually handle all accesses
that it knows of), and I would like to avoid the complexity of making the
list of handled accesses mutable. The semantic of "intersection" and
"union" of handled accesses is also not trivial: if ruleset A handles
read, and ruleset B handles read+write, their "intersection", if
interpreted as "only allow accesses allowed by both rulesets", would in
fact handle read+write (and their "union" would handle read only).
In the second (union) case, there is also the problem of what to do if
ruleset B has write access rules - these rules would technically become
invalid (although to no negative effect) in a ruleset that doesn't handle
write.
I do see the benefit of modifying scope bits (and maybe also
quiet_access_* bits), but I'm still worried about the extra complexity
(and thus also testing / docs needed etc)
>
> Intersecting at the ruleset level allows for grouping the intersection rules
> together, so you could create an unenforced ruleset for the sole purpose
> of intersecting with rulesets, and intersect all the rule(s) at once.
>
> That way, the ruleset fd can be reused for this purpose later with other
> supervisees, instead of creating ruleset, intersecting individual rules,
> repeat.
>
> I think also the semantics of having a function called
> "landlock_add_rule" actually removing accesses (when the intersect flag
> is added) is also confusing, because we're not really *add*-ing
> anything, we're removing.
>
> ALTERNATIVE #1
>
> Maybe the best way to do it is instead continue treating rulesets as
> immutable, but allow composition of them at ruleset creation time.
>
> This would look something like:
>
> Ruleset C = Ruleset A & Ruleset B
>
> Ruleset A and B are never modified, but instead a new Ruleset C is
> created that is the intersection of A and B. This could be done in a
> variety of ways (LANDLOCK_CREATE_RULESET_INTERSECT? new IOCTL?)
>
> An example API for what this might look like:
>
> struct landlock_ruleset_attr ruleset_attr = {
> // other fields for handled accesses must be blank.
> .left_fd = existing_fd,
> .right_fd = other_existing_fd,
> };
> int new_ruleset_fd = syscall(SYS_landlock_create_ruleset, &ruleset_attr,
> sizeof(ruleset_attr), LANDLOCK_CREATE_RULESET_INTERSECT);
>
> And then the resulting ruleset which is the intersection of existing_fd
> and other_existing_fd could be returned.
>
> Similarly, we could:
>
> int new_ruleset_fd = syscall(SYS_landlock_create_ruleset, &ruleset_attr,
> sizeof(ruleset_attr), LANDLOCK_CREATE_RULESET_UNION);
If we do keep with the "intersect" way of removing rules (instead of
replace / clear all), this does seem like an interesting idea. However,
it is more complex to implement (it will probably require traversing two
rbtrees at once to be implemented efficiently), and I'm not sure how much
utility this would add compared to just LANDLOCK_ADD_RULE_INTERSECT. See
below for more reasoning.
>
> Which would be convienent for creating unions of rulesets.
>
> Then instead mutating rulesets, we commit/replace an entirely new ruleset.
>
> ioctl(supervisee_fd, LANDLOCK_IOCTL_COMMIT_RULESET, &new_ruleset_fd);
Using a dedicated ioctl to commit is also a potentially better idea - I
find that having the commit be a side effect of landlock_add_rule() via a
flag a bit unwieldy, as it would either require the supervisor to track
when it adds the last rule, or to add an "empty" rule just to commit.
Mickaël, you initially suggested the LANDLOCK_ADD_RULE_COMMIT_SUPERVISOR
flag, but do you think this is better?
>
> This has the following benefits:
>
> 1. Clearer semantics: "landlock_add_rule" is just for adding rules, not
> removing.
>
> 2. Intersection of all ruleset attributes, not just individual rule
> attributes.
>
> 3. Better logical grouping of rules for the purpose of intersection, and
> better composition.
>
> It does have drawbacks:
>
> 1. Intersecting individual rules requires making an entire ruleset for
> that one rule.
>
> 2. Users must be responsible for closing the unused/old rulesets that
> they might not longer need.
>
> ALTERNATIVE #2
>
> A middle ground is to keep the ruleset mutation via landlock_add_rule,
> but have it be done at the ruleset_fd level.
>
> Something like this:
>
> struct landlock_ruleset_operand intersection = {
> .operand = other_ruleset_fd
> };
> landlock_add_rule(ruleset_fd, LANDLOCK_RULE_INTERSECT_RULESET, &intersection, 0))
>
> I think this is also a valid way to do things, and increases the
> reusibility of rulesets.
>
> 1. Again, having landlock_add_rule being used to actually remove rules
> is confusing.
In this case, wouldn't we also be removing rules via landlock_add_rule()?
Personally I feel like this inconsistency is tolerable (it's easy enough
to explain), but I guess we could also change this to an ioctl if this is
a problem.
>
> 2. I'm unsure if we can change handled accesses after ruleset creation,
> so we might not be able to intersect the handled accesses like we can in
> the ALTERNATIVE #1.
>
>> Another alternative is to simply have a "clear all rules in this ruleset"
>> flag. This allows the supervisor to not have to track what is already
>> allowed - if it reloads the config file, it can simply clear the ruleset,
>> re-add all rules based on the config, then commit it. Although I worry
>> that this might make implementing some other use cases more difficult.
>
> At a minimum, it is cumbersome, and I worry about file descriptors
> becoming inaccessible (due to bind mounts / namespace changes in the
> supervisor's environment).
>
> Of course they can just hold those file descriptors open for the purposes
> of future intersections, but this is annoying and error prone.
If a supervisor doesn't care about potential renames / mount / namespace
changes making the sandboxed application lose access to previously
accessible files, the "clear all rules" approach would not force it to
keep any fds open in order to remove rules (i.e. it can clear everything,
then re-open the fds to add the rules back). On the other hand, with the
"intersect" approach, it would have to keep the fds open in all cases to
correctly remove previously added rules, so I think this "clear all" is
not more cumbersome.
There is a general consideration here about how much we want to design the
API to advantage / disadvantage particular ways of using it. For example,
having ruleset-ruleset intersection / union operations would (in theory,
setting aside the fact that to remain compatible to older kernels it
cannot do this for constructions of existing static rulesets) work very
well for something like island [1] / landlock-config, where we compose
rulesets by intersecting / unioning different landlock configuration files
together. However, it will introduce more complexity to someone who just
wants to allow access as they come up (e.g. something like a "permissive /
learning mode"), as they now have to, every time when an access is denied,
create a new ruleset, add one single rule, and do a union.
IMO, there is also the general preference of having complicated logic
being in user-space rather than implemented by the kernel. In this case,
one can argue that for someone that wants to compose rulesets via logical
operations, this should really be handled by a Landlock library, which
could, for static rulesets, do it completely internally in-memory, and do
one landlock_create_ruleset() + n landlock_add_rule()s in the end. For
live modifications, it could then use the more low-level "intersect" /
"add rule" / "clear all" uAPI. Compared to intersecting single rules,
having the kernel do the logical operations on entire rulesets also
doesn't reduce the number of syscalls needed (you still need one
landlock_add_rule() for each rule to be modified), so there is not a
performance argument either
(although for the use case where the supervisor wants to incrementally add
and remove rules, there is a performance benefit to intersect vs "clear
all". But I do wonder how often in practice this would be implemented for
a supervisor that can remove rules - because it needs to keep an in-memory
table for the fds it has open anyway, in order to correctly remove rules,
a simpler approach would be to simply clear all, then re-add whatever it
wants based on what it still has in that table).
[1]: https://github.com/landlock-lsm/island
^ permalink raw reply
* Re: [RFC] Landlock: mutable domains (and supervisor notification uAPI options)
From: Tingmao Wang @ 2026-02-22 18:04 UTC (permalink / raw)
To: Günther Noack, Mickaël Salaün, Justin Suess
Cc: linux-security-module
In-Reply-To: <6981cdab-dcff-47fc-a236-1890f9bc9c55@maowtm.org>
On 2/15/26 02:54, Tingmao Wang wrote:
> PoC code used in the above videos are largely generated, somewhat buggy,
> and unreviewed, but they are available:
>
> - mutable domains:
> https://github.com/micromaomao/linux-dev/pull/26/changes
> - supervisor notification:
> https://github.com/micromaomao/linux-dev/pull/27/changes
btw, on second thought I should have clarified that I don't expect anyone
to review any of the code here. Those were done purely to ensure that the
design I'm asking for review here actually works, and I'm open to any
changes.
^ permalink raw reply
* Re: [PATCH 05/13] selinux: Use simple_start_creating() / simple_done_creating()
From: Paul Moore @ 2026-02-22 14:19 UTC (permalink / raw)
To: NeilBrown
Cc: Christian Brauner, Alexander Viro, David Howells, Jan Kara,
Chuck Lever, Jeff Layton, Miklos Szeredi, Amir Goldstein,
John Johansen, James Morris, Serge E. Hallyn, Stephen Smalley,
linux-kernel, netfs, linux-fsdevel, linux-nfs, linux-unionfs,
apparmor, linux-security-module, selinux
In-Reply-To: <177171292163.8396.10671162503209732019@noble.neil.brown.name>
On Sat, Feb 21, 2026 at 5:28 PM NeilBrown <neilb@ownmail.net> wrote:
> On Sat, 21 Feb 2026, Paul Moore wrote:
> > On Wed, Feb 4, 2026 at 12:08 AM NeilBrown <neilb@ownmail.net> wrote:
> > >
> > > From: NeilBrown <neil@brown.name>
> > >
> > > Instead of explicitly locking the parent and performing a lookup in
> > > selinux, use simple_start_creating(), and then use
> > > simple_done_creating() to unlock.
> > >
> > > This extends the region that the directory is locked for, and also
> > > performs a lookup.
> > > The lock extension is of no real consequence.
> > > The lookup uses simple_lookup() and so always succeeds. Thus when
> > > d_make_persistent() is called the dentry will already be hashed.
> > > d_make_persistent() handles this case.
> > >
> > > Signed-off-by: NeilBrown <neil@brown.name>
> > > ---
> > > security/selinux/selinuxfs.c | 15 +++++++--------
> > > 1 file changed, 7 insertions(+), 8 deletions(-)
> >
> > Unless I'm missing something, there is no reason why I couldn't take
> > just this patch into the SELinux tree once the merge window closes,
> > yes?
>
> Yes - but ...
>
> Once this series lands (hopefully soon - I will resend after -rc1 is
> out) I have another batch which depends on the new start_creating etc
> API being used everywhere ...
Okay, thanks for letting me know. I was curious about something like
that based on the cover letter, but the timing wasn't clear.
Acked-by: Paul Moore <paul@paul-moore.com>
--
paul-moore.com
^ permalink raw reply
* Re: [PATCH 05/13] selinux: Use simple_start_creating() / simple_done_creating()
From: NeilBrown @ 2026-02-21 22:28 UTC (permalink / raw)
To: Paul Moore
Cc: Christian Brauner, Alexander Viro, David Howells, Jan Kara,
Chuck Lever, Jeff Layton, Miklos Szeredi, Amir Goldstein,
John Johansen, James Morris, Serge E. Hallyn, Stephen Smalley,
linux-kernel, netfs, linux-fsdevel, linux-nfs, linux-unionfs,
apparmor, linux-security-module, selinux
In-Reply-To: <CAHC9VhThChVk1Dk+f-KANGj7Tu7zzHCiA==taeQ+=nQaH6a7sg@mail.gmail.com>
On Sat, 21 Feb 2026, Paul Moore wrote:
> On Wed, Feb 4, 2026 at 12:08 AM NeilBrown <neilb@ownmail.net> wrote:
> >
> > From: NeilBrown <neil@brown.name>
> >
> > Instead of explicitly locking the parent and performing a lookup in
> > selinux, use simple_start_creating(), and then use
> > simple_done_creating() to unlock.
> >
> > This extends the region that the directory is locked for, and also
> > performs a lookup.
> > The lock extension is of no real consequence.
> > The lookup uses simple_lookup() and so always succeeds. Thus when
> > d_make_persistent() is called the dentry will already be hashed.
> > d_make_persistent() handles this case.
> >
> > Signed-off-by: NeilBrown <neil@brown.name>
> > ---
> > security/selinux/selinuxfs.c | 15 +++++++--------
> > 1 file changed, 7 insertions(+), 8 deletions(-)
>
> Unless I'm missing something, there is no reason why I couldn't take
> just this patch into the SELinux tree once the merge window closes,
> yes?
Yes - but ...
Once this series lands (hopefully soon - I will resend after -rc1 is
out) I have another batch which depends on the new start_creating etc
API being used everywhere. So for Christian to be able to apply that,
he will need to pull in this patch from the SELinux tree.
So if you could apply just this patch to some branch and merge that
branch with your other work however works best for you, and make the
branch available, then I think Christian will be happy to merge that
with whatever vfs branch he includes my work in, and all should be good.
Thanks
NeilBrown
>
> --
> paul-moore.com
>
^ permalink raw reply
* [PATCH v2] landlock: Expand restrict flags example for ABI version 8
From: Panagiotis "Ivory" Vasilopoulos @ 2026-02-21 22:12 UTC (permalink / raw)
To: Mickaël Salaün, Günther Noack, Jonathan Corbet,
Shuah Khan
Cc: linux-security-module, linux-doc, linux-kernel
Add LANDLOCK_RESTRICT_SELF_TSYNC to the backwards compatibility example
for restrict flags. This introduces completeness, similar to that of
the ruleset attributes example.
Additionally, I modified the two comments of the example to make them
more consistent with the ruleset attributes example's.
Signed-off-by: Panagiotis 'Ivory' Vasilopoulos <git@n0toose.net>
---
Changes in v2:
- Fix formatting error.
- Link to v1: https://lore.kernel.org/r/20260221-landlock-docs-add-tsync-example-v1-1-f89383809eb4@n0toose.net
---
Documentation/userspace-api/landlock.rst | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst
index 13134bccdd39d78ddce3daf454f32dda162ce91b..0affe1c953d61a4b32aca700cd262c49cee6304a 100644
--- a/Documentation/userspace-api/landlock.rst
+++ b/Documentation/userspace-api/landlock.rst
@@ -197,12 +197,18 @@ similar backwards compatibility check is needed for the restrict flags
.. code-block:: c
- __u32 restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON;
- if (abi < 7) {
- /* Clear logging flags unsupported before ABI 7. */
+ __u32 restrict_flags =
+ LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON |
+ LANDLOCK_RESTRICT_SELF_TSYNC;
+ switch (abi) {
+ case 1 ... 6:
+ /* Clear logging flags unsupported for ABI < 7 */
restrict_flags &= ~(LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF |
LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON |
LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF);
+ case 7:
+ /* Removes multithread flag unsupported for ABI < 8 */
+ restrict_flags &= ~LANDLOCK_RESTRICT_SELF_TSYNC;
}
The next step is to restrict the current thread from gaining more privileges
---
base-commit: ceb977bfe9e8715e6cd3a4785c7aab8ea5cd2b77
change-id: 20260221-landlock-docs-add-tsync-example-e8fd5c64a366
Best regards,
--
Panagiotis "Ivory" Vasilopoulos <git@n0toose.net>
^ permalink raw reply related
* [PATCH] landlock: Expand restrict flags example for ABI version 8
From: Panagiotis "Ivory" Vasilopoulos @ 2026-02-21 22:05 UTC (permalink / raw)
To: Mickaël Salaün, Günther Noack, Jonathan Corbet,
Shuah Khan
Cc: linux-security-module, linux-doc, linux-kernel
Add LANDLOCK_RESTRICT_SELF_TSYNC to the backwards compatibility example
for restrict flags. This introduces completeness, similar to that of
the ruleset attributes example.
Additionally, I modified the two comments of the example to make them
more consistent with the ruleset attributes example's.
Signed-off-by: Panagiotis 'Ivory' Vasilopoulos <git@n0toose.net>
---
Documentation/userspace-api/landlock.rst | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst
index 13134bccdd39d78ddce3daf454f32dda162ce91b..d972af20791a722d4665bd2ac287b05c5e97248c 100644
--- a/Documentation/userspace-api/landlock.rst
+++ b/Documentation/userspace-api/landlock.rst
@@ -197,12 +197,18 @@ similar backwards compatibility check is needed for the restrict flags
.. code-block:: c
- __u32 restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON;
- if (abi < 7) {
- /* Clear logging flags unsupported before ABI 7. */
+ __u32 restrict_flags =
+ LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON |
+ LANDLOCK_RESTRICT_SELF_TSYNC;
+ switch (abi) {
+ case 1 ... 6:
+ /* Clear logging flags unsupported for ABI < 7 */
restrict_flags &= ~(LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF |
LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON |
LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF);
+ case 7:
+ /* Removes multithread flag unsupported for ABI < 8 */
+ restrict_flags &= ~LANDLOCK_RESTRICT_SELF_TSYNC;
}
The next step is to restrict the current thread from gaining more privileges
---
base-commit: ceb977bfe9e8715e6cd3a4785c7aab8ea5cd2b77
change-id: 20260221-landlock-docs-add-tsync-example-e8fd5c64a366
Best regards,
--
Panagiotis "Ivory" Vasilopoulos <git@n0toose.net>
^ permalink raw reply related
* Re: [PATCH v4 15/17] module: Introduce hash-based integrity checking
From: Nicolas Schier @ 2026-02-21 21:38 UTC (permalink / raw)
To: Thomas Weißschuh
Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Daniel Gomez,
Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Bouchinet,
Xiu Jianfeng, Fabian Grünbichler, Arnout Engelen,
Mattia Rizzolo, kpcyrd, Christian Heusel, Câju Mihai-Drosi,
Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
linux-modules, linux-security-module, linux-doc, linuxppc-dev,
linux-integrity
In-Reply-To: <20260113-module-hashes-v4-15-0b932db9b56b@weissschuh.net>
Hi Thomas,
On Tue, Jan 13, 2026 at 01:28:59PM +0100, Thomas Weißschuh wrote:
> The current signature-based module integrity checking has some drawbacks
> in combination with reproducible builds. Either the module signing key
> is generated at build time, which makes the build unreproducible, or a
> static signing key is used, which precludes rebuilds by third parties
> and makes the whole build and packaging process much more complicated.
>
> The goal is to reach bit-for-bit reproducibility. Excluding certain
> parts of the build output from the reproducibility analysis would be
> error-prone and force each downstream consumer to introduce new tooling.
>
> Introduce a new mechanism to ensure only well-known modules are loaded
> by embedding a merkle tree root of all modules built as part of the full
> kernel build into vmlinux.
>
> Non-builtin modules can be validated as before through signatures.
>
> Normally the .ko module files depend on a fully built vmlinux to be
> available for modpost validation and BTF generation. With
> CONFIG_MODULE_HASHES, vmlinux now depends on the modules
> to build a merkle tree. This introduces a dependency cycle which is
> impossible to satisfy. Work around this by building the modules during
> link-vmlinux.sh, after vmlinux is complete enough for modpost and BTF
> but before the final module hashes are
>
> The PKCS7 format which is used for regular module signatures can not
> represent Merkle proofs, so a new kind of module signature is
> introduced. As this signature type is only ever used for builtin
> modules, no compatibility issues can arise.
>
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
> .gitignore | 1 +
> Documentation/kbuild/reproducible-builds.rst | 5 +-
> Makefile | 8 +-
> include/asm-generic/vmlinux.lds.h | 11 +
> include/linux/module_hashes.h | 25 ++
> include/linux/module_signature.h | 1 +
> kernel/module/Kconfig | 21 +-
> kernel/module/Makefile | 1 +
> kernel/module/hashes.c | 92 ++++++
> kernel/module/hashes_root.c | 6 +
> kernel/module/internal.h | 1 +
> kernel/module/main.c | 4 +-
> scripts/.gitignore | 1 +
> scripts/Makefile | 3 +
> scripts/Makefile.modfinal | 11 +
> scripts/Makefile.modinst | 13 +
> scripts/Makefile.vmlinux | 5 +
> scripts/link-vmlinux.sh | 14 +-
> scripts/modules-merkle-tree.c | 467 +++++++++++++++++++++++++++
> security/lockdown/Kconfig | 2 +-
> 20 files changed, 685 insertions(+), 7 deletions(-)
>
[...]
> diff --git a/kernel/module/hashes_root.c b/kernel/module/hashes_root.c
> new file mode 100644
> index 000000000000..1abfcd3aa679
> --- /dev/null
> +++ b/kernel/module/hashes_root.c
> @@ -0,0 +1,6 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +
> +#include <linux/module_hashes.h>
> +
> +/* Blank dummy data. Will be overridden by link-vmlinux.sh */
> +const struct module_hashes_root module_hashes_root __module_hashes_section = {};
> diff --git a/kernel/module/internal.h b/kernel/module/internal.h
> index e2d49122c2a1..e22837d3ac76 100644
> --- a/kernel/module/internal.h
> +++ b/kernel/module/internal.h
> @@ -338,6 +338,7 @@ void module_mark_ro_after_init(const Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
> const char *secstrings);
>
> int module_sig_check(struct load_info *info, const u8 *sig, size_t sig_len);
> +int module_hash_check(struct load_info *info, const u8 *sig, size_t sig_len);
>
> #ifdef CONFIG_DEBUG_KMEMLEAK
> void kmemleak_load_module(const struct module *mod, const struct load_info *info);
> diff --git a/kernel/module/main.c b/kernel/module/main.c
> index 2a28a0ece809..fa30b6387936 100644
> --- a/kernel/module/main.c
> +++ b/kernel/module/main.c
> @@ -3362,8 +3362,10 @@ static int module_integrity_check(struct load_info *info, int flags)
>
> if (IS_ENABLED(CONFIG_MODULE_SIG) && sig_type == PKEY_ID_PKCS7) {
> err = module_sig_check(info, sig, sig_len);
> + } else if (IS_ENABLED(CONFIG_MODULE_HASHES) && sig_type == PKEY_ID_MERKLE) {
> + err = module_hash_check(info, sig, sig_len);
> } else {
> - pr_err("module: not signed with expected PKCS#7 message\n");
> + pr_err("module: not signed with signature mechanism\n");
> err = -ENOPKG;
To prevent others from running into the same issue:
My first test got stuck here, as I tested with virtme-ng, which symlinks
modules from build tree to /lib/modules/$(uname -r)/..., resulting in
[ 15.956855] module: not signed with signature mechanism
modprobe: ERROR: could not insert 'efivarfs': Package not installed
As the modules_install step was missing, modules were not being signed.
[...]
> diff --git a/scripts/modules-merkle-tree.c b/scripts/modules-merkle-tree.c
> new file mode 100644
> index 000000000000..a6ec0e21213b
> --- /dev/null
> +++ b/scripts/modules-merkle-tree.c
> @@ -0,0 +1,467 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Compute hashes for modules files and build a merkle tree.
> + *
> + * Copyright (C) 2025 Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
> + * Copyright (C) 2025 Thomas Weißschuh <linux@weissschuh.net>
> + *
> + */
> +#define _GNU_SOURCE 1
> +#include <arpa/inet.h>
> +#include <err.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <stdarg.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <stdbool.h>
> +#include <stdlib.h>
> +
> +#include <sys/stat.h>
> +#include <sys/mman.h>
> +
> +#include <openssl/evp.h>
> +#include <openssl/err.h>
> +
> +#include "ssl-common.h"
> +
> +static int hash_size;
> +static EVP_MD_CTX *ctx;
> +
> +struct module_signature {
> + uint8_t algo; /* Public-key crypto algorithm [0] */
> + uint8_t hash; /* Digest algorithm [0] */
> + uint8_t id_type; /* Key identifier type [PKEY_ID_PKCS7] */
> + uint8_t signer_len; /* Length of signer's name [0] */
> + uint8_t key_id_len; /* Length of key identifier [0] */
> + uint8_t __pad[3];
> + uint32_t sig_len; /* Length of signature data */
> +};
> +
> +#define PKEY_ID_MERKLE 3
> +
> +static const char magic_number[] = "~Module signature appended~\n";
This here will be the forth definition of struct module_signature,
increasing the risk of unwanted diversion. I second Petr's suggestion
to reuse a _common_ definition instead.
(Here, even include/linux/module_signature.h could be included itself.)
> +
> +struct file_entry {
> + char *name;
> + unsigned int pos;
> + unsigned char hash[EVP_MAX_MD_SIZE];
> +};
> +
> +static struct file_entry *fh_list;
> +static size_t num_files;
> +
> +struct leaf_hash {
> + unsigned char hash[EVP_MAX_MD_SIZE];
> +};
> +
> +struct mtree {
> + struct leaf_hash **l;
> + unsigned int *entries;
> + unsigned int levels;
> +};
> +
> +static inline void *xcalloc(size_t n, size_t size)
> +{
> + void *p;
> +
> + p = calloc(n, size);
> + if (!p)
> + errx(1, "Memory allocation failed");
> +
> + return p;
> +}
> +
> +static void *xmalloc(size_t size)
> +{
> + void *p;
> +
> + p = malloc(size);
> + if (!p)
> + errx(1, "Memory allocation failed");
> +
> + return p;
> +}
> +
> +static inline void *xreallocarray(void *oldp, size_t n, size_t size)
> +{
> + void *p;
> +
> + p = reallocarray(oldp, n, size);
> + if (!p)
> + errx(1, "Memory allocation failed");
> +
> + return p;
> +}
> +
> +static inline char *xasprintf(const char *fmt, ...)
> +{
> + va_list ap;
> + char *strp;
> + int ret;
> +
> + va_start(ap, fmt);
> + ret = vasprintf(&strp, fmt, ap);
> + va_end(ap);
> + if (ret == -1)
> + err(1, "Memory allocation failed");
> +
> + return strp;
> +}
Please consider moving these x* functions into scripts/include/xalloc.h
for reuse. (I am sure someone else wrote this already, but I can't find
it...)
>
>
thanks for all your efforts for reproducibility!
As I have no clue about that: Is the patent for merkle trees [1] a
problem when integrating that here?
Can you verify if I get the mechanics roughly correct?
* Modules are merkle tree leaves. Modules are built and logically
paired by the order from modules.order; a single left-over module is
paired with itself.
* Hashes of paired modules are hashed again (branch node hash);
hashes of pairs of branch nodes' hashes are hashed again;
repeat until we reach the single merkle tree root hash
* The final merkle tree root hash (and the count of tree levels) is
included in vmlinux
'make && find . -name '*.ko' -exec rm {} \; && make' does not rebuild
the in-tree modules. Shifting the module-hashes support from
scripts/link-vmlinux.sh to scripts/Makefile.vmlinux might (make it
easier) to fix this again.
Kind regards,
Nicolas
[1]: https://worldwide.espacenet.com/patent/search/family/022107098/publication/US4309569A?q=pn%3DUS4309569
--
Nicolas
^ permalink raw reply
* Re: [PATCH v6] lsm: Add LSM hook security_unix_find
From: Justin Suess @ 2026-02-21 13:22 UTC (permalink / raw)
To: Günther Noack
Cc: brauner, demiobenour, fahimitahera, hi, horms, ivanov.mikhail1,
jannh, jmorris, john.johansen, konstantin.meskhidze,
linux-security-module, m, matthieu, mic, netdev, paul,
samasth.norway.ananda, serge, viro
In-Reply-To: <20260220.70d0460d1309@gnoack.org>
On Fri, Feb 20, 2026 at 04:49:34PM +0100, Günther Noack wrote:
> Hello!
>
> On Thu, Feb 19, 2026 at 03:04:59PM -0500, Justin Suess wrote:
> > diff --git a/security/security.c b/security/security.c
> > index 67af9228c4e9..c73196b8db4b 100644
> > --- a/security/security.c
> > +++ b/security/security.c
> > @@ -4731,6 +4731,26 @@ int security_mptcp_add_subflow(struct sock *sk, struct sock *ssk)
> >
> > #endif /* CONFIG_SECURITY_NETWORK */
> >
> > +#if defined(CONFIG_SECURITY_NETWORK) && defined(CONFIG_SECURITY_PATH)
> > +/**
> > + * security_unix_find() - Check if a named AF_UNIX socket can connect
> > + * @path: path of the socket being connected to
> > + * @other: peer sock
> > + * @flags: flags associated with the socket
> > + *
> > + * This hook is called to check permissions before connecting to a named
> > + * AF_UNIX socket.
>
> Nit: Could we please insert a sentence about locking here?
>
> Something like:
>
> The caller holds no locks on @other.
>
> (Originally brought up by Mickaël in
> https://lore.kernel.org/all/20260217.lievaS8eeng8@digikod.net/)
>
> Thanks,
> –Günther
Sounds good. Would a "Link:" to the mentioned thread be appropriate in the commit
message?
I feel like the reasoning for this is subtle but important for hook
consumers.
Justin
^ permalink raw reply
* Re: [syzbot] [kernel?] INFO: task hung in restrict_one_thread_callback
From: Günther Noack @ 2026-02-21 13:19 UTC (permalink / raw)
To: Ding Yihan
Cc: syzbot, Mickaël Salaün, linux-security-module,
Jann Horn
In-Reply-To: <20260221.5d8a306bcaf1@gnoack.org>
On Sat, Feb 21, 2026 at 01:00:03PM +0100, Günther Noack wrote:
> (Very) tentative investigation:
>
> In the Syzkaller report [2], it seems that the reproducer [2.1] is
> creating two rulesets and then enforcing them in parallel, a scenario
> which we are exercising in the TEST(competing_enablement) in
> tools/testing/selftests/landlock/tsync_test.c already, but which has
> not failed in my own selftest runs.
>
> In the crash report, there are four threads in total:
>
> * Two are stuck in the line
> wait_for_completion(&ctx->ready_to_commit);
> in the per-thread task work (line 128 [4.1])
> * Two are stuck in the line
> wait_for_completion(&shared_ctx.all_prepared)
> in the calling thread's coordination logic (line 539 [4.2])
>
> In line 539, we are already on the code path where we detected that we
> are getting interrupted by another thread and where we are attempting
> to deal with the scenario where two landlock_restrict_self() calls
> compete. This is detected on line 523 when
> wait_for_completion_interruptible() is true. The approach to handle
> this is to set the overall -ERESTARTNOINTR error and cancel the work
> that has been ongoing so far, by canceling the task works that did not
> start running yet and waiting for the ones that did start running
> (that is the step where we are blocked!). The reasoning there was
> that these task works will all hit the "all_prepared" stage now, but
> as we can see in the stack trace, the task works that are actively
> running are already on line 128 and have passed the "all_prepared"
> stage).
>
> Differences I can see between syzkaller and our own test:
>
> * The reproducer also calls openat() and then twice socketpair().
> These syscalls should be unrelated, but it's possible that the
> "async" invocation of socketpair() contributes to adding more
> threads. (Assuming that "async" means "in new thread" in syzkaller)
> * Syzkaller gives it more attempts. ([2.2])
>
> I do not understand yet what went wrong in our scheme and need to look
> deeper.
OK, I think I understand now. Our existing recovery code for this
conflict is this:
/*
* Decrement num_preparing for current, to undo that we initialized it
* to 1 a few lines above.
*/
if (atomic_dec_return(&shared_ctx.num_preparing) > 0) {
if (wait_for_completion_interruptible(
&shared_ctx.all_prepared)) {
/* In case of interruption, we need to retry the system call. */
atomic_set(&shared_ctx.preparation_error,
-ERESTARTNOINTR);
/*
* Cancel task works for tasks that did not start running yet,
* and decrement all_prepared and num_unfinished accordingly.
*/
cancel_tsync_works(&works, &shared_ctx);
/*
* The remaining task works have started running, so waiting for
* their completion will finish.
*/
wait_for_completion(&shared_ctx.all_prepared);
}
}
When I wrote this, I assumed, as the last comment states, that the
task works which we could not cancel, are already running.
I was wrong there, because I had misunderstood task_work_run(). When
the task works get run there, it first *atomically dequeues the entire
queue of scheduled task works*, and then runs them sequentially.
That is why, if we have one task work that belongs to the first
landlock_restrict_self() call and one which belongs to the other, the
task work which is scheduled later can (a) not be dequeued with
cancel_tsync_works() any more, and (b) also has not started running
yet.
Now the only thing that is necessary to produce the deadlock is that
we have a pair of threads where the task works for the restriction
calls have been scheduled in different order. When the two
landlock_restrict_self() calls end up in the recovery path quoted
above, they will wait for one of their task works to run which is
blocked from running by another task work that is scheduled before and
does not finish either.
(Just pasting a brain dump here to save you some time hunting for the
root cause. I don't know the best solution yet either.)
–Günther
^ permalink raw reply
* Re: [syzbot] [kernel?] INFO: task hung in restrict_one_thread_callback
From: Günther Noack @ 2026-02-21 12:00 UTC (permalink / raw)
To: Ding Yihan
Cc: syzbot, Mickaël Salaün, linux-security-module,
Jann Horn
In-Reply-To: <00A9E53EDC82309F+7b1dfc69-95f8-4ffc-a67c-967de0e2dfee@uniontech.com>
Hello Ding!
On Sat, Feb 21, 2026 at 03:28:47PM +0800, Ding Yihan wrote:
> Since I am relatively new to the inner workings of this specific subsystem,
> I would like to take a few days to thoroughly study the root cause
> (the task_work and mutex interaction) and prepare a detailed and proper commit message.
>
> I will send out the formal patch (v1) to the mailing list later.
Thank you very much for preparing a patch, and especially also for
forwarding this to us. (The original syzkaller report was somehow not
addressed to Landlock or the LSM list. We should fix that.)
Timing wise, the feature was picked up for the 7.0 release, so we
still have some time to fix it before this is stable.
As an early review for the patch:
Background:
We had previously convinced ourselves that grabbing the
cred_guard_mutex was not necessary. To quote the comment in
landlock_restrict_sibling_threads():
Unlike seccomp, which modifies sibling tasks directly, we do not need to
acquire the cred_guard_mutex and sighand->siglock:
- As in our case, all threads are themselves exchanging their own struct
cred through the credentials API, no locks are needed for that.
- Our for_each_thread() loops are protected by RCU.
- We do not acquire a lock to keep the list of sibling threads stable
between our for_each_thread loops. If the list of available sibling
threads changes between these for_each_thread loops, we make up for
that by continuing to look for threads until they are all discovered
and have entered their task_work, where they are unable to spawn new
threads.
The question of locking cred_guard_mutex came up in the patch
discussion multiple times as well, the most recent discussion was:
https://lore.kernel.org/all/20251020.fohbo6Iecahz@digikod.net/
If it helps, I keep some of my own notes for this particular feature
on https://wiki.gnoack.org/LandlockMultithreadedEnforcement.
(Very) tentative investigation:
In the Syzkaller report [2], it seems that the reproducer [2.1] is
creating two rulesets and then enforcing them in parallel, a scenario
which we are exercising in the TEST(competing_enablement) in
tools/testing/selftests/landlock/tsync_test.c already, but which has
not failed in my own selftest runs.
In the crash report, there are four threads in total:
* Two are stuck in the line
wait_for_completion(&ctx->ready_to_commit);
in the per-thread task work (line 128 [4.1])
* Two are stuck in the line
wait_for_completion(&shared_ctx.all_prepared)
in the calling thread's coordination logic (line 539 [4.2])
In line 539, we are already on the code path where we detected that we
are getting interrupted by another thread and where we are attempting
to deal with the scenario where two landlock_restrict_self() calls
compete. This is detected on line 523 when
wait_for_completion_interruptible() is true. The approach to handle
this is to set the overall -ERESTARTNOINTR error and cancel the work
that has been ongoing so far, by canceling the task works that did not
start running yet and waiting for the ones that did start running
(that is the step where we are blocked!). The reasoning there was
that these task works will all hit the "all_prepared" stage now, but
as we can see in the stack trace, the task works that are actively
running are already on line 128 and have passed the "all_prepared"
stage).
Differences I can see between syzkaller and our own test:
* The reproducer also calls openat() and then twice socketpair().
These syscalls should be unrelated, but it's possible that the
"async" invocation of socketpair() contributes to adding more
threads. (Assuming that "async" means "in new thread" in syzkaller)
* Syzkaller gives it more attempts. ([2.2])
I do not understand yet what went wrong in our scheme and need to look
deeper.
Ding, do you have more insights into it from your debugging?
Thanks,
–Günther
For reference:
[1] Report Mail: https://lore.kernel.org/all/69984159.050a0220.21cd75.01bb.GAE@google.com/
[2] Report: https://syzkaller.appspot.com/bug?extid=7ea2f5e9dfd468201817
[2.1] Reproducer: https://syzkaller.appspot.com/text?tag=ReproSyz&x=16e41c02580000
[2.2] Reproducer (C): https://syzkaller.appspot.com/text?tag=ReproC&x=15813652580000
[3] Patch: https://lore.kernel.org/all/6999504d.a70a0220.2c38d7.0154.GAE@google.com/
[4.1] https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/tree/security/landlock/tsync.c?id=635c467cc14ebdffab3f77610217c1dacaf88e8c#n128
[4.2] https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/tree/security/landlock/tsync.c?id=635c467cc14ebdffab3f77610217c1dacaf88e8c#n539
^ permalink raw reply
* Re: [syzbot] [kernel?] INFO: task hung in restrict_one_thread_callback
From: Ding Yihan @ 2026-02-21 7:28 UTC (permalink / raw)
To: syzbot, Mickaël Salaün
Cc: linux-security-module, Günther Noack
In-Reply-To: <69995a88.050a0220.340abe.0d25.GAE@google.com>
Hi all,
Thanks to syzbot for the testing and confirmation.
Since I am relatively new to the inner workings of this specific subsystem,
I would like to take a few days to thoroughly study the root cause
(the task_work and mutex interaction) and prepare a detailed and proper commit message.
I will send out the formal patch (v1) to the mailing list later.
Best regards,
Yihan Ding
在 2026/2/21 15:11, syzbot 写道:
> Hello,
>
> syzbot has tested the proposed patch and the reproducer did not trigger any issue:
>
> Reported-by: syzbot+7ea2f5e9dfd468201817@syzkaller.appspotmail.com
> Tested-by: syzbot+7ea2f5e9dfd468201817@syzkaller.appspotmail.com
>
> Tested on:
>
> commit: d4906ae1 Add linux-next specific files for 20260220
> git tree: linux-next
> console output: https://syzkaller.appspot.com/x/log.txt?x=13ea89e6580000
> kernel config: https://syzkaller.appspot.com/x/.config?x=51f859f3211496bc
> dashboard link: https://syzkaller.appspot.com/bug?extid=7ea2f5e9dfd468201817
> compiler: Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8
> patch: https://syzkaller.appspot.com/x/patch.diff?x=15f0595a580000
>
> Note: testing is done by a robot and is best-effort only.
>
^ permalink raw reply
* Re: [GIT PULL] AppArmor updates for 7.0-rc1
From: pr-tracker-bot @ 2026-02-21 1:20 UTC (permalink / raw)
To: John Johansen; +Cc: Linus Torvalds, LKLM, open list:SECURITY SUBSYSTEM
In-Reply-To: <53c39cf4-d9e4-4ccf-ae16-dc5b143c9681@canonical.com>
The pull request you sent on Wed, 18 Feb 2026 16:19:34 -0800:
> git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor tags/apparmor-pr-2026-02-18
has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/219d7660c57ec7457a6020f3d025cfe1139ffc64
Thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html
^ permalink raw reply
* Re: [PATCH 05/13] selinux: Use simple_start_creating() / simple_done_creating()
From: Paul Moore @ 2026-02-20 22:47 UTC (permalink / raw)
To: NeilBrown
Cc: Christian Brauner, Alexander Viro, David Howells, Jan Kara,
Chuck Lever, Jeff Layton, Miklos Szeredi, Amir Goldstein,
John Johansen, James Morris, Serge E. Hallyn, Stephen Smalley,
linux-kernel, netfs, linux-fsdevel, linux-nfs, linux-unionfs,
apparmor, linux-security-module, selinux
In-Reply-To: <20260204050726.177283-6-neilb@ownmail.net>
On Wed, Feb 4, 2026 at 12:08 AM NeilBrown <neilb@ownmail.net> wrote:
>
> From: NeilBrown <neil@brown.name>
>
> Instead of explicitly locking the parent and performing a lookup in
> selinux, use simple_start_creating(), and then use
> simple_done_creating() to unlock.
>
> This extends the region that the directory is locked for, and also
> performs a lookup.
> The lock extension is of no real consequence.
> The lookup uses simple_lookup() and so always succeeds. Thus when
> d_make_persistent() is called the dentry will already be hashed.
> d_make_persistent() handles this case.
>
> Signed-off-by: NeilBrown <neil@brown.name>
> ---
> security/selinux/selinuxfs.c | 15 +++++++--------
> 1 file changed, 7 insertions(+), 8 deletions(-)
Unless I'm missing something, there is no reason why I couldn't take
just this patch into the SELinux tree once the merge window closes,
yes?
--
paul-moore.com
^ permalink raw reply
* Re: [PATCH 2/2] fanotify: call fanotify_events_supported() before path_permission() and security_path_notify()
From: Paul Moore @ 2026-02-20 22:16 UTC (permalink / raw)
To: Ondrej Mosnacek
Cc: Jan Kara, Amir Goldstein, Matthew Bobrowski, linux-fsdevel,
linux-security-module, selinux, linux-kernel
In-Reply-To: <20260216150625.793013-3-omosnace@redhat.com>
On Mon, Feb 16, 2026 at 10:14 AM Ondrej Mosnacek <omosnace@redhat.com> wrote:
>
> The latter trigger LSM (e.g. SELinux) checks, which will log a denial
> when permission is denied, so it's better to do them after validity
> checks to avoid logging a denial when the operation would fail anyway.
>
> Fixes: 0b3b094ac9a7 ("fanotify: Disallow permission events for proc filesystem")
> Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
> ---
> fs/notify/fanotify/fanotify_user.c | 25 ++++++++++---------------
> 1 file changed, 10 insertions(+), 15 deletions(-)
Reviewed-by: Paul Moore <paul@paul-moore.com>
--
paul-moore.com
^ permalink raw reply
* Re: [PATCH 1/2] fanotify: avoid/silence premature LSM capability checks
From: Paul Moore @ 2026-02-20 22:15 UTC (permalink / raw)
To: Ondrej Mosnacek
Cc: Jan Kara, Amir Goldstein, Matthew Bobrowski, linux-fsdevel,
linux-security-module, selinux, linux-kernel
In-Reply-To: <20260216150625.793013-2-omosnace@redhat.com>
On Mon, Feb 16, 2026 at 10:13 AM Ondrej Mosnacek <omosnace@redhat.com> wrote:
>
> Make sure calling capable()/ns_capable() actually leads to access denied
> when false is returned, because these functions emit an audit record
> when a Linux Security Module denies the capability, which makes it
> difficult to avoid allowing/silencing unnecessary permissions in
> security policies (namely with SELinux).
>
> Where the return value just used to set a flag, use the non-auditing
> ns_capable_noaudit() instead.
>
> Fixes: 7cea2a3c505e ("fanotify: support limited functionality for unprivileged users")
> Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
> ---
> fs/notify/fanotify/fanotify_user.c | 25 +++++++++++++------------
> 1 file changed, 13 insertions(+), 12 deletions(-)
Reviewed-by: Paul Moore <paul@paul-moore.com>
--
paul-moore.com
^ permalink raw reply
* Re: [PATCH] lsm: move inode IS_PRIVATE checks to individual LSMs
From: Casey Schaufler @ 2026-02-20 21:13 UTC (permalink / raw)
To: danieldurning.work, linux-security-module, selinux,
linux-integrity
Cc: stephen.smalley.work, paul, jmorris, serge, john.johansen, zohar,
roberto.sassu, dmitry.kasatkin, mic, takedakn, penguin-kernel,
Casey Schaufler
In-Reply-To: <20260220195405.30612-1-danieldurning.work@gmail.com>
On 2/20/2026 11:54 AM, danieldurning.work@gmail.com wrote:
> From: Daniel Durning <danieldurning.work@gmail.com>
>
> Move responsibility of bypassing S_PRIVATE inodes to the
> individual LSMs. Originally the LSM framework would skip calling
> the hooks on any inode that was marked S_PRIVATE. This would
> prevent the LSMs from controlling access to any inodes marked as
> such (ie. pidfds). We now perform the same IS_PRIVATE checks
> within the LSMs instead. This is consistent with the general goal
> of deferring as much as possible to the individual LSMs.
Um ... ick?
Sure, we generally want the LSMs to be responsible for their own
decisions, but that doesn't look like the point to me. What appears
to be the issue is that pidfs isn't using S_PRIVATE in a way that
conveys the necessary information to the LSMs.
>
> This reorganization enables the LSMs to eventually implement
> checks or labeling for some specific S_PRIVATE inodes like pidfds.
We could consider these or similar changes when that eventuality occurs.
I would strongly suggest that this is a pidfs issue, not an LSM
infrastructure issue.
>
> Signed-off-by: Daniel Durning <danieldurning.work@gmail.com>
> ---
> security/apparmor/lsm.c | 32 ++++++++
> security/commoncap.c | 11 ++-
> security/integrity/evm/evm_main.c | 33 +++++++++
> security/integrity/ima/ima_appraise.c | 12 +++
> security/integrity/ima/ima_main.c | 6 ++
> security/landlock/fs.c | 23 ++++++
> security/security.c | 101 ++------------------------
> security/selinux/hooks.c | 77 ++++++++++++++++++++
> security/smack/smack_lsm.c | 56 ++++++++++++++
> security/tomoyo/tomoyo.c | 35 +++++++++
> 10 files changed, 290 insertions(+), 96 deletions(-)
>
> diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
> index a87cd60ed206..5b3ced11bdbc 100644
> --- a/security/apparmor/lsm.c
> +++ b/security/apparmor/lsm.c
> @@ -323,18 +323,27 @@ static int common_perm_create(const char *op, const struct path *dir,
>
> static int apparmor_path_unlink(const struct path *dir, struct dentry *dentry)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> + return 0;
> +
> return common_perm_rm(OP_UNLINK, dir, dentry, AA_MAY_DELETE);
> }
>
> static int apparmor_path_mkdir(const struct path *dir, struct dentry *dentry,
> umode_t mode)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> + return 0;
> +
> return common_perm_create(OP_MKDIR, dir, dentry, AA_MAY_CREATE,
> S_IFDIR);
> }
>
> static int apparmor_path_rmdir(const struct path *dir, struct dentry *dentry)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> + return 0;
> +
> return common_perm_rm(OP_RMDIR, dir, dentry, AA_MAY_DELETE);
> }
>
> @@ -346,6 +355,9 @@ static int apparmor_path_mknod(const struct path *dir, struct dentry *dentry,
>
> static int apparmor_path_truncate(const struct path *path)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> + return 0;
> +
> return common_perm_cond(OP_TRUNC, path, MAY_WRITE | AA_MAY_SETATTR);
> }
>
> @@ -357,6 +369,9 @@ static int apparmor_file_truncate(struct file *file)
> static int apparmor_path_symlink(const struct path *dir, struct dentry *dentry,
> const char *old_name)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> + return 0;
> +
> return common_perm_create(OP_SYMLINK, dir, dentry, AA_MAY_CREATE,
> S_IFLNK);
> }
> @@ -367,6 +382,9 @@ static int apparmor_path_link(struct dentry *old_dentry, const struct path *new_
> struct aa_label *label;
> int error = 0;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
> + return 0;
> +
> if (!path_mediated_fs(old_dentry))
> return 0;
>
> @@ -386,6 +404,11 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d
> struct aa_label *label;
> int error = 0;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) ||
> + (d_is_positive(new_dentry) &&
> + IS_PRIVATE(d_backing_inode(new_dentry)))))
> + return 0;
> +
> if (!path_mediated_fs(old_dentry))
> return 0;
> if ((flags & RENAME_EXCHANGE) && !path_mediated_fs(new_dentry))
> @@ -444,16 +467,25 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d
>
> static int apparmor_path_chmod(const struct path *path, umode_t mode)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> + return 0;
> +
> return common_perm_cond(OP_CHMOD, path, AA_MAY_CHMOD);
> }
>
> static int apparmor_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> + return 0;
> +
> return common_perm_cond(OP_CHOWN, path, AA_MAY_CHOWN);
> }
>
> static int apparmor_inode_getattr(const struct path *path)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> + return 0;
> +
> return common_perm_cond(OP_GETATTR, path, AA_MAY_GETATTR);
> }
>
> diff --git a/security/commoncap.c b/security/commoncap.c
> index 8a23dfab7fac..1b61d43529c2 100644
> --- a/security/commoncap.c
> +++ b/security/commoncap.c
> @@ -88,7 +88,7 @@ static inline int cap_capable_helper(const struct cred *cred,
> if (ns->level <= cred_ns->level)
> return -EPERM;
>
> - /*
> + /*
> * The owner of the user namespace in the parent of the
> * user namespace has all caps.
> */
> @@ -432,6 +432,9 @@ int cap_inode_getsecurity(struct mnt_idmap *idmap,
> struct dentry *dentry;
> struct user_namespace *fs_ns;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return -EOPNOTSUPP;
> +
> if (strcmp(name, "capability") != 0)
> return -EOPNOTSUPP;
>
> @@ -1027,6 +1030,9 @@ int cap_inode_setxattr(struct dentry *dentry, const char *name,
> {
> struct user_namespace *user_ns = dentry->d_sb->s_user_ns;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> /* Ignore non-security xattrs */
> if (strncmp(name, XATTR_SECURITY_PREFIX,
> XATTR_SECURITY_PREFIX_LEN) != 0)
> @@ -1068,6 +1074,9 @@ int cap_inode_removexattr(struct mnt_idmap *idmap,
> {
> struct user_namespace *user_ns = dentry->d_sb->s_user_ns;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> /* Ignore non-security xattrs */
> if (strncmp(name, XATTR_SECURITY_PREFIX,
> XATTR_SECURITY_PREFIX_LEN) != 0)
> diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
> index 73d500a375cb..0095712b8d75 100644
> --- a/security/integrity/evm/evm_main.c
> +++ b/security/integrity/evm/evm_main.c
> @@ -590,6 +590,9 @@ static int evm_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
> {
> const struct evm_ima_xattr_data *xattr_data = xattr_value;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> /* Policy permits modification of the protected xattrs even though
> * there's no HMAC key loaded
> */
> @@ -675,6 +678,9 @@ static int evm_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
> {
> enum integrity_status evm_status;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> /* Policy permits modification of the protected xattrs even though
> * there's no HMAC key loaded
> */
> @@ -725,6 +731,9 @@ static int evm_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
> static int evm_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
> const char *acl_name)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> return evm_inode_set_acl(idmap, dentry, acl_name, NULL);
> }
>
> @@ -807,6 +816,9 @@ static void evm_inode_post_setxattr(struct dentry *dentry,
> size_t xattr_value_len,
> int flags)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return;
> +
> if (!evm_revalidate_status(xattr_name))
> return;
>
> @@ -836,6 +848,9 @@ static void evm_inode_post_setxattr(struct dentry *dentry,
> static void evm_inode_post_set_acl(struct dentry *dentry, const char *acl_name,
> struct posix_acl *kacl)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return;
> +
> return evm_inode_post_setxattr(dentry, acl_name, NULL, 0, 0);
> }
>
> @@ -852,6 +867,9 @@ static void evm_inode_post_set_acl(struct dentry *dentry, const char *acl_name,
> static void evm_inode_post_removexattr(struct dentry *dentry,
> const char *xattr_name)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return;
> +
> if (!evm_revalidate_status(xattr_name))
> return;
>
> @@ -879,6 +897,9 @@ static inline void evm_inode_post_remove_acl(struct mnt_idmap *idmap,
> struct dentry *dentry,
> const char *acl_name)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return;
> +
> evm_inode_post_removexattr(dentry, acl_name);
> }
>
> @@ -911,6 +932,9 @@ static int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
> unsigned int ia_valid = attr->ia_valid;
> enum integrity_status evm_status;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> /* Policy permits modification of the protected attrs even though
> * there's no HMAC key loaded
> */
> @@ -960,6 +984,9 @@ static int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
> static void evm_inode_post_setattr(struct mnt_idmap *idmap,
> struct dentry *dentry, int ia_valid)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return;
> +
> if (!evm_revalidate_status(NULL))
> return;
>
> @@ -1019,6 +1046,9 @@ int evm_inode_init_security(struct inode *inode, struct inode *dir,
> bool evm_protected_xattrs = false;
> int rc;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return 0;
> +
> if (!(evm_initialized & EVM_INIT_HMAC) || !xattrs)
> return 0;
>
> @@ -1094,6 +1124,9 @@ static void evm_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
> struct inode *inode = d_backing_inode(dentry);
> struct evm_iint_cache *iint = evm_iint_inode(inode);
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return;
> +
> if (!S_ISREG(inode->i_mode))
> return;
>
> diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
> index 5149ff4fd50d..d705c908132f 100644
> --- a/security/integrity/ima/ima_appraise.c
> +++ b/security/integrity/ima/ima_appraise.c
> @@ -665,6 +665,9 @@ static void ima_inode_post_setattr(struct mnt_idmap *idmap,
> struct ima_iint_cache *iint;
> int action;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return;
> +
> if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)
> || !(inode->i_opflags & IOP_XATTR))
> return;
> @@ -790,6 +793,9 @@ static int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
> int result;
> int err;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> result = ima_protect_xattr(dentry, xattr_name, xattr_value,
> xattr_value_len);
> if (result == 1) {
> @@ -817,6 +823,9 @@ static int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
> static int ima_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
> const char *acl_name, struct posix_acl *kacl)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> if (evm_revalidate_status(acl_name))
> ima_reset_appraise_flags(d_backing_inode(dentry), -1);
>
> @@ -842,6 +851,9 @@ static int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
> static int ima_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
> const char *acl_name)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> return ima_inode_set_acl(idmap, dentry, acl_name, NULL);
> }
>
> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> index 5770cf691912..5ddac6c15c7f 100644
> --- a/security/integrity/ima/ima_main.c
> +++ b/security/integrity/ima/ima_main.c
> @@ -794,6 +794,9 @@ static void ima_post_create_tmpfile(struct mnt_idmap *idmap,
> struct ima_iint_cache *iint;
> int must_appraise;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return;
> +
> if (!ima_policy_flag || !S_ISREG(inode->i_mode))
> return;
>
> @@ -826,6 +829,9 @@ static void ima_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
> struct inode *inode = dentry->d_inode;
> int must_appraise;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return;
> +
> if (!ima_policy_flag || !S_ISREG(inode->i_mode))
> return;
>
> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> index fe794875ad46..5fe15313a3f5 100644
> --- a/security/landlock/fs.c
> +++ b/security/landlock/fs.c
> @@ -1528,6 +1528,9 @@ static int hook_path_link(struct dentry *const old_dentry,
> const struct path *const new_dir,
> struct dentry *const new_dentry)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
> + return 0;
> +
> return current_check_refer_path(old_dentry, new_dir, new_dentry, false,
> false);
> }
> @@ -1538,6 +1541,11 @@ static int hook_path_rename(const struct path *const old_dir,
> struct dentry *const new_dentry,
> const unsigned int flags)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) ||
> + (d_is_positive(new_dentry) &&
> + IS_PRIVATE(d_backing_inode(new_dentry)))))
> + return 0;
> +
> /* old_dir refers to old_dentry->d_parent and new_dir->mnt */
> return current_check_refer_path(old_dentry, new_dir, new_dentry, true,
> !!(flags & RENAME_EXCHANGE));
> @@ -1546,6 +1554,9 @@ static int hook_path_rename(const struct path *const old_dir,
> static int hook_path_mkdir(const struct path *const dir,
> struct dentry *const dentry, const umode_t mode)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> + return 0;
> +
> return current_check_access_path(dir, LANDLOCK_ACCESS_FS_MAKE_DIR);
> }
>
> @@ -1560,23 +1571,35 @@ static int hook_path_symlink(const struct path *const dir,
> struct dentry *const dentry,
> const char *const old_name)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> + return 0;
> +
> return current_check_access_path(dir, LANDLOCK_ACCESS_FS_MAKE_SYM);
> }
>
> static int hook_path_unlink(const struct path *const dir,
> struct dentry *const dentry)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> + return 0;
> +
> return current_check_access_path(dir, LANDLOCK_ACCESS_FS_REMOVE_FILE);
> }
>
> static int hook_path_rmdir(const struct path *const dir,
> struct dentry *const dentry)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> + return 0;
> +
> return current_check_access_path(dir, LANDLOCK_ACCESS_FS_REMOVE_DIR);
> }
>
> static int hook_path_truncate(const struct path *const path)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> + return 0;
> +
> return current_check_access_path(path, LANDLOCK_ACCESS_FS_TRUNCATE);
> }
>
> diff --git a/security/security.c b/security/security.c
> index 31a688650601..658dbb10ea40 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -1310,9 +1310,6 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
> struct xattr *new_xattrs = NULL;
> int ret = -EOPNOTSUPP, xattr_count = 0;
>
> - if (unlikely(IS_PRIVATE(inode)))
> - return 0;
> -
> if (!blob_sizes.lbs_xattr_count)
> return 0;
>
> @@ -1386,8 +1383,6 @@ int security_inode_init_security_anon(struct inode *inode,
> int security_path_mknod(const struct path *dir, struct dentry *dentry,
> umode_t mode, unsigned int dev)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> - return 0;
> return call_int_hook(path_mknod, dir, dentry, mode, dev);
> }
> EXPORT_SYMBOL(security_path_mknod);
> @@ -1401,8 +1396,6 @@ EXPORT_SYMBOL(security_path_mknod);
> */
> void security_path_post_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return;
> call_void_hook(path_post_mknod, idmap, dentry);
> }
>
> @@ -1419,8 +1412,6 @@ void security_path_post_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
> int security_path_mkdir(const struct path *dir, struct dentry *dentry,
> umode_t mode)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> - return 0;
> return call_int_hook(path_mkdir, dir, dentry, mode);
> }
> EXPORT_SYMBOL(security_path_mkdir);
> @@ -1436,8 +1427,6 @@ EXPORT_SYMBOL(security_path_mkdir);
> */
> int security_path_rmdir(const struct path *dir, struct dentry *dentry)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> - return 0;
> return call_int_hook(path_rmdir, dir, dentry);
> }
>
> @@ -1452,8 +1441,6 @@ int security_path_rmdir(const struct path *dir, struct dentry *dentry)
> */
> int security_path_unlink(const struct path *dir, struct dentry *dentry)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> - return 0;
> return call_int_hook(path_unlink, dir, dentry);
> }
> EXPORT_SYMBOL(security_path_unlink);
> @@ -1471,8 +1458,6 @@ EXPORT_SYMBOL(security_path_unlink);
> int security_path_symlink(const struct path *dir, struct dentry *dentry,
> const char *old_name)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> - return 0;
> return call_int_hook(path_symlink, dir, dentry, old_name);
> }
>
> @@ -1489,8 +1474,6 @@ int security_path_symlink(const struct path *dir, struct dentry *dentry,
> int security_path_link(struct dentry *old_dentry, const struct path *new_dir,
> struct dentry *new_dentry)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
> - return 0;
> return call_int_hook(path_link, old_dentry, new_dir, new_dentry);
> }
>
> @@ -1510,11 +1493,6 @@ int security_path_rename(const struct path *old_dir, struct dentry *old_dentry,
> const struct path *new_dir, struct dentry *new_dentry,
> unsigned int flags)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) ||
> - (d_is_positive(new_dentry) &&
> - IS_PRIVATE(d_backing_inode(new_dentry)))))
> - return 0;
> -
> return call_int_hook(path_rename, old_dir, old_dentry, new_dir,
> new_dentry, flags);
> }
> @@ -1532,8 +1510,6 @@ EXPORT_SYMBOL(security_path_rename);
> */
> int security_path_truncate(const struct path *path)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> - return 0;
> return call_int_hook(path_truncate, path);
> }
>
> @@ -1550,8 +1526,6 @@ int security_path_truncate(const struct path *path)
> */
> int security_path_chmod(const struct path *path, umode_t mode)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> - return 0;
> return call_int_hook(path_chmod, path, mode);
> }
>
> @@ -1567,8 +1541,6 @@ int security_path_chmod(const struct path *path, umode_t mode)
> */
> int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> - return 0;
> return call_int_hook(path_chown, path, uid, gid);
> }
>
> @@ -1599,8 +1571,6 @@ int security_path_chroot(const struct path *path)
> int security_inode_create(struct inode *dir, struct dentry *dentry,
> umode_t mode)
> {
> - if (unlikely(IS_PRIVATE(dir)))
> - return 0;
> return call_int_hook(inode_create, dir, dentry, mode);
> }
> EXPORT_SYMBOL_GPL(security_inode_create);
> @@ -1615,8 +1585,6 @@ EXPORT_SYMBOL_GPL(security_inode_create);
> void security_inode_post_create_tmpfile(struct mnt_idmap *idmap,
> struct inode *inode)
> {
> - if (unlikely(IS_PRIVATE(inode)))
> - return;
> call_void_hook(inode_post_create_tmpfile, idmap, inode);
> }
>
> @@ -1633,8 +1601,6 @@ void security_inode_post_create_tmpfile(struct mnt_idmap *idmap,
> int security_inode_link(struct dentry *old_dentry, struct inode *dir,
> struct dentry *new_dentry)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
> - return 0;
> return call_int_hook(inode_link, old_dentry, dir, new_dentry);
> }
>
> @@ -1649,8 +1615,6 @@ int security_inode_link(struct dentry *old_dentry, struct inode *dir,
> */
> int security_inode_unlink(struct inode *dir, struct dentry *dentry)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return 0;
> return call_int_hook(inode_unlink, dir, dentry);
> }
>
> @@ -1667,8 +1631,6 @@ int security_inode_unlink(struct inode *dir, struct dentry *dentry)
> int security_inode_symlink(struct inode *dir, struct dentry *dentry,
> const char *old_name)
> {
> - if (unlikely(IS_PRIVATE(dir)))
> - return 0;
> return call_int_hook(inode_symlink, dir, dentry, old_name);
> }
>
> @@ -1685,8 +1647,6 @@ int security_inode_symlink(struct inode *dir, struct dentry *dentry,
> */
> int security_inode_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
> {
> - if (unlikely(IS_PRIVATE(dir)))
> - return 0;
> return call_int_hook(inode_mkdir, dir, dentry, mode);
> }
> EXPORT_SYMBOL_GPL(security_inode_mkdir);
> @@ -1702,8 +1662,6 @@ EXPORT_SYMBOL_GPL(security_inode_mkdir);
> */
> int security_inode_rmdir(struct inode *dir, struct dentry *dentry)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return 0;
> return call_int_hook(inode_rmdir, dir, dentry);
> }
>
> @@ -1724,8 +1682,6 @@ int security_inode_rmdir(struct inode *dir, struct dentry *dentry)
> int security_inode_mknod(struct inode *dir, struct dentry *dentry,
> umode_t mode, dev_t dev)
> {
> - if (unlikely(IS_PRIVATE(dir)))
> - return 0;
> return call_int_hook(inode_mknod, dir, dentry, mode, dev);
> }
>
> @@ -1745,11 +1701,6 @@ int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
> struct inode *new_dir, struct dentry *new_dentry,
> unsigned int flags)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) ||
> - (d_is_positive(new_dentry) &&
> - IS_PRIVATE(d_backing_inode(new_dentry)))))
> - return 0;
> -
> if (flags & RENAME_EXCHANGE) {
> int err = call_int_hook(inode_rename, new_dir, new_dentry,
> old_dir, old_dentry);
> @@ -1771,8 +1722,6 @@ int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
> */
> int security_inode_readlink(struct dentry *dentry)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return 0;
> return call_int_hook(inode_readlink, dentry);
> }
>
> @@ -1790,8 +1739,6 @@ int security_inode_readlink(struct dentry *dentry)
> int security_inode_follow_link(struct dentry *dentry, struct inode *inode,
> bool rcu)
> {
> - if (unlikely(IS_PRIVATE(inode)))
> - return 0;
> return call_int_hook(inode_follow_link, dentry, inode, rcu);
> }
>
> @@ -1811,8 +1758,6 @@ int security_inode_follow_link(struct dentry *dentry, struct inode *inode,
> */
> int security_inode_permission(struct inode *inode, int mask)
> {
> - if (unlikely(IS_PRIVATE(inode)))
> - return 0;
> return call_int_hook(inode_permission, inode, mask);
> }
>
> @@ -1832,8 +1777,6 @@ int security_inode_permission(struct inode *inode, int mask)
> int security_inode_setattr(struct mnt_idmap *idmap,
> struct dentry *dentry, struct iattr *attr)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return 0;
> return call_int_hook(inode_setattr, idmap, dentry, attr);
> }
> EXPORT_SYMBOL_GPL(security_inode_setattr);
> @@ -1849,8 +1792,6 @@ EXPORT_SYMBOL_GPL(security_inode_setattr);
> void security_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
> int ia_valid)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return;
> call_void_hook(inode_post_setattr, idmap, dentry, ia_valid);
> }
>
> @@ -1864,8 +1805,6 @@ void security_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
> */
> int security_inode_getattr(const struct path *path)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> - return 0;
> return call_int_hook(inode_getattr, path);
> }
>
> @@ -1901,11 +1840,11 @@ int security_inode_setxattr(struct mnt_idmap *idmap,
> {
> int rc;
>
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return 0;
> -
> /* enforce the capability checks at the lsm layer, if needed */
> if (!call_int_hook(inode_xattr_skipcap, name)) {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> rc = cap_inode_setxattr(dentry, name, value, size, flags);
> if (rc)
> return rc;
> @@ -1931,8 +1870,6 @@ int security_inode_set_acl(struct mnt_idmap *idmap,
> struct dentry *dentry, const char *acl_name,
> struct posix_acl *kacl)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return 0;
> return call_int_hook(inode_set_acl, idmap, dentry, acl_name, kacl);
> }
>
> @@ -1948,8 +1885,6 @@ int security_inode_set_acl(struct mnt_idmap *idmap,
> void security_inode_post_set_acl(struct dentry *dentry, const char *acl_name,
> struct posix_acl *kacl)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return;
> call_void_hook(inode_post_set_acl, dentry, acl_name, kacl);
> }
>
> @@ -1967,8 +1902,6 @@ void security_inode_post_set_acl(struct dentry *dentry, const char *acl_name,
> int security_inode_get_acl(struct mnt_idmap *idmap,
> struct dentry *dentry, const char *acl_name)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return 0;
> return call_int_hook(inode_get_acl, idmap, dentry, acl_name);
> }
>
> @@ -1986,8 +1919,6 @@ int security_inode_get_acl(struct mnt_idmap *idmap,
> int security_inode_remove_acl(struct mnt_idmap *idmap,
> struct dentry *dentry, const char *acl_name)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return 0;
> return call_int_hook(inode_remove_acl, idmap, dentry, acl_name);
> }
>
> @@ -2003,8 +1934,6 @@ int security_inode_remove_acl(struct mnt_idmap *idmap,
> void security_inode_post_remove_acl(struct mnt_idmap *idmap,
> struct dentry *dentry, const char *acl_name)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return;
> call_void_hook(inode_post_remove_acl, idmap, dentry, acl_name);
> }
>
> @@ -2021,8 +1950,6 @@ void security_inode_post_remove_acl(struct mnt_idmap *idmap,
> void security_inode_post_setxattr(struct dentry *dentry, const char *name,
> const void *value, size_t size, int flags)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return;
> call_void_hook(inode_post_setxattr, dentry, name, value, size, flags);
> }
>
> @@ -2038,8 +1965,6 @@ void security_inode_post_setxattr(struct dentry *dentry, const char *name,
> */
> int security_inode_getxattr(struct dentry *dentry, const char *name)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return 0;
> return call_int_hook(inode_getxattr, dentry, name);
> }
>
> @@ -2054,8 +1979,6 @@ int security_inode_getxattr(struct dentry *dentry, const char *name)
> */
> int security_inode_listxattr(struct dentry *dentry)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return 0;
> return call_int_hook(inode_listxattr, dentry);
> }
>
> @@ -2087,11 +2010,11 @@ int security_inode_removexattr(struct mnt_idmap *idmap,
> {
> int rc;
>
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return 0;
> -
> /* enforce the capability checks at the lsm layer, if needed */
> if (!call_int_hook(inode_xattr_skipcap, name)) {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> rc = cap_inode_removexattr(idmap, dentry, name);
> if (rc)
> return rc;
> @@ -2109,8 +2032,6 @@ int security_inode_removexattr(struct mnt_idmap *idmap,
> */
> void security_inode_post_removexattr(struct dentry *dentry, const char *name)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return;
> call_void_hook(inode_post_removexattr, dentry, name);
> }
>
> @@ -2197,9 +2118,6 @@ int security_inode_getsecurity(struct mnt_idmap *idmap,
> struct inode *inode, const char *name,
> void **buffer, bool alloc)
> {
> - if (unlikely(IS_PRIVATE(inode)))
> - return LSM_RET_DEFAULT(inode_getsecurity);
> -
> return call_int_hook(inode_getsecurity, idmap, inode, name, buffer,
> alloc);
> }
> @@ -2222,9 +2140,6 @@ int security_inode_getsecurity(struct mnt_idmap *idmap,
> int security_inode_setsecurity(struct inode *inode, const char *name,
> const void *value, size_t size, int flags)
> {
> - if (unlikely(IS_PRIVATE(inode)))
> - return LSM_RET_DEFAULT(inode_setsecurity);
> -
> return call_int_hook(inode_setsecurity, inode, name, value, size,
> flags);
> }
> @@ -2245,8 +2160,6 @@ int security_inode_setsecurity(struct inode *inode, const char *name,
> int security_inode_listsecurity(struct inode *inode,
> char *buffer, size_t buffer_size)
> {
> - if (unlikely(IS_PRIVATE(inode)))
> - return 0;
> return call_int_hook(inode_listsecurity, inode, buffer, buffer_size);
> }
> EXPORT_SYMBOL(security_inode_listsecurity);
> @@ -3596,8 +3509,6 @@ int security_sem_semop(struct kern_ipc_perm *sma, struct sembuf *sops,
> */
> void security_d_instantiate(struct dentry *dentry, struct inode *inode)
> {
> - if (unlikely(inode && IS_PRIVATE(inode)))
> - return;
> call_void_hook(d_instantiate, dentry, inode);
> }
> EXPORT_SYMBOL(security_d_instantiate);
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index feda34b18d83..e17d776fb159 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -2948,6 +2948,9 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
> int rc;
> char *context;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return 0;
> +
> sbsec = selinux_superblock(dir->i_sb);
>
> newsid = crsec->create_sid;
> @@ -3049,42 +3052,68 @@ static int selinux_inode_init_security_anon(struct inode *inode,
>
> static int selinux_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode)
> {
> + if (unlikely(IS_PRIVATE(dir)))
> + return 0;
> +
> return may_create(dir, dentry, SECCLASS_FILE);
> }
>
> static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
> + return 0;
> +
> return may_link(dir, old_dentry, MAY_LINK);
> }
>
> static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> return may_link(dir, dentry, MAY_UNLINK);
> }
>
> static int selinux_inode_symlink(struct inode *dir, struct dentry *dentry, const char *name)
> {
> + if (unlikely(IS_PRIVATE(dir)))
> + return 0;
> +
> return may_create(dir, dentry, SECCLASS_LNK_FILE);
> }
>
> static int selinux_inode_mkdir(struct inode *dir, struct dentry *dentry, umode_t mask)
> {
> + if (unlikely(IS_PRIVATE(dir)))
> + return 0;
> +
> return may_create(dir, dentry, SECCLASS_DIR);
> }
>
> static int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> return may_link(dir, dentry, MAY_RMDIR);
> }
>
> static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
> {
> + if (unlikely(IS_PRIVATE(dir)))
> + return 0;
> +
> return may_create(dir, dentry, inode_mode_to_security_class(mode));
> }
>
> static int selinux_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
> struct inode *new_inode, struct dentry *new_dentry)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) ||
> + (d_is_positive(new_dentry) &&
> + IS_PRIVATE(d_backing_inode(new_dentry)))))
> + return 0;
> +
> return may_rename(old_inode, old_dentry, new_inode, new_dentry);
> }
>
> @@ -3092,6 +3121,9 @@ static int selinux_inode_readlink(struct dentry *dentry)
> {
> const struct cred *cred = current_cred();
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> return dentry_has_perm(cred, dentry, FILE__READ);
> }
>
> @@ -3102,6 +3134,9 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode,
> struct inode_security_struct *isec;
> u32 sid = current_sid();
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return 0;
> +
> ad.type = LSM_AUDIT_DATA_DENTRY;
> ad.u.dentry = dentry;
> isec = inode_security_rcu(inode, rcu);
> @@ -3230,6 +3265,9 @@ static int selinux_inode_permission(struct inode *inode, int requested)
> int rc, rc2;
> u32 audited, denied;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return 0;
> +
> mask = requested & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND);
>
> /* No permission to check. Existence test. */
> @@ -3283,6 +3321,9 @@ static int selinux_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
> unsigned int ia_valid = iattr->ia_valid;
> u32 av = FILE__WRITE;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return 0;
> +
> /* ATTR_FORCE is just used for ATTR_KILL_S[UG]ID. */
> if (ia_valid & ATTR_FORCE) {
> ia_valid &= ~(ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_MODE |
> @@ -3308,6 +3349,9 @@ static int selinux_inode_getattr(const struct path *path)
> {
> struct task_security_struct *tsec;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> + return 0;
> +
> tsec = selinux_task(current);
>
> if (task_avdcache_permnoaudit(tsec, current_sid()))
> @@ -3356,6 +3400,9 @@ static int selinux_inode_setxattr(struct mnt_idmap *idmap,
> u32 newsid, sid = current_sid();
> int rc = 0;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> /* if not a selinux xattr, only check the ordinary setattr perm */
> if (strcmp(name, XATTR_NAME_SELINUX))
> return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
> @@ -3435,18 +3482,27 @@ static int selinux_inode_set_acl(struct mnt_idmap *idmap,
> struct dentry *dentry, const char *acl_name,
> struct posix_acl *kacl)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
> }
>
> static int selinux_inode_get_acl(struct mnt_idmap *idmap,
> struct dentry *dentry, const char *acl_name)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> return dentry_has_perm(current_cred(), dentry, FILE__GETATTR);
> }
>
> static int selinux_inode_remove_acl(struct mnt_idmap *idmap,
> struct dentry *dentry, const char *acl_name)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
> }
>
> @@ -3459,6 +3515,9 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
> u32 newsid;
> int rc;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return;
> +
> if (strcmp(name, XATTR_NAME_SELINUX)) {
> /* Not an attribute we recognize, so nothing to do. */
> return;
> @@ -3494,6 +3553,9 @@ static int selinux_inode_getxattr(struct dentry *dentry, const char *name)
> {
> const struct cred *cred = current_cred();
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> return dentry_has_perm(cred, dentry, FILE__GETATTR);
> }
>
> @@ -3501,6 +3563,9 @@ static int selinux_inode_listxattr(struct dentry *dentry)
> {
> const struct cred *cred = current_cred();
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> return dentry_has_perm(cred, dentry, FILE__GETATTR);
> }
>
> @@ -3593,6 +3658,9 @@ static int selinux_inode_getsecurity(struct mnt_idmap *idmap,
> char *context = NULL;
> struct inode_security_struct *isec;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return -EOPNOTSUPP;
> +
> /*
> * If we're not initialized yet, then we can't validate contexts, so
> * just let vfs_getxattr fall back to using the on-disk xattr.
> @@ -3637,6 +3705,9 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name,
> u32 newsid;
> int rc;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return -EOPNOTSUPP;
> +
> if (strcmp(name, XATTR_SELINUX_SUFFIX))
> return -EOPNOTSUPP;
>
> @@ -3664,6 +3735,9 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t
> {
> const int len = sizeof(XATTR_NAME_SELINUX);
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return 0;
> +
> if (!selinux_initialized())
> return 0;
>
> @@ -6546,6 +6620,9 @@ static void selinux_ipc_getlsmprop(struct kern_ipc_perm *ipcp,
>
> static void selinux_d_instantiate(struct dentry *dentry, struct inode *inode)
> {
> + if (unlikely(inode && IS_PRIVATE(inode)))
> + return;
> +
> if (inode)
> inode_doinit_with_dentry(inode, dentry);
> }
> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> index a0bd4919a9d9..8f432348dfbd 100644
> --- a/security/smack/smack_lsm.c
> +++ b/security/smack/smack_lsm.c
> @@ -1020,6 +1020,9 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
> bool trans_cred;
> bool trans_rule;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return 0;
> +
> /*
> * UNIX domain sockets use lower level socket data. Let
> * UDS inode have fixed * label to keep smack_inode_permission() calm
> @@ -1093,6 +1096,9 @@ static int smack_inode_link(struct dentry *old_dentry, struct inode *dir,
> struct smk_audit_info ad;
> int rc;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
> + return 0;
> +
> smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
> smk_ad_setfield_u_fs_path_dentry(&ad, old_dentry);
>
> @@ -1124,6 +1130,9 @@ static int smack_inode_unlink(struct inode *dir, struct dentry *dentry)
> struct smk_audit_info ad;
> int rc;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
> smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
>
> @@ -1157,6 +1166,9 @@ static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry)
> struct smk_audit_info ad;
> int rc;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
> smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
>
> @@ -1199,6 +1211,11 @@ static int smack_inode_rename(struct inode *old_inode,
> struct smack_known *isp;
> struct smk_audit_info ad;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) ||
> + (d_is_positive(new_dentry) &&
> + IS_PRIVATE(d_backing_inode(new_dentry)))))
> + return 0;
> +
> smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
> smk_ad_setfield_u_fs_path_dentry(&ad, old_dentry);
>
> @@ -1231,6 +1248,9 @@ static int smack_inode_permission(struct inode *inode, int mask)
> int no_block = mask & MAY_NOT_BLOCK;
> int rc;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return 0;
> +
> mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND);
> /*
> * No permission to check. Existence test. Yup, it's there.
> @@ -1267,6 +1287,9 @@ static int smack_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
> struct smk_audit_info ad;
> int rc;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> /*
> * Need to allow for clearing the setuid bit.
> */
> @@ -1292,6 +1315,9 @@ static int smack_inode_getattr(const struct path *path)
> struct inode *inode = d_backing_inode(path->dentry);
> int rc;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> + return 0;
> +
> smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
> smk_ad_setfield_u_fs_path(&ad, *path);
> rc = smk_curacc(smk_of_inode(inode), MAY_READ, &ad);
> @@ -1351,6 +1377,9 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
> int rc = 0;
> umode_t const i_mode = d_backing_inode(dentry)->i_mode;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> /*
> * Check label validity here so import won't fail in post_setxattr
> */
> @@ -1421,6 +1450,9 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
> struct smack_known *skp;
> struct inode_smack *isp = smack_inode(d_backing_inode(dentry));
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return;
> +
> if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
> isp->smk_flags |= SMK_INODE_TRANSMUTE;
> return;
> @@ -1455,6 +1487,9 @@ static int smack_inode_getxattr(struct dentry *dentry, const char *name)
> struct smk_audit_info ad;
> int rc;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
> smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
>
> @@ -1541,6 +1576,9 @@ static int smack_inode_set_acl(struct mnt_idmap *idmap,
> struct smk_audit_info ad;
> int rc;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
> smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
>
> @@ -1563,6 +1601,9 @@ static int smack_inode_get_acl(struct mnt_idmap *idmap,
> struct smk_audit_info ad;
> int rc;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
> smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
>
> @@ -1585,6 +1626,9 @@ static int smack_inode_remove_acl(struct mnt_idmap *idmap,
> struct smk_audit_info ad;
> int rc;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
> smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
>
> @@ -1616,6 +1660,9 @@ static int smack_inode_getsecurity(struct mnt_idmap *idmap,
> size_t label_len;
> char *label = NULL;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return -EOPNOTSUPP;
> +
> if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) {
> isp = smk_of_inode(inode);
> } else if (strcmp(name, XATTR_SMACK_TRANSMUTE) == 0) {
> @@ -1672,6 +1719,9 @@ static int smack_inode_listsecurity(struct inode *inode, char *buffer,
> {
> int len = sizeof(XATTR_NAME_SMACK);
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return 0;
> +
> if (buffer != NULL && len <= buffer_size)
> memcpy(buffer, XATTR_NAME_SMACK, len);
>
> @@ -2918,6 +2968,9 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
> struct socket *sock;
> int rc = 0;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return -EOPNOTSUPP;
> +
> if (value == NULL || size > SMK_LONGLABEL || size == 0)
> return -EINVAL;
>
> @@ -3517,6 +3570,9 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
> if (inode == NULL)
> return;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return;
> +
> isp = smack_inode(inode);
>
> /*
> diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
> index c66e02ed8ee3..98eb8cd67f78 100644
> --- a/security/tomoyo/tomoyo.c
> +++ b/security/tomoyo/tomoyo.c
> @@ -120,6 +120,9 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
> */
> static int tomoyo_inode_getattr(const struct path *path)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> + return 0;
> +
> return tomoyo_path_perm(TOMOYO_TYPE_GETATTR, path, NULL);
> }
>
> @@ -132,6 +135,9 @@ static int tomoyo_inode_getattr(const struct path *path)
> */
> static int tomoyo_path_truncate(const struct path *path)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> + return 0;
> +
> return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path, NULL);
> }
>
> @@ -159,6 +165,9 @@ static int tomoyo_path_unlink(const struct path *parent, struct dentry *dentry)
> {
> struct path path = { .mnt = parent->mnt, .dentry = dentry };
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(parent->dentry))))
> + return 0;
> +
> return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path, NULL);
> }
>
> @@ -176,6 +185,9 @@ static int tomoyo_path_mkdir(const struct path *parent, struct dentry *dentry,
> {
> struct path path = { .mnt = parent->mnt, .dentry = dentry };
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(parent->dentry))))
> + return 0;
> +
> return tomoyo_path_number_perm(TOMOYO_TYPE_MKDIR, &path,
> mode & S_IALLUGO);
> }
> @@ -192,6 +204,9 @@ static int tomoyo_path_rmdir(const struct path *parent, struct dentry *dentry)
> {
> struct path path = { .mnt = parent->mnt, .dentry = dentry };
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(parent->dentry))))
> + return 0;
> +
> return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path, NULL);
> }
>
> @@ -209,6 +224,9 @@ static int tomoyo_path_symlink(const struct path *parent, struct dentry *dentry,
> {
> struct path path = { .mnt = parent->mnt, .dentry = dentry };
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(parent->dentry))))
> + return 0;
> +
> return tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, &path, old_name);
> }
>
> @@ -229,6 +247,9 @@ static int tomoyo_path_mknod(const struct path *parent, struct dentry *dentry,
> int type = TOMOYO_TYPE_CREATE;
> const unsigned int perm = mode & S_IALLUGO;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(parent->dentry))))
> + return 0;
> +
> switch (mode & S_IFMT) {
> case S_IFCHR:
> type = TOMOYO_TYPE_MKCHAR;
> @@ -267,6 +288,9 @@ static int tomoyo_path_link(struct dentry *old_dentry, const struct path *new_di
> struct path path1 = { .mnt = new_dir->mnt, .dentry = old_dentry };
> struct path path2 = { .mnt = new_dir->mnt, .dentry = new_dentry };
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
> + return 0;
> +
> return tomoyo_path2_perm(TOMOYO_TYPE_LINK, &path1, &path2);
> }
>
> @@ -290,6 +314,11 @@ static int tomoyo_path_rename(const struct path *old_parent,
> struct path path1 = { .mnt = old_parent->mnt, .dentry = old_dentry };
> struct path path2 = { .mnt = new_parent->mnt, .dentry = new_dentry };
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) ||
> + (d_is_positive(new_dentry) &&
> + IS_PRIVATE(d_backing_inode(new_dentry)))))
> + return 0;
> +
> if (flags & RENAME_EXCHANGE) {
> const int err = tomoyo_path2_perm(TOMOYO_TYPE_RENAME, &path2,
> &path1);
> @@ -360,6 +389,9 @@ static int tomoyo_file_ioctl(struct file *file, unsigned int cmd,
> */
> static int tomoyo_path_chmod(const struct path *path, umode_t mode)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> + return 0;
> +
> return tomoyo_path_number_perm(TOMOYO_TYPE_CHMOD, path,
> mode & S_IALLUGO);
> }
> @@ -377,6 +409,9 @@ static int tomoyo_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
> {
> int error = 0;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> + return 0;
> +
> if (uid_valid(uid))
> error = tomoyo_path_number_perm(TOMOYO_TYPE_CHOWN, path,
> from_kuid(&init_user_ns, uid));
^ permalink raw reply
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