* [PATCH v2 2/2] landlock: Improve TSYNC types
From: Mickaël Salaün @ 2026-02-17 12:23 UTC (permalink / raw)
To: Günther Noack
Cc: Mickaël Salaün, linux-security-module, Jann Horn
In-Reply-To: <20260217122341.2359582-1-mic@digikod.net>
Constify pointers when it makes sense.
Consistently use size_t for loops, especially to match works->size type.
Add new lines to improve readability.
Cc: Jann Horn <jannh@google.com>
Reviewed-by: Günther Noack <gnoack@google.com>
Signed-off-by: Mickaël Salaün <mic@digikod.net>
---
Changes since v1:
- Added Reviewed-by Günther.
---
security/landlock/tsync.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/security/landlock/tsync.c b/security/landlock/tsync.c
index 42cc0ef0c704..c588cdd111d3 100644
--- a/security/landlock/tsync.c
+++ b/security/landlock/tsync.c
@@ -290,13 +290,14 @@ static int tsync_works_grow_by(struct tsync_works *s, size_t n, gfp_t flags)
* tsync_works_contains - checks for presence of task in s
*/
static bool tsync_works_contains_task(const struct tsync_works *s,
- struct task_struct *task)
+ const struct task_struct *task)
{
size_t i;
for (i = 0; i < s->size; i++)
if (s->works[i]->task == task)
return true;
+
return false;
}
@@ -318,6 +319,7 @@ static void tsync_works_release(struct tsync_works *s)
for (i = 0; i < s->capacity; i++)
kfree(s->works[i]);
+
kfree(s->works);
s->works = NULL;
s->size = 0;
@@ -329,7 +331,7 @@ static void tsync_works_release(struct tsync_works *s)
*/
static size_t count_additional_threads(const struct tsync_works *works)
{
- struct task_struct *thread, *caller;
+ const struct task_struct *caller, *thread;
size_t n = 0;
caller = current;
@@ -368,7 +370,8 @@ static bool schedule_task_work(struct tsync_works *works,
struct tsync_shared_context *shared_ctx)
{
int err;
- struct task_struct *thread, *caller;
+ const struct task_struct *caller;
+ struct task_struct *thread;
struct tsync_work *ctx;
bool found_more_threads = false;
@@ -438,10 +441,10 @@ static bool schedule_task_work(struct tsync_works *works,
* shared_ctx->num_preparing and shared_ctx->num_unfished and mark the two
* completions if needed, as if the task was never scheduled.
*/
-static void cancel_tsync_works(struct tsync_works *works,
+static void cancel_tsync_works(const struct tsync_works *works,
struct tsync_shared_context *shared_ctx)
{
- int i;
+ size_t i;
for (i = 0; i < works->size; i++) {
if (WARN_ON_ONCE(!works->works[i]->task))
--
2.53.0
^ permalink raw reply related
* [PATCH v2 1/2] landlock: Fully release unused TSYNC work entries
From: Mickaël Salaün @ 2026-02-17 12:23 UTC (permalink / raw)
To: Günther Noack
Cc: Mickaël Salaün, linux-security-module, Jann Horn
If task_work_add() failed, ctx->task is put but the tsync_works struct
is not reset to its previous state. The first consequence is that the
kernel allocates memory for dying threads, which could lead to
user-accounted memory exhaustion (not very useful nor specific to this
case). The second consequence is that task_work_cancel(), called by
cancel_tsync_works(), can dereference a NULL task pointer.
Fix this issues by keeping a consistent works->size wrt the added task
work. This is done in a new tsync_works_trim() helper which also cleans
up the shared_ctx and work fields.
As a safeguard, add a pointer check to cancel_tsync_works() and update
tsync_works_release() accordingly.
Cc: Günther Noack <gnoack@google.com>
Cc: Jann Horn <jannh@google.com>
Signed-off-by: Mickaël Salaün <mic@digikod.net>
---
Changes since v1:
https://lore.kernel.org/all/20260216142641.2100407-1-mic@digikod.net/
- Move the return/release logic into a new tsync_works_trim() helper
(suggested by Günther).
- Reset the whole ctx with memset().
- Add an unlinkely(err).
---
security/landlock/tsync.c | 47 ++++++++++++++++++++++++++++++++++-----
1 file changed, 41 insertions(+), 6 deletions(-)
diff --git a/security/landlock/tsync.c b/security/landlock/tsync.c
index 0d2b9c646030..42cc0ef0c704 100644
--- a/security/landlock/tsync.c
+++ b/security/landlock/tsync.c
@@ -203,6 +203,40 @@ static struct tsync_work *tsync_works_provide(struct tsync_works *s,
return ctx;
}
+/**
+ * tsync_works_trim - Put the last tsync_work element
+ *
+ * @s: TSYNC works to trim.
+ *
+ * Put the last task and decrement the size of @s.
+ *
+ * This helper does not cancel a running task, but just reset the last element
+ * to zero.
+ */
+static void tsync_works_trim(struct tsync_works *s)
+{
+ struct tsync_work *ctx;
+
+ if (WARN_ON_ONCE(s->size <= 0))
+ return;
+
+ ctx = s->works[s->size - 1];
+
+ /*
+ * For consistency, remove the task from ctx so that it does not look like
+ * we handed it a task_work.
+ */
+ put_task_struct(ctx->task);
+ memset(ctx, 0, sizeof(*ctx));
+
+ /*
+ * Cancel the tsync_works_provide() change to recycle the reserved memory
+ * for the next thread, if any. This also ensures that cancel_tsync_works()
+ * and tsync_works_release() do not see any NULL task pointers.
+ */
+ s->size--;
+}
+
/*
* tsync_works_grow_by - preallocates space for n more contexts in s
*
@@ -276,7 +310,7 @@ static void tsync_works_release(struct tsync_works *s)
size_t i;
for (i = 0; i < s->size; i++) {
- if (!s->works[i]->task)
+ if (WARN_ON_ONCE(!s->works[i]->task))
continue;
put_task_struct(s->works[i]->task);
@@ -379,16 +413,14 @@ static bool schedule_task_work(struct tsync_works *works,
init_task_work(&ctx->work, restrict_one_thread_callback);
err = task_work_add(thread, &ctx->work, TWA_SIGNAL);
- if (err) {
+ if (unlikely(err)) {
/*
* task_work_add() only fails if the task is about to exit. We
* checked that earlier, but it can happen as a race. Resume
* without setting an error, as the task is probably gone in the
- * next loop iteration. For consistency, remove the task from ctx
- * so that it does not look like we handed it a task_work.
+ * next loop iteration.
*/
- put_task_struct(ctx->task);
- ctx->task = NULL;
+ tsync_works_trim(works);
atomic_dec(&shared_ctx->num_preparing);
atomic_dec(&shared_ctx->num_unfinished);
@@ -412,6 +444,9 @@ static void cancel_tsync_works(struct tsync_works *works,
int i;
for (i = 0; i < works->size; i++) {
+ if (WARN_ON_ONCE(!works->works[i]->task))
+ continue;
+
if (!task_work_cancel(works->works[i]->task,
&works->works[i]->work))
continue;
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v2 1/2] landlock: Fully release unused TSYNC work entries
From: Günther Noack @ 2026-02-17 12:41 UTC (permalink / raw)
To: Mickaël Salaün; +Cc: linux-security-module, Jann Horn
In-Reply-To: <20260217122341.2359582-1-mic@digikod.net>
On Tue, Feb 17, 2026 at 01:23:39PM +0100, Mickaël Salaün wrote:
> If task_work_add() failed, ctx->task is put but the tsync_works struct
> is not reset to its previous state. The first consequence is that the
> kernel allocates memory for dying threads, which could lead to
> user-accounted memory exhaustion (not very useful nor specific to this
> case). The second consequence is that task_work_cancel(), called by
> cancel_tsync_works(), can dereference a NULL task pointer.
>
> Fix this issues by keeping a consistent works->size wrt the added task
> work. This is done in a new tsync_works_trim() helper which also cleans
> up the shared_ctx and work fields.
>
> As a safeguard, add a pointer check to cancel_tsync_works() and update
> tsync_works_release() accordingly.
>
> Cc: Günther Noack <gnoack@google.com>
> Cc: Jann Horn <jannh@google.com>
> Signed-off-by: Mickaël Salaün <mic@digikod.net>
> ---
>
> Changes since v1:
> https://lore.kernel.org/all/20260216142641.2100407-1-mic@digikod.net/
> - Move the return/release logic into a new tsync_works_trim() helper
> (suggested by Günther).
> - Reset the whole ctx with memset().
> - Add an unlinkely(err).
> ---
> security/landlock/tsync.c | 47 ++++++++++++++++++++++++++++++++++-----
> 1 file changed, 41 insertions(+), 6 deletions(-)
>
> diff --git a/security/landlock/tsync.c b/security/landlock/tsync.c
> index 0d2b9c646030..42cc0ef0c704 100644
> --- a/security/landlock/tsync.c
> +++ b/security/landlock/tsync.c
> @@ -203,6 +203,40 @@ static struct tsync_work *tsync_works_provide(struct tsync_works *s,
> return ctx;
> }
>
> +/**
> + * tsync_works_trim - Put the last tsync_work element
> + *
> + * @s: TSYNC works to trim.
> + *
> + * Put the last task and decrement the size of @s.
> + *
> + * This helper does not cancel a running task, but just reset the last element
> + * to zero.
> + */
> +static void tsync_works_trim(struct tsync_works *s)
> +{
> + struct tsync_work *ctx;
> +
> + if (WARN_ON_ONCE(s->size <= 0))
> + return;
> +
> + ctx = s->works[s->size - 1];
> +
> + /*
> + * For consistency, remove the task from ctx so that it does not look like
> + * we handed it a task_work.
> + */
> + put_task_struct(ctx->task);
> + memset(ctx, 0, sizeof(*ctx));
Minor (and highly optional) remark, this is the same as
*ctx = (struct tsync_work){};
which I find slightly easier to read when resetting a struct value.
Both is fine though.
> +
> + /*
> + * Cancel the tsync_works_provide() change to recycle the reserved memory
> + * for the next thread, if any. This also ensures that cancel_tsync_works()
> + * and tsync_works_release() do not see any NULL task pointers.
> + */
> + s->size--;
> +}
> +
> /*
> * tsync_works_grow_by - preallocates space for n more contexts in s
> *
> @@ -276,7 +310,7 @@ static void tsync_works_release(struct tsync_works *s)
> size_t i;
>
> for (i = 0; i < s->size; i++) {
> - if (!s->works[i]->task)
> + if (WARN_ON_ONCE(!s->works[i]->task))
> continue;
>
> put_task_struct(s->works[i]->task);
> @@ -379,16 +413,14 @@ static bool schedule_task_work(struct tsync_works *works,
>
> init_task_work(&ctx->work, restrict_one_thread_callback);
> err = task_work_add(thread, &ctx->work, TWA_SIGNAL);
> - if (err) {
> + if (unlikely(err)) {
> /*
> * task_work_add() only fails if the task is about to exit. We
> * checked that earlier, but it can happen as a race. Resume
> * without setting an error, as the task is probably gone in the
> - * next loop iteration. For consistency, remove the task from ctx
> - * so that it does not look like we handed it a task_work.
> + * next loop iteration.
> */
> - put_task_struct(ctx->task);
> - ctx->task = NULL;
> + tsync_works_trim(works);
>
> atomic_dec(&shared_ctx->num_preparing);
> atomic_dec(&shared_ctx->num_unfinished);
> @@ -412,6 +444,9 @@ static void cancel_tsync_works(struct tsync_works *works,
> int i;
>
> for (i = 0; i < works->size; i++) {
> + if (WARN_ON_ONCE(!works->works[i]->task))
> + continue;
> +
> if (!task_work_cancel(works->works[i]->task,
> &works->works[i]->work))
> continue;
> --
> 2.53.0
>
Reviewed-by: Günther Noack <gnoack@google.com>
Thanks for spotting and fixing this!
—Günther
^ permalink raw reply
* Re: [PATCH v2 1/2] landlock: Fully release unused TSYNC work entries
From: Mickaël Salaün @ 2026-02-17 13:52 UTC (permalink / raw)
To: Günther Noack; +Cc: linux-security-module, Jann Horn
In-Reply-To: <aZRh52TIPAmMPJxc@google.com>
On Tue, Feb 17, 2026 at 01:41:11PM +0100, Günther Noack wrote:
> On Tue, Feb 17, 2026 at 01:23:39PM +0100, Mickaël Salaün wrote:
> > If task_work_add() failed, ctx->task is put but the tsync_works struct
> > is not reset to its previous state. The first consequence is that the
> > kernel allocates memory for dying threads, which could lead to
> > user-accounted memory exhaustion (not very useful nor specific to this
> > case). The second consequence is that task_work_cancel(), called by
> > cancel_tsync_works(), can dereference a NULL task pointer.
> >
> > Fix this issues by keeping a consistent works->size wrt the added task
> > work. This is done in a new tsync_works_trim() helper which also cleans
> > up the shared_ctx and work fields.
> >
> > As a safeguard, add a pointer check to cancel_tsync_works() and update
> > tsync_works_release() accordingly.
> >
> > Cc: Günther Noack <gnoack@google.com>
> > Cc: Jann Horn <jannh@google.com>
> > Signed-off-by: Mickaël Salaün <mic@digikod.net>
> > ---
> >
> > Changes since v1:
> > https://lore.kernel.org/all/20260216142641.2100407-1-mic@digikod.net/
> > - Move the return/release logic into a new tsync_works_trim() helper
> > (suggested by Günther).
> > - Reset the whole ctx with memset().
> > - Add an unlinkely(err).
> > ---
> > security/landlock/tsync.c | 47 ++++++++++++++++++++++++++++++++++-----
> > 1 file changed, 41 insertions(+), 6 deletions(-)
> >
> > diff --git a/security/landlock/tsync.c b/security/landlock/tsync.c
> > index 0d2b9c646030..42cc0ef0c704 100644
> > --- a/security/landlock/tsync.c
> > +++ b/security/landlock/tsync.c
> > @@ -203,6 +203,40 @@ static struct tsync_work *tsync_works_provide(struct tsync_works *s,
> > return ctx;
> > }
> >
> > +/**
> > + * tsync_works_trim - Put the last tsync_work element
> > + *
> > + * @s: TSYNC works to trim.
> > + *
> > + * Put the last task and decrement the size of @s.
> > + *
> > + * This helper does not cancel a running task, but just reset the last element
> > + * to zero.
> > + */
> > +static void tsync_works_trim(struct tsync_works *s)
> > +{
> > + struct tsync_work *ctx;
> > +
> > + if (WARN_ON_ONCE(s->size <= 0))
> > + return;
> > +
> > + ctx = s->works[s->size - 1];
> > +
> > + /*
> > + * For consistency, remove the task from ctx so that it does not look like
> > + * we handed it a task_work.
> > + */
> > + put_task_struct(ctx->task);
> > + memset(ctx, 0, sizeof(*ctx));
>
> Minor (and highly optional) remark, this is the same as
>
> *ctx = (struct tsync_work){};
What about:
*ctx = (typeof(*ctx)){};
>
> which I find slightly easier to read when resetting a struct value.
> Both is fine though.
>
> > +
> > + /*
> > + * Cancel the tsync_works_provide() change to recycle the reserved memory
> > + * for the next thread, if any. This also ensures that cancel_tsync_works()
> > + * and tsync_works_release() do not see any NULL task pointers.
> > + */
> > + s->size--;
> > +}
> > +
> > /*
> > * tsync_works_grow_by - preallocates space for n more contexts in s
> > *
> > @@ -276,7 +310,7 @@ static void tsync_works_release(struct tsync_works *s)
> > size_t i;
> >
> > for (i = 0; i < s->size; i++) {
> > - if (!s->works[i]->task)
> > + if (WARN_ON_ONCE(!s->works[i]->task))
> > continue;
> >
> > put_task_struct(s->works[i]->task);
> > @@ -379,16 +413,14 @@ static bool schedule_task_work(struct tsync_works *works,
> >
> > init_task_work(&ctx->work, restrict_one_thread_callback);
> > err = task_work_add(thread, &ctx->work, TWA_SIGNAL);
> > - if (err) {
> > + if (unlikely(err)) {
> > /*
> > * task_work_add() only fails if the task is about to exit. We
> > * checked that earlier, but it can happen as a race. Resume
> > * without setting an error, as the task is probably gone in the
> > - * next loop iteration. For consistency, remove the task from ctx
> > - * so that it does not look like we handed it a task_work.
> > + * next loop iteration.
> > */
> > - put_task_struct(ctx->task);
> > - ctx->task = NULL;
> > + tsync_works_trim(works);
> >
> > atomic_dec(&shared_ctx->num_preparing);
> > atomic_dec(&shared_ctx->num_unfinished);
> > @@ -412,6 +444,9 @@ static void cancel_tsync_works(struct tsync_works *works,
> > int i;
> >
> > for (i = 0; i < works->size; i++) {
> > + if (WARN_ON_ONCE(!works->works[i]->task))
> > + continue;
> > +
> > if (!task_work_cancel(works->works[i]->task,
> > &works->works[i]->work))
> > continue;
> > --
> > 2.53.0
> >
>
> Reviewed-by: Günther Noack <gnoack@google.com>
>
> Thanks for spotting and fixing this!
> —Günther
>
^ permalink raw reply
* Re: [PATCH v2 1/2] landlock: Fully release unused TSYNC work entries
From: Günther Noack @ 2026-02-17 16:35 UTC (permalink / raw)
To: Mickaël Salaün; +Cc: linux-security-module, Jann Horn
In-Reply-To: <20260217.cheoghae8Ahh@digikod.net>
On Tue, Feb 17, 2026 at 02:52:46PM +0100, Mickaël Salaün wrote:
> On Tue, Feb 17, 2026 at 01:41:11PM +0100, Günther Noack wrote:
> > On Tue, Feb 17, 2026 at 01:23:39PM +0100, Mickaël Salaün wrote:
> > > + memset(ctx, 0, sizeof(*ctx));
> >
> > Minor (and highly optional) remark, this is the same as
> >
> > *ctx = (struct tsync_work){};
>
> What about:
>
> *ctx = (typeof(*ctx)){};
I find that harder to read, because it is less commonly seen and the typeof() is
an indirection that makes me think as a reader. But at this point, this is only
a vague opinion and I don't feel strongly about it. Please submit either one of
these three options :)
—Günther
^ permalink raw reply
* Re: [PATCH RFC] security: add LSM blob and hooks for namespaces
From: Casey Schaufler @ 2026-02-17 17:29 UTC (permalink / raw)
To: Christian Brauner
Cc: Paul Moore, James Morris, linux-security-module, linux-kernel,
Casey Schaufler
In-Reply-To: <20260217-glasur-hinnimmt-ac72b3e67661@brauner>
On 2/17/2026 1:38 AM, Christian Brauner wrote:
> On Mon, Feb 16, 2026 at 09:34:57AM -0800, Casey Schaufler wrote:
>> On 2/16/2026 5:52 AM, Christian Brauner wrote:
>>> All namespace types now share the same ns_common infrastructure. Extend
>>> this to include a security blob so LSMs can start managing namespaces
>>> uniformly without having to add one-off hooks or security fields to
>>> every individual namespace type.
>> The implementation appears sound.
>>
>> I have to question whether having LSM controls on namespaces is reasonable.
> This is already in active use today but only in a very limited capacity.
> This generalizes it.
>
>> I suppose that you could have a system where (for example) SELinux runs
>> in permissive mode except within a specific user namespace, where it would
>> enforce policy. Do you have a use case in mind?
> We will use it in systemd services and containers to monitor and
> supervise namespaces.
While I am not among them, many people have objected strongly to making
containers an identified entity in the kernel. If these hooks were available
implementing a container scheme completely within the kernel would be
reasonably strait forward. I might consider tackling it myself.
I am also reminded of the kdbus effort of a decade ago:
https://www.linuxfoundation.org/blog/blog/kdbus-details
Are we ready for ksystemd? UNIX systems of the 1980's suffered greatly from
an excessive enthusiasm for pushing user space functionality (e.g. STREAMS)
into the kernel. Systemd is a fine scheme, but so was inittab, in a very
different way. Adding kernel facilities to support particular application
schemes is very tempting, but often leads to dead code and interfaces that
require maintenance long after the user space scheme has moved on.
^ permalink raw reply
* Re: [PATCH] xfrm: kill xfrm_dev_{state,policy}_flush_secctx_check()
From: Steffen Klassert @ 2026-02-18 9:22 UTC (permalink / raw)
To: Tetsuo Handa
Cc: Paul Moore, SELinux, linux-security-module, Herbert Xu,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Network Development
In-Reply-To: <d20f1b63-714f-48ba-9bee-cd074205404f@I-love.SAKURA.ne.jp>
On Fri, Feb 13, 2026 at 10:59:15PM +0900, Tetsuo Handa wrote:
> On 2026/02/13 19:19, Steffen Klassert wrote:
> On 2026/02/13 19:19, Steffen Klassert wrote:
> >> The NETDEV_UNREGISTER path can be triggered by just doing "unshare -n ip addr show"
> >> (i.e. implicit cleanup of a network namespace due to termination of init process in
> >> that namespace). We are not allowed to reject the cleanup_net() route.
> >
> > And here we come to the other problem I mentioned. When a LSM policy
> > rejects to flush the xfrm states and policies on network namespace
> > exit, we leak all the xfrm states and policies in that namespace.
> > Here we have no other option, we must flush the xfrm states and
> > policies regardless of any LSM policy. This can be fixed with
> > something like that:
>
> This something is what I explained at
> https://lkml.kernel.org/r/1bb453af-3ef2-4ab6-a909-0705bd07c136@I-love.SAKURA.ne.jp .
> The "task_valid" argument does not always reflect whether LSM policy can reject or not.
That was to fix the memleak on network namespace exit.
The task_valid check should be ok for xfrm_policy_flush()
and xfrm_state_flush().
>
> Anyway, the patch to add xfrm_dev_unregister(dev) seems OK if we do like
> https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit?h=next-20260123&id=fc0f090e41e652d158f946c616cdd82baed3c8f4 ?
That would be OK as a first fix. Later we should
just unlink policies and states from the device,
as explained in my last mail.
^ permalink raw reply
* Re: [PATCH v5 2/9] landlock: Control pathname UNIX domain socket resolution by path
From: Mickaël Salaün @ 2026-02-18 9:37 UTC (permalink / raw)
To: Günther Noack
Cc: John Johansen, Tingmao Wang, Justin Suess, Jann Horn,
linux-security-module, Samasth Norway Ananda, Matthieu Buffet,
Mikhail Ivanov, konstantin.meskhidze, Demi Marie Obenour,
Alyssa Ross, Tahera Fahimi
In-Reply-To: <20260215105158.28132-3-gnoack3000@gmail.com>
On Sun, Feb 15, 2026 at 11:51:50AM +0100, Günther Noack wrote:
> * Add a new access right LANDLOCK_ACCESS_FS_RESOLVE_UNIX, which
> controls the look up operations for named UNIX domain sockets. The
> resolution happens during connect() and sendmsg() (depending on
> socket type).
> * Hook into the path lookup in unix_find_bsd() in af_unix.c, using a
> LSM hook. Make policy decisions based on the new access rights
> * Increment the Landlock ABI version.
> * Minor test adaptions to keep the tests working.
>
> With this access right, access is granted if either of the following
> conditions is met:
>
> * The target socket's filesystem path was allow-listed using a
> LANDLOCK_RULE_PATH_BENEATH rule, *or*:
> * The target socket was created in the same Landlock domain in which
> LANDLOCK_ACCESS_FS_RESOLVE_UNIX was restricted.
>
> In case of a denial, connect() and sendmsg() return EACCES, which is
> the same error as it is returned if the user does not have the write
> bit in the traditional Unix file system permissions of that file.
>
> This feature was created with substantial discussion and input from
> Justin Suess, Tingmao Wang and Mickaël Salaün.
>
> Cc: Tingmao Wang <m@maowtm.org>
> Cc: Justin Suess <utilityemal77@gmail.com>
> Cc: Mickaël Salaün <mic@digikod.net>
> Suggested-by: Jann Horn <jannh@google.com>
> Link: https://github.com/landlock-lsm/linux/issues/36
> Signed-off-by: Günther Noack <gnoack3000@gmail.com>
> ---
> include/uapi/linux/landlock.h | 10 ++
> security/landlock/access.h | 11 +-
> security/landlock/audit.c | 1 +
> security/landlock/fs.c | 102 ++++++++++++++++++-
> security/landlock/limits.h | 2 +-
> security/landlock/syscalls.c | 2 +-
> tools/testing/selftests/landlock/base_test.c | 2 +-
> tools/testing/selftests/landlock/fs_test.c | 5 +-
> 8 files changed, 128 insertions(+), 7 deletions(-)
>
> diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
> index f88fa1f68b77..3a8fc3af0d64 100644
> --- a/include/uapi/linux/landlock.h
> +++ b/include/uapi/linux/landlock.h
> @@ -248,6 +248,15 @@ struct landlock_net_port_attr {
> *
> * This access right is available since the fifth version of the Landlock
> * ABI.
> + * - %LANDLOCK_ACCESS_FS_RESOLVE_UNIX: Look up pathname UNIX domain sockets
> + * (:manpage:`unix(7)`). On UNIX domain sockets, this restricts both calls to
> + * :manpage:`connect(2)` as well as calls to :manpage:`sendmsg(2)` with an
> + * explicit recipient address.
> + *
> + * This access right only applies to connections to UNIX server sockets which
> + * were created outside of the newly created Landlock domain (e.g. from within
> + * a parent domain or from an unrestricted process). Newly created UNIX
> + * servers within the same Landlock domain continue to be accessible.
It might help to add a reference to the explicit scope mechanism.
Please squash patch 9/9 into this one and also add a reference here to
the rationale described in security/landlock.rst
> *
> * Whether an opened file can be truncated with :manpage:`ftruncate(2)` or used
> * with `ioctl(2)` is determined during :manpage:`open(2)`, in the same way as
> @@ -333,6 +342,7 @@ struct landlock_net_port_attr {
> #define LANDLOCK_ACCESS_FS_REFER (1ULL << 13)
> #define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14)
> #define LANDLOCK_ACCESS_FS_IOCTL_DEV (1ULL << 15)
> +#define LANDLOCK_ACCESS_FS_RESOLVE_UNIX (1ULL << 16)
> /* clang-format on */
>
> /**
> diff --git a/security/landlock/access.h b/security/landlock/access.h
> index 42c95747d7bd..9a2991688835 100644
> --- a/security/landlock/access.h
> +++ b/security/landlock/access.h
> @@ -34,7 +34,7 @@
> LANDLOCK_ACCESS_FS_IOCTL_DEV)
> /* clang-format on */
>
> -typedef u16 access_mask_t;
> +typedef u32 access_mask_t;
>
> /* Makes sure all filesystem access rights can be stored. */
> static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_FS);
> @@ -76,6 +76,15 @@ struct layer_access_masks {
> access_mask_t access[LANDLOCK_MAX_NUM_LAYERS];
> };
>
> +static inline bool
> +layer_access_masks_empty(const struct layer_access_masks *masks)
> +{
> + for (size_t i = 0; i < ARRAY_SIZE(masks->access); i++)
> + if (masks->access[i])
> + return false;
> + return true;
> +}
> +
> /*
> * Tracks domains responsible of a denied access. This avoids storing in each
> * object the full matrix of per-layer unfulfilled access rights, which is
> diff --git a/security/landlock/audit.c b/security/landlock/audit.c
> index 60ff217ab95b..8d0edf94037d 100644
> --- a/security/landlock/audit.c
> +++ b/security/landlock/audit.c
> @@ -37,6 +37,7 @@ static const char *const fs_access_strings[] = {
> [BIT_INDEX(LANDLOCK_ACCESS_FS_REFER)] = "fs.refer",
> [BIT_INDEX(LANDLOCK_ACCESS_FS_TRUNCATE)] = "fs.truncate",
> [BIT_INDEX(LANDLOCK_ACCESS_FS_IOCTL_DEV)] = "fs.ioctl_dev",
> + [BIT_INDEX(LANDLOCK_ACCESS_FS_RESOLVE_UNIX)] = "fs.resolve_unix",
> };
>
> static_assert(ARRAY_SIZE(fs_access_strings) == LANDLOCK_NUM_ACCESS_FS);
> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> index e764470f588c..76035c6f2bf1 100644
> --- a/security/landlock/fs.c
> +++ b/security/landlock/fs.c
> @@ -27,6 +27,7 @@
> #include <linux/lsm_hooks.h>
> #include <linux/mount.h>
> #include <linux/namei.h>
> +#include <linux/net.h>
> #include <linux/path.h>
> #include <linux/pid.h>
> #include <linux/rcupdate.h>
> @@ -314,7 +315,8 @@ static struct landlock_object *get_inode_object(struct inode *const inode)
> LANDLOCK_ACCESS_FS_WRITE_FILE | \
> LANDLOCK_ACCESS_FS_READ_FILE | \
> LANDLOCK_ACCESS_FS_TRUNCATE | \
> - LANDLOCK_ACCESS_FS_IOCTL_DEV)
> + LANDLOCK_ACCESS_FS_IOCTL_DEV | \
> + LANDLOCK_ACCESS_FS_RESOLVE_UNIX)
> /* clang-format on */
>
> /*
> @@ -1561,6 +1563,103 @@ static int hook_path_truncate(const struct path *const path)
> return current_check_access_path(path, LANDLOCK_ACCESS_FS_TRUNCATE);
> }
>
> +/**
> + * unmask_scoped_access - Remove access right bits in @masks in all layers
> + * where @client and @server have the same domain
> + *
> + * This does the same as domain_is_scoped(), but unmasks bits in @masks.
> + * It can not return early as domain_is_scoped() does.
> + *
> + * @client: Client domain
> + * @server: Server domain
> + * @masks: Layer access masks to unmask
> + * @access: Access bit that controls scoping
> + */
> +static void unmask_scoped_access(const struct landlock_ruleset *const client,
> + const struct landlock_ruleset *const server,
> + struct layer_access_masks *const masks,
> + const access_mask_t access)
This helper should be moved to task.c and factored out with
domain_is_scoped(). This should be a dedicated patch.
> +{
> + int client_layer, server_layer;
> + const struct landlock_hierarchy *client_walker, *server_walker;
> +
> + if (WARN_ON_ONCE(!client))
> + return; /* should not happen */
> +
> + if (!server)
> + return; /* server has no Landlock domain; nothing to clear */
> +
> + client_layer = client->num_layers - 1;
> + client_walker = client->hierarchy;
> + server_layer = server->num_layers - 1;
> + server_walker = server->hierarchy;
> +
> + /*
> + * Clears the access bits at all layers where the client domain is the
> + * same as the server domain. We start the walk at min(client_layer,
> + * server_layer). The layer bits until there can not be cleared because
> + * either the client or the server domain is missing.
> + */
> + for (; client_layer > server_layer; client_layer--)
> + client_walker = client_walker->parent;
> +
> + for (; server_layer > client_layer; server_layer--)
> + server_walker = server_walker->parent;
> +
> + for (; client_layer >= 0; client_layer--) {
> + if (masks->access[client_layer] & access &&
> + client_walker == server_walker)
> + masks->access[client_layer] &= ~access;
> +
> + client_walker = client_walker->parent;
> + server_walker = server_walker->parent;
> + }
> +}
> +
> +static int hook_unix_find(const struct path *const path, struct sock *other,
> + int flags)
> +{
> + const struct landlock_ruleset *dom_other;
> + const struct landlock_cred_security *subject;
> + struct layer_access_masks layer_masks;
> + struct landlock_request request = {};
> + static const struct access_masks fs_resolve_unix = {
> + .fs = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + };
> +
> + /* Lookup for the purpose of saving coredumps is OK. */
> + if (unlikely(flags & SOCK_COREDUMP))
> + return 0;
> +
> + /* Access to the same (or a lower) domain is always allowed. */
> + subject = landlock_get_applicable_subject(current_cred(),
> + fs_resolve_unix, NULL);
> +
> + if (!subject)
> + return 0;
> +
> + if (!landlock_init_layer_masks(subject->domain, fs_resolve_unix.fs,
> + &layer_masks, LANDLOCK_KEY_INODE))
> + return 0;
> +
> + /* Checks the layers in which we are connecting within the same domain. */
> + dom_other = landlock_cred(other->sk_socket->file->f_cred)->domain;
We need to call unix_state_lock(other) before reading it, and check for
SOCK_DEAD, and check sk_socket before dereferencing it. Indeed,
the socket can be make orphan (see unix_dgram_sendmsg and
unix_stream_connect). I *think* a socket cannot be "resurrected" or
recycled once dead, so we may assume there is no race condition wrt
dom_other, but please double check. This lockless call should be made
clear in the LSM hook. It's OK to not lock the socket before
security_unix_find() (1) because no LSM might implement and (2) they
might not need to lock the socket (e.g. if the caller is not sandboxed).
The updated code should look something like this:
unix_state_unlock(other);
if (unlikely(sock_flag(other, SOCK_DEAD) || !other->sk_socket)) {
unix_state_unlock(other);
return 0;
}
dom_other = landlock_cred(other->sk_socket->file->f_cred)->domain;
unix_state_unlock(other);
> + unmask_scoped_access(subject->domain, dom_other, &layer_masks,
> + fs_resolve_unix.fs);
> +
> + if (layer_access_masks_empty(&layer_masks))
> + return 0;
> +
> + /* Checks the connections to allow-listed paths. */
> + if (is_access_to_paths_allowed(subject->domain, path,
> + fs_resolve_unix.fs, &layer_masks,
> + &request, NULL, 0, NULL, NULL, NULL))
> + return 0;
> +
> + landlock_log_denial(subject, &request);
> + return -EACCES;
> +}
> +
> /* File hooks */
>
> /**
> @@ -1838,6 +1937,7 @@ static struct security_hook_list landlock_hooks[] __ro_after_init = {
> LSM_HOOK_INIT(path_unlink, hook_path_unlink),
> LSM_HOOK_INIT(path_rmdir, hook_path_rmdir),
> LSM_HOOK_INIT(path_truncate, hook_path_truncate),
> + LSM_HOOK_INIT(unix_find, hook_unix_find),
>
> LSM_HOOK_INIT(file_alloc_security, hook_file_alloc_security),
> LSM_HOOK_INIT(file_open, hook_file_open),
> diff --git a/security/landlock/limits.h b/security/landlock/limits.h
> index eb584f47288d..b454ad73b15e 100644
> --- a/security/landlock/limits.h
> +++ b/security/landlock/limits.h
> @@ -19,7 +19,7 @@
> #define LANDLOCK_MAX_NUM_LAYERS 16
> #define LANDLOCK_MAX_NUM_RULES U32_MAX
>
> -#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_IOCTL_DEV
> +#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_RESOLVE_UNIX
> #define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1)
> #define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS)
>
> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
> index 0d66a68677b7..933902d43241 100644
> --- a/security/landlock/syscalls.c
> +++ b/security/landlock/syscalls.c
> @@ -164,7 +164,7 @@ static const struct file_operations ruleset_fops = {
> * If the change involves a fix that requires userspace awareness, also update
> * the errata documentation in Documentation/userspace-api/landlock.rst .
> */
> -const int landlock_abi_version = 8;
> +const int landlock_abi_version = 9;
>
> /**
> * sys_landlock_create_ruleset - Create a new ruleset
> diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c
> index 0fea236ef4bd..30d37234086c 100644
> --- a/tools/testing/selftests/landlock/base_test.c
> +++ b/tools/testing/selftests/landlock/base_test.c
> @@ -76,7 +76,7 @@ TEST(abi_version)
> const struct landlock_ruleset_attr ruleset_attr = {
> .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
> };
> - ASSERT_EQ(8, landlock_create_ruleset(NULL, 0,
> + ASSERT_EQ(9, landlock_create_ruleset(NULL, 0,
> LANDLOCK_CREATE_RULESET_VERSION));
>
> ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,
> diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
> index 968a91c927a4..b318627e7561 100644
> --- a/tools/testing/selftests/landlock/fs_test.c
> +++ b/tools/testing/selftests/landlock/fs_test.c
> @@ -575,9 +575,10 @@ TEST_F_FORK(layout1, inval)
> LANDLOCK_ACCESS_FS_WRITE_FILE | \
> LANDLOCK_ACCESS_FS_READ_FILE | \
> LANDLOCK_ACCESS_FS_TRUNCATE | \
> - LANDLOCK_ACCESS_FS_IOCTL_DEV)
> + LANDLOCK_ACCESS_FS_IOCTL_DEV | \
> + LANDLOCK_ACCESS_FS_RESOLVE_UNIX)
>
> -#define ACCESS_LAST LANDLOCK_ACCESS_FS_IOCTL_DEV
> +#define ACCESS_LAST LANDLOCK_ACCESS_FS_RESOLVE_UNIX
>
> #define ACCESS_ALL ( \
> ACCESS_FILE | \
> --
> 2.52.0
>
>
^ permalink raw reply
* Re: [PATCH v5 8/9] landlock: Document FS access right for pathname UNIX sockets
From: Mickaël Salaün @ 2026-02-18 9:39 UTC (permalink / raw)
To: Günther Noack
Cc: John Johansen, Justin Suess, linux-security-module, Tingmao Wang,
Samasth Norway Ananda, Matthieu Buffet, Mikhail Ivanov,
konstantin.meskhidze, Demi Marie Obenour, Alyssa Ross, Jann Horn,
Tahera Fahimi
In-Reply-To: <20260215105158.28132-9-gnoack3000@gmail.com>
On Sun, Feb 15, 2026 at 11:51:56AM +0100, Günther Noack wrote:
> Cc: Justin Suess <utilityemal77@gmail.com>
> Cc: Mickaël Salaün <mic@digikod.net>
> Signed-off-by: Günther Noack <gnoack3000@gmail.com>
> ---
> Documentation/userspace-api/landlock.rst | 16 +++++++++++++++-
> 1 file changed, 15 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst
> index 13134bccdd39..3ba73afcbc4b 100644
> --- a/Documentation/userspace-api/landlock.rst
> +++ b/Documentation/userspace-api/landlock.rst
> @@ -77,7 +77,8 @@ to be explicit about the denied-by-default access rights.
> LANDLOCK_ACCESS_FS_MAKE_SYM |
> LANDLOCK_ACCESS_FS_REFER |
> LANDLOCK_ACCESS_FS_TRUNCATE |
> - LANDLOCK_ACCESS_FS_IOCTL_DEV,
> + LANDLOCK_ACCESS_FS_IOCTL_DEV |
> + LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> .handled_access_net =
> LANDLOCK_ACCESS_NET_BIND_TCP |
> LANDLOCK_ACCESS_NET_CONNECT_TCP,
> @@ -127,6 +128,12 @@ version, and only use the available subset of access rights:
> /* Removes LANDLOCK_SCOPE_* for ABI < 6 */
> ruleset_attr.scoped &= ~(LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET |
> LANDLOCK_SCOPE_SIGNAL);
> + __attribute__((fallthrough));
> + case 7:
> + __attribute__((fallthrough));
I don't think the fallthrough attribute is needed here. Same for the
sample.
> + case 8:
> + /* Removes LANDLOCK_ACCESS_FS_RESOLVE_UNIX for ABI < 8 */
ABI < 9
> + ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_RESOLVE_UNIX;
> }
>
> This enables the creation of an inclusive ruleset that will contain our rules.
> @@ -685,6 +692,13 @@ enforce Landlock rulesets across all threads of the calling process
> using the ``LANDLOCK_RESTRICT_SELF_TSYNC`` flag passed to
> sys_landlock_restrict_self().
>
> +Pathname UNIX sockets (ABI < 9)
> +-------------------------------
> +
> +Starting with the Landlock ABI version 9, it is possible to restrict
> +connections to pathname UNIX domain sockets (:manpage:`unix(7)`) using
> +the new ``LANDLOCK_ACCESS_FS_RESOLVE_UNIX`` right.
> +
> .. _kernel_support:
>
> Kernel support
> --
> 2.52.0
>
>
^ permalink raw reply
* Re: [PATCH v5 1/9] lsm: Add LSM hook security_unix_find
From: Mickaël Salaün @ 2026-02-18 9:36 UTC (permalink / raw)
To: Günther Noack
Cc: John Johansen, Paul Moore, James Morris, Serge E . Hallyn,
Tingmao Wang, Justin Suess, linux-security-module,
Samasth Norway Ananda, Matthieu Buffet, Mikhail Ivanov,
konstantin.meskhidze, Demi Marie Obenour, Alyssa Ross, Jann Horn,
Tahera Fahimi, Simon Horman, netdev, Alexander Viro,
Christian Brauner
In-Reply-To: <20260215105158.28132-2-gnoack3000@gmail.com>
On Sun, Feb 15, 2026 at 11:51:49AM +0100, Günther Noack wrote:
> From: Justin Suess <utilityemal77@gmail.com>
>
> Add a LSM hook security_unix_find.
>
> This hook is called to check the path of a named unix socket before a
> connection is initiated. The peer socket may be inspected as well.
>
> Why existing hooks are unsuitable:
>
> Existing socket hooks, security_unix_stream_connect(),
> security_unix_may_send(), and security_socket_connect() don't provide
> TOCTOU-free / namespace independent access to the paths of sockets.
>
> (1) We cannot resolve the path from the struct sockaddr in existing hooks.
> This requires another path lookup. A change in the path between the
> two lookups will cause a TOCTOU bug.
>
> (2) We cannot use the struct path from the listening socket, because it
> may be bound to a path in a different namespace than the caller,
> resulting in a path that cannot be referenced at policy creation time.
>
> Cc: Günther Noack <gnoack3000@gmail.com>
> Cc: Tingmao Wang <m@maowtm.org>
> Signed-off-by: Justin Suess <utilityemal77@gmail.com>
> ---
> include/linux/lsm_hook_defs.h | 5 +++++
> include/linux/security.h | 11 +++++++++++
> net/unix/af_unix.c | 8 ++++++++
> security/security.c | 20 ++++++++++++++++++++
> 4 files changed, 44 insertions(+)
>
> diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
> index 8c42b4bde09c..7a0fd3dbfa29 100644
> --- a/include/linux/lsm_hook_defs.h
> +++ b/include/linux/lsm_hook_defs.h
> @@ -317,6 +317,11 @@ LSM_HOOK(int, 0, post_notification, const struct cred *w_cred,
> LSM_HOOK(int, 0, watch_key, struct key *key)
> #endif /* CONFIG_SECURITY && CONFIG_KEY_NOTIFICATIONS */
>
> +#if defined(CONFIG_SECURITY_NETWORK) && defined(CONFIG_SECURITY_PATH)
> +LSM_HOOK(int, 0, unix_find, const struct path *path, struct sock *other,
> + int flags)
> +#endif /* CONFIG_SECURITY_NETWORK && CONFIG_SECURITY_PATH */
> +
> #ifdef CONFIG_SECURITY_NETWORK
> LSM_HOOK(int, 0, unix_stream_connect, struct sock *sock, struct sock *other,
> struct sock *newsk)
> diff --git a/include/linux/security.h b/include/linux/security.h
> index 83a646d72f6f..99a33d8eb28d 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -1931,6 +1931,17 @@ static inline int security_mptcp_add_subflow(struct sock *sk, struct sock *ssk)
> }
> #endif /* CONFIG_SECURITY_NETWORK */
>
> +#if defined(CONFIG_SECURITY_NETWORK) && defined(CONFIG_SECURITY_PATH)
> +
> +int security_unix_find(const struct path *path, struct sock *other, int flags);
> +
> +#else /* CONFIG_SECURITY_NETWORK && CONFIG_SECURITY_PATH */
> +static inline int security_unix_find(const struct path *path, struct sock *other, int flags)
> +{
> + return 0;
> +}
> +#endif /* CONFIG_SECURITY_NETWORK && CONFIG_SECURITY_PATH */
> +
> #ifdef CONFIG_SECURITY_INFINIBAND
> int security_ib_pkey_access(void *sec, u64 subnet_prefix, u16 pkey);
> int security_ib_endport_manage_subnet(void *sec, const char *name, u8 port_num);
> diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
> index d0511225799b..369812b79dd8 100644
> --- a/net/unix/af_unix.c
> +++ b/net/unix/af_unix.c
> @@ -1230,6 +1230,14 @@ static struct sock *unix_find_bsd(struct sockaddr_un *sunaddr, int addr_len,
> if (!sk)
> goto path_put;
>
> + /*
> + * We call the hook because we know that the inode is a socket and we
> + * hold a valid reference to it via the path.
> + */
> + err = security_unix_find(&path, sk, flags);
> + if (err)
> + goto sock_put;
> +
> err = -EPROTOTYPE;
> if (sk->sk_type == type)
I think this hook call should be moved here, just before the
touch_atime() call for consistency with the socket type check, and to
avoid doing useless check in the hook.
> touch_atime(&path);
> 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.
> + *
> + * Return: Returns 0 if permission is granted.
> + */
> +int security_unix_find(const struct path *path, struct sock *other, int flags)
> +{
> + return call_int_hook(unix_find, path, other, flags);
> +}
> +EXPORT_SYMBOL(security_unix_find);
> +
> +#endif /* CONFIG_SECURITY_NETWORK && CONFIG_SECURITY_PATH */
> +
> #ifdef CONFIG_SECURITY_INFINIBAND
> /**
> * security_ib_pkey_access() - Check if access to an IB pkey is allowed
> --
> 2.52.0
>
>
^ permalink raw reply
* Re: [PATCH v5 3/9] samples/landlock: Add support for named UNIX domain socket restrictions
From: Mickaël Salaün @ 2026-02-18 9:37 UTC (permalink / raw)
To: Günther Noack
Cc: John Johansen, Justin Suess, linux-security-module, Tingmao Wang,
Samasth Norway Ananda, Matthieu Buffet, Mikhail Ivanov,
konstantin.meskhidze, Demi Marie Obenour, Alyssa Ross, Jann Horn,
Tahera Fahimi
In-Reply-To: <20260215105158.28132-4-gnoack3000@gmail.com>
On Sun, Feb 15, 2026 at 11:51:51AM +0100, Günther Noack wrote:
> The access right for UNIX domain socket lookups is grouped with the
> read-write rights in the sample tool. Rationale: In the general case,
> any operations are possible through a UNIX domain socket, including
> data-mutating operations.
>
> Cc: Justin Suess <utilityemal77@gmail.com>
> Cc: Mickaël Salaün <mic@digikod.net>
> Signed-off-by: Günther Noack <gnoack3000@gmail.com>
> ---
> samples/landlock/sandboxer.c | 15 ++++++++++++---
> 1 file changed, 12 insertions(+), 3 deletions(-)
>
> diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
> index e7af02f98208..0bbbc5c9ead6 100644
> --- a/samples/landlock/sandboxer.c
> +++ b/samples/landlock/sandboxer.c
> @@ -111,7 +111,8 @@ static int parse_path(char *env_path, const char ***const path_list)
> LANDLOCK_ACCESS_FS_WRITE_FILE | \
> LANDLOCK_ACCESS_FS_READ_FILE | \
> LANDLOCK_ACCESS_FS_TRUNCATE | \
> - LANDLOCK_ACCESS_FS_IOCTL_DEV)
> + LANDLOCK_ACCESS_FS_IOCTL_DEV | \
> + LANDLOCK_ACCESS_FS_RESOLVE_UNIX)
>
> /* clang-format on */
>
> @@ -295,11 +296,12 @@ static bool check_ruleset_scope(const char *const env_var,
> LANDLOCK_ACCESS_FS_MAKE_SYM | \
> LANDLOCK_ACCESS_FS_REFER | \
> LANDLOCK_ACCESS_FS_TRUNCATE | \
> - LANDLOCK_ACCESS_FS_IOCTL_DEV)
> + LANDLOCK_ACCESS_FS_IOCTL_DEV | \
> + LANDLOCK_ACCESS_FS_RESOLVE_UNIX)
>
> /* clang-format on */
>
> -#define LANDLOCK_ABI_LAST 7
> +#define LANDLOCK_ABI_LAST 9
>
> #define XSTR(s) #s
> #define STR(s) XSTR(s)
> @@ -444,6 +446,13 @@ int main(const int argc, char *const argv[], char *const *const envp)
> "provided by ABI version %d (instead of %d).\n",
> LANDLOCK_ABI_LAST, abi);
> __attribute__((fallthrough));
> + case 7:
> + __attribute__((fallthrough));
The current code should print the hint when ABI <= 7. Please send a
dedicated patch to fix the TSYNC-related changes.
> + case 8:
> + /* Removes LANDLOCK_ACCESS_FS_RESOLVE_UNIX for ABI < 9 */
> + ruleset_attr.handled_access_fs &=
> + ~LANDLOCK_ACCESS_FS_RESOLVE_UNIX;
> + __attribute__((fallthrough));
> case LANDLOCK_ABI_LAST:
> break;
> default:
> --
> 2.52.0
>
>
^ permalink raw reply
* Re: [PATCH RFC] security: add LSM blob and hooks for namespaces
From: Dr. Greg @ 2026-02-18 11:15 UTC (permalink / raw)
To: Christian Brauner
Cc: Casey Schaufler, Paul Moore, James Morris, linux-security-module,
linux-kernel
In-Reply-To: <20260217-glasur-hinnimmt-ac72b3e67661@brauner>
On Tue, Feb 17, 2026 at 10:38:33AM +0100, Christian Brauner wrote:
Good morning, I hope the week is going well for everyone.
> On Mon, Feb 16, 2026 at 09:34:57AM -0800, Casey Schaufler wrote:
> > On 2/16/2026 5:52 AM, Christian Brauner wrote:
> > > All namespace types now share the same ns_common infrastructure. Extend
> > > this to include a security blob so LSMs can start managing namespaces
> > > uniformly without having to add one-off hooks or security fields to
> > > every individual namespace type.
> >
> > The implementation appears sound.
> >
> > I have to question whether having LSM controls on namespaces is reasonable.
> This is already in active use today but only in a very limited capacity.
> This generalizes it.
This seems to be a tacid indication of the need for namespace specific
LSM policies and/or controls, and further acknowledgement, that such
controls are in active use out in the wild.
More below on the implications of this.
> > I suppose that you could have a system where (for example) SELinux runs
> > in permissive mode except within a specific user namespace, where it would
> > enforce policy. Do you have a use case in mind?
> We will use it in systemd services and containers to monitor and
> supervise namespaces.
Christian, you are no doubt not familiar with our work, but over the
last six years our team has developed and have in production the most
sophisticated implementation of LSM namespacing that has been done.
With the caveat, of course, of implementations that have been made
public.
That work has been driven by what is the clear and apparent need to
have namespace specific and orthogonal security controls and policies,
something your patch and comments seems to clearly acknowledge. This
need is particularily important with respect to the advancements that
are needed for AI based security modeling and interdiction.
So our comments are driven by having done a bit of this before.
There has been some dialogue and debate as to whether and how LSM
namespacing should be implemented. The essential ingredient is the
need to have a task specific context of data, which can be inherited
by subordinate processes, that can be used to evaluate the LSM
security events/hooks that are executed by tasks having access to
that context of data.
Unless we misinterpret the implementation, your patch provides such
context for any process that wishes to unshare any namespace that it
is participating in.
This in turn implies that your patch is a fundamental step forward in
LSM namespacing. This isn't a criticism, just an observation.
The reason we can feel pretty strongly about this is that we initially
used the same strategy that you are using in a very early
implementation of TSEM. We abandoned that approach, since the
dynamics/politics of Linux kernel development, particularily in
security, tends to disfavor having to touch core kernel
infrastructure, so we implemented the equivalent of your approach
entirely in the context of our LSM.
To widen the scope of the impact of this, your patch also lays the
framework for implementing LSM specific security policy with kernel
modules. Again, not a criticism, just an observation, because we
implement the same capability with TSEM.
For those reading along at home. The reason that this is safe with a
classic namespace approach and not with previous 'loadable LSM'
strategies is that a process can verify that a policy module is loaded
and prepared to handle requests to interpret the events, before the
namespace installation/activation that would drive use of the module
actually takes effect.
Your approach is quite generic, which is positive. The open question
is whether or not the strategy is generic enough to handle LSM's that
may have very dynamic and varied requirements with respect to how to
configure the policy that will be implemented for the namespace.
Hopefully all of this will enable further discussions on this issue.
Best wishes for a productive remainder of the week.
As always,
Dr. Greg
The Quixote Project - Flailing at the Travails of Cybersecurity
https://github.com/Quixote-Project
^ permalink raw reply
* Re: [PATCH 0/2] fanotify: avid some premature LSM checks
From: Ondrej Mosnacek @ 2026-02-18 12:36 UTC (permalink / raw)
To: Jan Kara
Cc: Amir Goldstein, Matthew Bobrowski, linux-fsdevel,
linux-security-module, selinux, linux-kernel
In-Reply-To: <yk2qcux2ee7afr24xw6p7wp4t3islu64ttfsrheac2zwr6odnw@kmagnqbldb3f>
On Tue, Feb 17, 2026 at 12:09 PM Jan Kara <jack@suse.cz> wrote:
>
> On Mon 16-02-26 16:06:23, Ondrej Mosnacek wrote:
> > Restructure some of the validity and security checks in
> > fs/notify/fanotify/fanotify_user.c to avoid generating LSM access
> > denials in the audit log where hey shouldn't be.
> >
> > Ondrej Mosnacek (2):
> > fanotify: avoid/silence premature LSM capability checks
> > fanotify: call fanotify_events_supported() before path_permission()
> > and security_path_notify()
> >
> > fs/notify/fanotify/fanotify_user.c | 50 ++++++++++++++----------------
> > 1 file changed, 23 insertions(+), 27 deletions(-)
>
> The series looks good to me as well. Thanks! I'll commit the series to my
> tree once the merge window closes and fixup the comment formatting on
> commit. No need to resend.
Great, thanks!
--
Ondrej Mosnacek
Senior Software Engineer, Linux Security - SELinux kernel
Red Hat, Inc.
^ permalink raw reply
* Re: [PATCH v5 4/9] landlock/selftests: Test LANDLOCK_ACCESS_FS_RESOLVE_UNIX
From: Mickaël Salaün @ 2026-02-18 19:11 UTC (permalink / raw)
To: Günther Noack
Cc: John Johansen, Justin Suess, Tingmao Wang, linux-security-module,
Samasth Norway Ananda, Matthieu Buffet, Mikhail Ivanov,
konstantin.meskhidze, Demi Marie Obenour, Alyssa Ross, Jann Horn,
Tahera Fahimi
In-Reply-To: <20260215105158.28132-5-gnoack3000@gmail.com>
On Sun, Feb 15, 2026 at 11:51:52AM +0100, Günther Noack wrote:
> * Extract common helpers from an existing IOCTL test that
> also uses pathname unix(7) sockets.
> * These tests use the common scoped domains fixture which is also used
> in other Landlock scoping tests and which was used in Tingmao Wang's
> earlier patch set in [1].
>
> These tests exercise the cross product of the following scenarios:
>
> * Stream connect(), Datagram connect(), Datagram sendmsg() and
> Seqpacket connect().
> * Child-to-parent and parent-to-child communication
> * The Landlock policy configuration as listed in the scoped_domains
> fixture.
> * In the default variant, Landlock domains are only placed where
> prescribed in the fixture.
> * In the "ALL_DOMAINS" variant, Landlock domains are also placed in
> the places where the fixture says to omit them, but with a
> LANDLOCK_RULE_PATH_BENEATH that allows connection.
>
> Cc: Justin Suess <utilityemal77@gmail.com>
> Cc: Tingmao Wang <m@maowtm.org>
> Cc: Mickaël Salaün <mic@digikod.net>
> Link[1]: https://lore.kernel.org/all/53b9883648225d5a08e82d2636ab0b4fda003bc9.1767115163.git.m@maowtm.org/
> Signed-off-by: Günther Noack <gnoack3000@gmail.com>
> ---
> tools/testing/selftests/landlock/fs_test.c | 384 ++++++++++++++++++++-
> 1 file changed, 368 insertions(+), 16 deletions(-)
>
> diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
> index b318627e7561..bdeff2e0e029 100644
> --- a/tools/testing/selftests/landlock/fs_test.c
> +++ b/tools/testing/selftests/landlock/fs_test.c
> @@ -4358,30 +4358,61 @@ TEST_F_FORK(layout1, named_pipe_ioctl)
> ASSERT_EQ(child_pid, waitpid(child_pid, NULL, 0));
> }
>
> +/*
> + * set_up_named_unix_server - Create a pathname unix socket
> + *
> + * If the socket type is not SOCK_DGRAM, also invoke listen(2).
> + *
> + * Return: The listening FD - it is the caller responsibility to close it.
> + */
> +static int set_up_named_unix_server(struct __test_metadata *const _metadata,
> + int type, const char *const path)
> +{
> + int fd;
> + struct sockaddr_un addr = {
> + .sun_family = AF_UNIX,
> + };
> +
> + fd = socket(AF_UNIX, type, 0);
> + ASSERT_LE(0, fd);
> +
> + strncpy(addr.sun_path, path, sizeof(addr.sun_path));
fs_test.c: In function ‘set_up_named_unix_server’:
fs_test.c:4125:9: error: ‘strncpy’ specified bound 108 equals destination size [-Werror=stringop-truncation]
4125 | strncpy(addr.sun_path, path, sizeof(addr.sun_path));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We should also ASSERT the result to make sure path's length is not too big.
> + ASSERT_EQ(0, bind(fd, (struct sockaddr *)&addr, sizeof(addr)));
> +
> + if (type != SOCK_DGRAM)
> + ASSERT_EQ(0, listen(fd, 10 /* qlen */));
> + return fd;
> +}
^ permalink raw reply
* Re: [PATCH v5 6/9] landlock/selftests: Check that coredump sockets stay unrestricted
From: Mickaël Salaün @ 2026-02-18 20:05 UTC (permalink / raw)
To: Günther Noack
Cc: John Johansen, linux-security-module, Tingmao Wang, Justin Suess,
Samasth Norway Ananda, Matthieu Buffet, Mikhail Ivanov,
konstantin.meskhidze, Demi Marie Obenour, Alyssa Ross, Jann Horn,
Tahera Fahimi
In-Reply-To: <20260215105158.28132-7-gnoack3000@gmail.com>
On Sun, Feb 15, 2026 at 11:51:54AM +0100, Günther Noack wrote:
> Even when a process is restricted with the new
> LANDLOCK_ACCESS_FS_RESOLVE_SOCKET right, the kernel can continue
> writing its coredump to the configured coredump socket.
>
> In the test, we create a local server and rewire the system to write
> coredumps into it. We then create a child process within a Landlock
> domain where LANDLOCK_ACCESS_FS_RESOLVE_SOCKET is restricted and make
> the process crash. The test uses SO_PEERCRED to check that the
> connecting client process is the expected one.
>
> Signed-off-by: Günther Noack <gnoack3000@gmail.com>
> ---
> tools/testing/selftests/landlock/fs_test.c | 122 +++++++++++++++++++++
> 1 file changed, 122 insertions(+)
>
> diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
> index 8fa9d7c49ac3..705d8a13d2e0 100644
> --- a/tools/testing/selftests/landlock/fs_test.c
> +++ b/tools/testing/selftests/landlock/fs_test.c
> @@ -22,6 +22,7 @@
> #include <sys/ioctl.h>
> #include <sys/mount.h>
> #include <sys/prctl.h>
> +#include <sys/resource.h>
> #include <sys/sendfile.h>
> #include <sys/socket.h>
> #include <sys/stat.h>
> @@ -4922,6 +4923,127 @@ TEST_F(scoped_domains, unix_seqpacket_connect_to_child_full)
> #undef USE_SENDTO
> #undef ENFORCE_ALL
>
> +static void read_core_pattern(struct __test_metadata *const _metadata,
> + char *buf, size_t buf_size)
> +{
> + int fd;
> + ssize_t ret;
> +
> + fd = open("/proc/sys/kernel/core_pattern", O_RDONLY | O_CLOEXEC);
> + ASSERT_LE(0, fd);
> +
> + ret = read(fd, buf, buf_size - 1);
> + ASSERT_LE(0, ret);
> + EXPECT_EQ(0, close(fd));
> +
> + buf[ret] = '\0';
> +}
> +
> +static void set_core_pattern(struct __test_metadata *const _metadata,
> + const char *pattern)
> +{
> + int fd;
> + size_t len = strlen(pattern);
> +
> + fd = open("/proc/sys/kernel/core_pattern", O_WRONLY | O_CLOEXEC);
> + ASSERT_LE(0, fd);
> +
> + ASSERT_EQ(len, write(fd, pattern, len));
> + EXPECT_EQ(0, close(fd));
> +}
I had to fix this helper to make it work with check-linux.sh:
diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
index ae32513fb54b..64887d34079a 100644
--- a/tools/testing/selftests/landlock/fs_test.c
+++ b/tools/testing/selftests/landlock/fs_test.c
@@ -4659,11 +4659,34 @@ static void set_core_pattern(struct __test_metadata *const _metadata,
int fd;
size_t len = strlen(pattern);
+ /*
+ * Writing to /proc/sys/kernel/core_pattern requires EUID 0 because
+ * sysctl_perm() checks that, ignoring capabilities like
+ * CAP_SYS_ADMIN or CAP_DAC_OVERRIDE.
+ *
+ * Switching EUID clears the dumpable flag, which must be restored
+ * afterwards to allow coredumps.
+ */
+ set_cap(_metadata, CAP_SETUID);
+ ASSERT_EQ(0, seteuid(0));
+ clear_cap(_metadata, CAP_SETUID);
+
fd = open("/proc/sys/kernel/core_pattern", O_WRONLY | O_CLOEXEC);
- ASSERT_LE(0, fd);
+ ASSERT_LE(0, fd)
+ {
+ TH_LOG("Failed to open core_pattern for writing: %s",
+ strerror(errno));
+ }
ASSERT_EQ(len, write(fd, pattern, len));
EXPECT_EQ(0, close(fd));
+
+ set_cap(_metadata, CAP_SETUID);
+ ASSERT_EQ(0, seteuid(getuid()));
+ clear_cap(_metadata, CAP_SETUID);
+
+ /* Restore dumpable flag cleared by seteuid(). */
+ ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 1 , 0, 0, 0));
}
FIXTURE(coredump)
@@ -4680,9 +4703,7 @@ FIXTURE_SETUP(coredump)
FIXTURE_TEARDOWN_PARENT(coredump)
{
- set_cap(_metadata, CAP_SYS_ADMIN);
set_core_pattern(_metadata, self->original_core_pattern);
- clear_cap(_metadata, CAP_SYS_ADMIN);
}
/*
@@ -4705,9 +4726,7 @@ TEST_F_FORK(coredump, socket_not_restricted)
srv_fd = set_up_named_unix_server(_metadata, SOCK_STREAM, sock_path);
/* Point coredumps at our socket. */
- set_cap(_metadata, CAP_SYS_ADMIN);
set_core_pattern(_metadata, core_pattern);
- clear_cap(_metadata, CAP_SYS_ADMIN);
/* Restrict LANDLOCK_ACCESS_FS_RESOLVE_UNIX. */
enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, NULL);
Please run tests in this (minimal) environment.
> +
> +FIXTURE(coredump)
> +{
> + char original_core_pattern[256];
> +};
> +
> +FIXTURE_SETUP(coredump)
> +{
> + disable_caps(_metadata);
> + read_core_pattern(_metadata, self->original_core_pattern,
> + sizeof(self->original_core_pattern));
> +}
> +
> +FIXTURE_TEARDOWN_PARENT(coredump)
> +{
> + set_cap(_metadata, CAP_SYS_ADMIN);
> + set_core_pattern(_metadata, self->original_core_pattern);
> + clear_cap(_metadata, CAP_SYS_ADMIN);
> +}
> +
> +/*
> + * Test that even when a process is restricted with
> + * LANDLOCK_ACCESS_FS_RESOLVE_UNIX, the kernel can still initiate a connection
> + * to the coredump socket on the processes' behalf.
> + */
> +TEST_F_FORK(coredump, socket_not_restricted)
> +{
> + static const char core_pattern[] = "@/tmp/landlock_coredump_test.sock";
> + const char *const sock_path = core_pattern + 1;
> + int srv_fd, conn_fd, status;
> + pid_t child_pid;
> + struct ucred cred;
> + socklen_t cred_len = sizeof(cred);
> + char buf[4096];
> +
> + /* Set up the coredump server socket. */
> + unlink(sock_path);
> + srv_fd = set_up_named_unix_server(_metadata, SOCK_STREAM, sock_path);
> +
> + /* Point coredumps at our socket. */
> + set_cap(_metadata, CAP_SYS_ADMIN);
> + set_core_pattern(_metadata, core_pattern);
> + clear_cap(_metadata, CAP_SYS_ADMIN);
> +
> + /* Restrict LANDLOCK_ACCESS_FS_RESOLVE_UNIX. */
> + drop_access_rights(_metadata, &(struct landlock_ruleset_attr){
> + .handled_access_fs = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + });
> +
> + /* Fork a child that crashes. */
> + child_pid = fork();
> + ASSERT_LE(0, child_pid);
> + if (child_pid == 0) {
> + struct rlimit rl = {
> + .rlim_cur = RLIM_INFINITY,
> + .rlim_max = RLIM_INFINITY,
> + };
> +
> + ASSERT_EQ(0, setrlimit(RLIMIT_CORE, &rl));
> +
> + /* Crash on purpose. */
> + kill(getpid(), SIGSEGV);
> + _exit(1);
> + }
> +
> + /*
> + * Accept the coredump connection. If Landlock incorrectly denies the
> + * kernel's coredump connect, accept() will block forever, so the test
> + * would time out.
> + */
> + conn_fd = accept(srv_fd, NULL, NULL);
> + ASSERT_LE(0, conn_fd);
> +
> + /* Check that the connection came from the crashing child. */
> + ASSERT_EQ(0, getsockopt(conn_fd, SOL_SOCKET, SO_PEERCRED, &cred,
> + &cred_len));
> + EXPECT_EQ(child_pid, cred.pid);
> +
> + /* Drain the coredump data so the kernel can finish. */
> + while (read(conn_fd, buf, sizeof(buf)) > 0)
> + ;
> +
> + EXPECT_EQ(0, close(conn_fd));
> +
> + /* Wait for the child and verify it coredumped. */
> + ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
> + ASSERT_TRUE(WIFSIGNALED(status));
> + ASSERT_TRUE(WCOREDUMP(status));
> +
> + EXPECT_EQ(0, close(srv_fd));
> + EXPECT_EQ(0, unlink(sock_path));
> +}
> +
> /* clang-format off */
> FIXTURE(layout1_bind) {};
> /* clang-format on */
> --
> 2.52.0
>
>
^ permalink raw reply related
* [PATCH 0/2] landlock: Simplify path walk logic
From: Justin Suess @ 2026-02-18 20:18 UTC (permalink / raw)
To: linux-security-module, Mickaël Salaün
Cc: Günther Noack, Tingmao Wang, Justin Suess
Hello,
These two patches simplify the path walk logic in fs.c.
This patch was originally included in a very basic form in my
LANDLOCK_ADD_RULE_NO_INHERIT series [1], but I think that it would be better
submitted separately, as logically it doesn't have much to do with the
feature implemented in the patch.
This patch is based on the mic/next branch.
Motivation
===
Additionally, existing path walk logic is tightly bound to the
is_access_to_paths_allowed and collect_domain_accesses, and is difficult to
read and understand.
Centralizing the path logic would more easily allow other Landlock features
that may rely on path walking, such as the proposed path walk controls, or
my LANDLOCK_ADD_RULE_NO_INHERIT patch, to reuse the same logic as
currently implemented.
Background
===
The first patch in this small series introduces a helper function
landlock_walk_path_up, which takes a pointer to a struct path, and walks it
up through the VFS. The function returns an enum landlock_walk_result which
encodes whether the current path position is an internal mountpoint, the real
root, or neither.
The is_access_to_paths_allowed function is then altered to use this new helper,
cleaning up the traversal logic while retaining existing documentation comments
and improving readability.
The next patch in the series removes the collect_domain_accesses function. After
an initial re-implementation with the helper it was found that collect_domain_accesses
could be more succicently inlined into current_check_refer_path and there was little
benefit to keeping check_domain_accesses as a standalone function.
These changes overall reduce about 25 lines of code, including new documentation
for the return values of the landlock_walk_path_up function.
Results
===
These patches pass all existing selftests and kunit tests, and favorably influence
stack size.
Checkstack Results (CONFIG_AUDIT enabled)
===
Current Master Branch:
0xffffffff817d3f40 current_check_refer_path [vmlinux]: 608
0xffffffff817d2f80 is_access_to_paths_allowed [vmlinux]:352
This Patch Series:
0xffffffff817d3db0 current_check_refer_path [vmlinux]: 384
0xffffffff817d30c0 is_access_to_paths_allowed [vmlinux]:336
Thank you for your time.
Kind Regards,
Justin Suess
[1]: https://lore.kernel.org/linux-security-module/20251221194301.247484-2-utilityemal77@gmail.com/
Justin Suess (2):
landlock: Add path walk helper
landlock: Remove collect_domain_accesses
security/landlock/fs.c | 220 ++++++++++++++++++-----------------------
1 file changed, 98 insertions(+), 122 deletions(-)
--
2.51.0
^ permalink raw reply
* [PATCH 1/2] landlock: Add path walk helper
From: Justin Suess @ 2026-02-18 20:18 UTC (permalink / raw)
To: linux-security-module, Mickaël Salaün
Cc: Günther Noack, Tingmao Wang, Justin Suess
In-Reply-To: <20260218201857.1194667-1-utilityemal77@gmail.com>
Add a new helper function landlock_walk_path_up, which takes a pointer
to the current path in the walk, and returns an enum
landlock_walk_result corresponding to whether the current position in
the walk is a mountpoint, the real root, or neither.
Signed-off-by: Justin Suess <utilityemal77@gmail.com>
---
security/landlock/fs.c | 92 ++++++++++++++++++++++++------------------
1 file changed, 52 insertions(+), 40 deletions(-)
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index e764470f588c..c6ff686c9cde 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -317,6 +317,38 @@ static struct landlock_object *get_inode_object(struct inode *const inode)
LANDLOCK_ACCESS_FS_IOCTL_DEV)
/* clang-format on */
+/**
+ * enum landlock_walk_result - Result codes for landlock_walk_path_up()
+ * @LANDLOCK_WALK_CONTINUE: Path is now neither the real root nor an internal mount point.
+ * @LANDLOCK_WALK_STOP_REAL_ROOT: Path has reached the real VFS root.
+ * @LANDLOCK_WALK_INTERNAL: Path has reached an internal mount point.
+ */
+enum landlock_walk_result {
+ LANDLOCK_WALK_CONTINUE,
+ LANDLOCK_WALK_STOP_REAL_ROOT,
+ LANDLOCK_WALK_INTERNAL,
+};
+
+static enum landlock_walk_result landlock_walk_path_up(struct path *const path)
+{
+ struct dentry *old;
+
+ while (path->dentry == path->mnt->mnt_root) {
+ if (!follow_up(path))
+ return LANDLOCK_WALK_STOP_REAL_ROOT;
+ }
+ old = path->dentry;
+ if (unlikely(IS_ROOT(old))) {
+ if (likely(path->mnt->mnt_flags & MNT_INTERNAL))
+ return LANDLOCK_WALK_INTERNAL;
+ path->dentry = dget(path->mnt->mnt_root);
+ } else {
+ path->dentry = dget_parent(old);
+ }
+ dput(old);
+ return LANDLOCK_WALK_CONTINUE;
+}
+
/*
* @path: Should have been checked by get_path_from_fd().
*/
@@ -874,47 +906,27 @@ is_access_to_paths_allowed(const struct landlock_ruleset *const domain,
/* Stops when a rule from each layer grants access. */
if (allowed_parent1 && allowed_parent2)
break;
-
-jump_up:
- if (walker_path.dentry == walker_path.mnt->mnt_root) {
- if (follow_up(&walker_path)) {
- /* Ignores hidden mount points. */
- goto jump_up;
- } else {
- /*
- * Stops at the real root. Denies access
- * because not all layers have granted access.
- */
- break;
- }
- }
-
- if (unlikely(IS_ROOT(walker_path.dentry))) {
- if (likely(walker_path.mnt->mnt_flags & MNT_INTERNAL)) {
- /*
- * Stops and allows access when reaching disconnected root
- * directories that are part of internal filesystems (e.g. nsfs,
- * which is reachable through /proc/<pid>/ns/<namespace>).
- */
- allowed_parent1 = true;
- allowed_parent2 = true;
- break;
- }
-
- /*
- * We reached a disconnected root directory from a bind mount.
- * Let's continue the walk with the mount point we missed.
- */
- dput(walker_path.dentry);
- walker_path.dentry = walker_path.mnt->mnt_root;
- dget(walker_path.dentry);
- } else {
- struct dentry *const parent_dentry =
- dget_parent(walker_path.dentry);
-
- dput(walker_path.dentry);
- walker_path.dentry = parent_dentry;
+ /* Otherwise, keep walking up to the root. */
+ switch (landlock_walk_path_up(&walker_path)) {
+ /*
+ * Stops and allows access when reaching disconnected root
+ * directories that are part of internal filesystems (e.g. nsfs,
+ * which is reachable through /proc/<pid>/ns/<namespace>).
+ */
+ case LANDLOCK_WALK_INTERNAL:
+ allowed_parent1 = true;
+ allowed_parent2 = true;
+ break;
+ /*
+ * Stops at the real root. Denies access
+ * because not all layers have granted access
+ */
+ case LANDLOCK_WALK_STOP_REAL_ROOT:
+ break;
+ case LANDLOCK_WALK_CONTINUE:
+ continue;
}
+ break;
}
path_put(&walker_path);
--
2.51.0
^ permalink raw reply related
* [PATCH 2/2] landlock: Remove collect_domain_accesses
From: Justin Suess @ 2026-02-18 20:18 UTC (permalink / raw)
To: linux-security-module, Mickaël Salaün
Cc: Günther Noack, Tingmao Wang, Justin Suess
In-Reply-To: <20260218201857.1194667-1-utilityemal77@gmail.com>
Remove collect_domain_accesses and replace with inline logic using the
new path walk helper in the check_current_refer_path.
Signed-off-by: Justin Suess <utilityemal77@gmail.com>
---
security/landlock/fs.c | 128 +++++++++++++++--------------------------
1 file changed, 46 insertions(+), 82 deletions(-)
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index c6ff686c9cde..efc65dc41c0d 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -1013,77 +1013,6 @@ static access_mask_t maybe_remove(const struct dentry *const dentry)
LANDLOCK_ACCESS_FS_REMOVE_FILE;
}
-/**
- * collect_domain_accesses - Walk through a file path and collect accesses
- *
- * @domain: Domain to check against.
- * @mnt_root: Last directory to check.
- * @dir: Directory to start the walk from.
- * @layer_masks_dom: Where to store the collected accesses.
- *
- * This helper is useful to begin a path walk from the @dir directory to a
- * @mnt_root directory used as a mount point. This mount point is the common
- * ancestor between the source and the destination of a renamed and linked
- * file. While walking from @dir to @mnt_root, we record all the domain's
- * allowed accesses in @layer_masks_dom.
- *
- * Because of disconnected directories, this walk may not reach @mnt_dir. In
- * this case, the walk will continue to @mnt_dir after this call.
- *
- * This is similar to is_access_to_paths_allowed() but much simpler because it
- * only handles walking on the same mount point and only checks one set of
- * accesses.
- *
- * Returns:
- * - true if all the domain access rights are allowed for @dir;
- * - false if the walk reached @mnt_root.
- */
-static bool collect_domain_accesses(const struct landlock_ruleset *const domain,
- const struct dentry *const mnt_root,
- struct dentry *dir,
- struct layer_access_masks *layer_masks_dom)
-{
- bool ret = false;
-
- if (WARN_ON_ONCE(!domain || !mnt_root || !dir || !layer_masks_dom))
- return true;
- if (is_nouser_or_private(dir))
- return true;
-
- if (!landlock_init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
- layer_masks_dom, LANDLOCK_KEY_INODE))
- return true;
-
- dget(dir);
- while (true) {
- struct dentry *parent_dentry;
-
- /* Gets all layers allowing all domain accesses. */
- if (landlock_unmask_layers(find_rule(domain, dir),
- layer_masks_dom)) {
- /*
- * Stops when all handled accesses are allowed by at
- * least one rule in each layer.
- */
- ret = true;
- break;
- }
-
- /*
- * Stops at the mount point or the filesystem root for a disconnected
- * directory.
- */
- if (dir == mnt_root || unlikely(IS_ROOT(dir)))
- break;
-
- parent_dentry = dget_parent(dir);
- dput(dir);
- dir = parent_dentry;
- }
- dput(dir);
- return ret;
-}
-
/**
* current_check_refer_path - Check if a rename or link action is allowed
*
@@ -1147,7 +1076,7 @@ static int current_check_refer_path(struct dentry *const old_dentry,
bool allow_parent1, allow_parent2;
access_mask_t access_request_parent1, access_request_parent2;
struct path mnt_dir;
- struct dentry *old_parent;
+ struct path old_parent_path;
struct layer_access_masks layer_masks_parent1 = {},
layer_masks_parent2 = {};
struct landlock_request request1 = {}, request2 = {};
@@ -1202,20 +1131,55 @@ static int current_check_refer_path(struct dentry *const old_dentry,
/*
* old_dentry may be the root of the common mount point and
* !IS_ROOT(old_dentry) at the same time (e.g. with open_tree() and
- * OPEN_TREE_CLONE). We do not need to call dget(old_parent) because
+ * OPEN_TREE_CLONE). We do not need to path_get(old_parent_path) because
* we keep a reference to old_dentry.
*/
- old_parent = (old_dentry == mnt_dir.dentry) ? old_dentry :
- old_dentry->d_parent;
+ old_parent_path.mnt = mnt_dir.mnt;
+ old_parent_path.dentry = unlikely(old_dentry == mnt_dir.dentry) ?
+ old_dentry :
+ old_dentry->d_parent;
/* new_dir->dentry is equal to new_dentry->d_parent */
- allow_parent1 = collect_domain_accesses(subject->domain, mnt_dir.dentry,
- old_parent,
- &layer_masks_parent1);
- allow_parent2 = collect_domain_accesses(subject->domain, mnt_dir.dentry,
- new_dir->dentry,
- &layer_masks_parent2);
+ allow_parent1 = false;
+ allow_parent2 = false;
+ for (size_t i = 0; i < 2; i++) {
+ const struct path *const parent_path = i ? new_dir :
+ &old_parent_path;
+ struct layer_access_masks *const layer_masks =
+ i ? &layer_masks_parent2 : &layer_masks_parent1;
+ bool *const allow_parent = i ? &allow_parent2 : &allow_parent1;
+
+ if (is_nouser_or_private(parent_path->dentry) ||
+ !landlock_init_layer_masks(
+ subject->domain, LANDLOCK_MASK_ACCESS_FS,
+ layer_masks, LANDLOCK_KEY_INODE)) {
+ *allow_parent = true;
+ continue;
+ }
+ {
+ struct path walker = *parent_path;
+
+ path_get(&walker);
+ do {
+ /* Gets all layers allowing all domain accesses. */
+ if (landlock_unmask_layers(
+ find_rule(subject->domain,
+ walker.dentry),
+ layer_masks)) {
+ /*
+ * Stops when all handled accesses are
+ * allowed by at least one rule in each
+ * layer.
+ */
+ *allow_parent = true;
+ break;
+ }
+ } while (landlock_walk_path_up(&walker) ==
+ LANDLOCK_WALK_CONTINUE);
+ path_put(&walker);
+ }
+ }
if (allow_parent1 && allow_parent2)
return 0;
@@ -1233,7 +1197,7 @@ static int current_check_refer_path(struct dentry *const old_dentry,
return 0;
if (request1.access) {
- request1.audit.u.path.dentry = old_parent;
+ request1.audit.u.path.dentry = old_parent_path.dentry;
landlock_log_denial(subject, &request1);
}
if (request2.access) {
--
2.51.0
^ permalink raw reply related
* [GIT PULL] AppArmor updates for 7.0-rc1
From: John Johansen @ 2026-02-19 0:19 UTC (permalink / raw)
To: Linus Torvalds; +Cc: LKLM, open list:SECURITY SUBSYSTEM
Hi Linus,
Below is the AppArmor update PR for 7.0
These patches have all been merge, build, and regression tested
against your tree as of yesterday. The code has been in linux-next
and the many of the patches in the Ubuntu kernels for testing.
This PR is mostly comprised of cleanups, and bug fixes, with 3 minor
features, the first being an improvement to our kunit testing, and
the other two extending the information available in audit messages.
Because this is coming so late in the window (sorry life happens),
if you would prefer I have prepared an alternate PR that contains
the set of bug fixes that apply without the features, or cleanups,
available via the tag bugfix-2026-02-18, which I can send a PR for
instead.
thanks
- john
The following changes since commit 8f0b4cce4481fb22653697cced8d0d04027cb1e8:
Linux 6.19-rc1 (2025-12-14 16:05:07 +1200)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor tags/apparmor-pr-2026-02-18
for you to fetch changes up to 08020dbe3125e936429e7966bf072e08fa964f36:
apparmor: fix signedness bug in unpack_tags() (2026-02-18 11:50:20 -0800)
----------------------------------------------------------------
+ Features
- add .kunitconfig
- audit execpath in userns mediation
- add support loading per permission tagging
+ Cleanups
- cleanup remove unused percpu critical sections in buffer management
- document the buffer hold, add an overflow guard
- split xxx_in_ns into its two separate semantic use cases
- remove apply_modes_to_perms from label_match
- refactor/cleanup cred helper fns.
- guard against free attachment/data routines being called with a NULL
- drop in_atomic flag in common_mmap, and common_file_perm, and cleanup
- make str table more generic and be able to have multiple entries
- Replace deprecated strcpy with memcpy in gen_symlink_name
- Replace deprecated strcpy in d_namespace_path
- Replace sprintf/strcpy with scnprintf/strscpy in aa_policy_init
- replace sprintf with snprintf in aa_new_learning_profile
+ Bug Fixes
- fix cast in format string DEBUG statement
- fix make aa_labelmatch return consistent
- fix fmt string type error in process_strs_entry
- fix kernel-doc comments for inview
- fix invalid deref of rawdata when export_binary is unset
- avoid per-cpu hold underflow in aa_get_buffer
- fix fast path cache check for unix sockets
- fix rlimit for posix cpu timers
- fix label and profile debug macros
- move check for aa_null file to cover all cases
- return -ENOMEM in unpack_perms_table upon alloc failure
- fix boolean argument in apparmor_mmap_file
- Fix & Optimize table creation from possibly unaligned memory
- Allow apparmor to handle unaligned dfa tables
- fix NULL deref in aa_sock_file_perm
- fix NULL pointer dereference in __unix_needs_revalidation
- fix signedness bug in unpack_tags()
----------------------------------------------------------------
Georgia Garcia (1):
apparmor: fix invalid deref of rawdata when export_binary is unset
Helge Deller (2):
AppArmor: Allow apparmor to handle unaligned dfa tables
apparmor: Fix & Optimize table creation from possibly unaligned memory
John Johansen (19):
apparmor: fix NULL sock in aa_sock_file_perm
apparmor: make str table more generic and be able to have multiple entries
apparmor: add support loading per permission tagging
apparmor: drop in_atomic flag in common_mmap, and common_file_perm
apparmor: guard against free routines being called with a NULL
apparmor: move check for aa_null file to cover all cases
apparmor: fix label and profile debug macros
apparmor: refactor/cleanup cred helper fns.
apparmor: fix rlimit for posix cpu timers
apparmor: fix fast path cache check for unix sockets
apparmor: remove apply_modes_to_perms from label_match
apparmor: make label_match return a consistent value
apparmor: split xxx_in_ns into its two separate semantic use cases
apparmor: document the buffer hold, add an overflow guard
apparmor: cleanup remove unused percpu critical sections in buffer management
apparmor: fix kernel-doc comments for inview
apparmor: fix fmt string type error in process_strs_entry
apparmor: fix aa_label to return state from compount and component match
apparmor: fix cast in format string DEBUG statement
Massimiliano Pellizzer (1):
apparmor: fix signedness bug in unpack_tags()
Maxime Bélair (1):
apparmor: userns: Add support for execpath in userns
Ryan Lee (3):
apparmor: fix boolean argument in apparmor_mmap_file
apparmor: account for in_atomic removal in common_file_perm
apparmor: return -ENOMEM in unpack_perms_table upon alloc failure
Ryota Sakamoto (1):
apparmor: add .kunitconfig
System Administrator (1):
apparmor: fix NULL pointer dereference in __unix_needs_revalidation
Thorsten Blum (4):
apparmor: replace sprintf with snprintf in aa_new_learning_profile
apparmor: Replace sprintf/strcpy with scnprintf/strscpy in aa_policy_init
apparmor: Replace deprecated strcpy in d_namespace_path
apparmor: Replace deprecated strcpy with memcpy in gen_symlink_name
Zhengmian Hu (1):
apparmor: avoid per-cpu hold underflow in aa_get_buffer
security/apparmor/.kunitconfig | 5 +
security/apparmor/af_unix.c | 2 +-
security/apparmor/apparmorfs.c | 23 ++-
security/apparmor/domain.c | 60 +++----
security/apparmor/file.c | 49 ++++--
security/apparmor/include/audit.h | 2 +
security/apparmor/include/cred.h | 100 +++++++----
security/apparmor/include/lib.h | 37 +++-
security/apparmor/include/match.h | 12 +-
security/apparmor/include/policy.h | 32 +++-
security/apparmor/label.c | 55 +++---
security/apparmor/lib.c | 29 ++--
security/apparmor/lsm.c | 66 +++++---
security/apparmor/match.c | 22 +--
security/apparmor/net.c | 6 +-
security/apparmor/path.c | 13 +-
security/apparmor/policy.c | 31 +++-
security/apparmor/policy_compat.c | 10 +-
security/apparmor/policy_unpack.c | 336 ++++++++++++++++++++++++++++++++-----
security/apparmor/resource.c | 5 +
security/apparmor/task.c | 32 ++++
21 files changed, 687 insertions(+), 240 deletions(-)
create mode 100644 security/apparmor/.kunitconfig
^ permalink raw reply
* Re: [PATCH] ima: check return value of crypto_shash_final() in boot aggregate
From: Roberto Sassu @ 2026-02-19 8:56 UTC (permalink / raw)
To: Daniel Hodges, Mimi Zohar, Roberto Sassu, Dmitry Kasatkin
Cc: Eric Snowberg, Paul Moore, James Morris, Serge E . Hallyn,
linux-integrity, linux-security-module, linux-kernel
In-Reply-To: <20260201024015.2862236-1-hodgesd@meta.com>
On Sat, 2026-01-31 at 18:40 -0800, Daniel Hodges wrote:
> The return value of crypto_shash_final() is not checked in
> ima_calc_boot_aggregate_tfm(). If the hash finalization fails, the
> function returns success and a corrupted boot aggregate digest could
> be used for IMA measurements.
>
> Capture the return value and propagate any error to the caller.
>
> Fixes: 76bb28f6126f ("ima: use new crypto_shash API instead of old crypto_hash")
> Signed-off-by: Daniel Hodges <hodgesd@meta.com>
Reviewed-by: Roberto Sassu <roberto.sassu@huawei.com>
Thanks
Roberto
> ---
> security/integrity/ima/ima_crypto.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
> index 6f5696d999d0..8ae7821a65c2 100644
> --- a/security/integrity/ima/ima_crypto.c
> +++ b/security/integrity/ima/ima_crypto.c
> @@ -825,21 +825,21 @@ static int ima_calc_boot_aggregate_tfm(char *digest, u16 alg_id,
> * non-SHA1 boot_aggregate digests to avoid ambiguity.
> */
> if (alg_id != TPM_ALG_SHA1) {
> for (i = TPM_PCR8; i < TPM_PCR10; i++) {
> ima_pcrread(i, &d);
> rc = crypto_shash_update(shash, d.digest,
> crypto_shash_digestsize(tfm));
> }
> }
> if (!rc)
> - crypto_shash_final(shash, digest);
> + rc = crypto_shash_final(shash, digest);
> return rc;
> }
>
> int ima_calc_boot_aggregate(struct ima_digest_data *hash)
> {
> struct crypto_shash *tfm;
> u16 crypto_id, alg_id;
> int rc, i, bank_idx = -1;
>
> for (i = 0; i < ima_tpm_chip->nr_allocated_banks; i++) {
^ permalink raw reply
* Re: [PATCH v4] ima_fs: Avoid creating measurement lists for unsupported hash algos
From: Roberto Sassu @ 2026-02-19 8:54 UTC (permalink / raw)
To: dima, Mimi Zohar, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
Paul Moore, James Morris, Serge E. Hallyn, Silvia Sisinni,
Enrico Bravi
Cc: linux-integrity, linux-security-module, linux-kernel, stable,
Dmitry Safonov
In-Reply-To: <701de3f87f0f6bde97872dd0c5bf150bfc1f2713.camel@huaweicloud.com>
On Tue, 2026-01-27 at 16:20 +0100, Roberto Sassu wrote:
> On Tue, 2026-01-27 at 15:03 +0000, Dmitry Safonov via B4 Relay wrote:
> > From: Dmitry Safonov <dima@arista.com>
> >
> > ima_init_crypto() skips initializing ima_algo_array[i] if the algorithm
> > from ima_tpm_chip->allocated_banks[i].crypto_id is not supported.
> > It seems avoid adding the unsupported algorithm to ima_algo_array will
> > break all the logic that relies on indexing by NR_BANKS(ima_tpm_chip).
>
> The patch looks good, although I didn't try yet myself.
>
> I would make the commit message slightly better, with a more fluid
> explanation.
>
> ima_tpm_chip->allocated_banks[i].crypto_id is initialized to
> HASH_ALGO__LAST if the TPM algorithm is not supported. However there
> are places relying on the algorithm to be valid because it is accessed
> by hash_algo_name[].
>
> Thus solve the problem by creating a file name that does not depend on
> the crypto algorithm to be initialized, ...
>
> Also print the template entry digest as populated by IMA.
>
> Something along these lines.
>
> Also, I have a preference for lower case instead of capital case for
> the file name, given the other names.
Hi Dmitry
do you have time to make these small changes, so that we queue the
patch for the next kernel?
Thanks
Roberto
> Could you also avoid the >, otherwise the mailer thinks it is a reply?
>
> Thanks
>
> Roberto
>
> > On 6.12.40 I observe the following read out-of-bounds in hash_algo_name:
> >
> > > ==================================================================
> > > BUG: KASAN: global-out-of-bounds in create_securityfs_measurement_lists+0x396/0x440
> > > Read of size 8 at addr ffffffff83e18138 by task swapper/0/1
> > >
> > > CPU: 4 UID: 0 PID: 1 Comm: swapper/0 Not tainted 6.12.40 #3
> > > Call Trace:
> > > <TASK>
> > > dump_stack_lvl+0x61/0x90
> > > print_report+0xc4/0x580
> > > ? kasan_addr_to_slab+0x26/0x80
> > > ? create_securityfs_measurement_lists+0x396/0x440
> > > kasan_report+0xc2/0x100
> > > ? create_securityfs_measurement_lists+0x396/0x440
> > > create_securityfs_measurement_lists+0x396/0x440
> > > ima_fs_init+0xa3/0x300
> > > ima_init+0x7d/0xd0
> > > init_ima+0x28/0x100
> > > do_one_initcall+0xa6/0x3e0
> > > kernel_init_freeable+0x455/0x740
> > > kernel_init+0x24/0x1d0
> > > ret_from_fork+0x38/0x80
> > > ret_from_fork_asm+0x11/0x20
> > > </TASK>
> > >
> > > The buggy address belongs to the variable:
> > > hash_algo_name+0xb8/0x420
> > >
> > > The buggy address belongs to the physical page:
> > > page: refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x107ce18
> > > flags: 0x8000000000002000(reserved|zone=2)
> > > raw: 8000000000002000 ffffea0041f38608 ffffea0041f38608 0000000000000000
> > > raw: 0000000000000000 0000000000000000 00000001ffffffff 0000000000000000
> > > page dumped because: kasan: bad access detected
> > >
> > > Memory state around the buggy address:
> > > ffffffff83e18000: 00 01 f9 f9 f9 f9 f9 f9 00 01 f9 f9 f9 f9 f9 f9
> > > ffffffff83e18080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> > > > ffffffff83e18100: 00 00 00 00 00 00 00 f9 f9 f9 f9 f9 00 05 f9 f9
> > > ^
> > > ffffffff83e18180: f9 f9 f9 f9 00 00 00 00 00 00 00 04 f9 f9 f9 f9
> > > ffffffff83e18200: 00 00 00 00 00 00 00 00 04 f9 f9 f9 f9 f9 f9 f9
> > > ==================================================================
> >
> > Seems like the TPM chip supports sha3_256, which isn't yet in
> > tpm_algorithms:
> > > tpm tpm0: TPM with unsupported bank algorithm 0x0027
> >
> > Use TPM_ALG_<ID> as a postfix for file names for unsupported hashing algorithms.
> >
> > This is how it looks on the test machine I have:
> > > # ls -1 /sys/kernel/security/ima/
> > > ascii_runtime_measurements
> > > ascii_runtime_measurements_TPM_ALG_27
> > > ascii_runtime_measurements_sha1
> > > ascii_runtime_measurements_sha256
> > > binary_runtime_measurements
> > > binary_runtime_measurements_TPM_ALG_27
> > > binary_runtime_measurements_sha1
> > > binary_runtime_measurements_sha256
> > > policy
> > > runtime_measurements_count
> > > violations
> >
> > Fixes: 9fa8e7625008 ("ima: add crypto agility support for template-hash algorithm")
> > Signed-off-by: Dmitry Safonov <dima@arista.com>
> > Cc: Enrico Bravi <enrico.bravi@polito.it>
> > Cc: Silvia Sisinni <silvia.sisinni@polito.it>
> > Cc: Roberto Sassu <roberto.sassu@huawei.com>
> > Cc: Mimi Zohar <zohar@linux.ibm.com>
> > ---
> > Changes in v4:
> > - Use ima_tpm_chip->allocated_banks[algo_idx].digest_size instead of hash_digest_size[algo]
> > (Roberto Sassu)
> > - Link to v3: https://lore.kernel.org/r/20260127-ima-oob-v3-1-1dd09f4c2a6a@arista.com
> > Testing note: I test it on v6.12.40 kernel backport, which slightly differs as
> > lookup_template_data_hash_algo() was yet present.
> >
> > Changes in v3:
> > - Now fix the spelling *for real* (sorry, messed it up in v2)
> > - Link to v2: https://lore.kernel.org/r/20260127-ima-oob-v2-1-f38a18c850cf@arista.com
> >
> > Changes in v2:
> > - Instead of skipping unknown algorithms, add files under their TPM_ALG_ID (Roberto Sassu)
> > - Fix spelling (Roberto Sassu)
> > - Copy @stable on the fix
> > - Link to v1: https://lore.kernel.org/r/20260127-ima-oob-v1-1-2d42f3418e57@arista.com
> > ---
> > security/integrity/ima/ima_fs.c | 34 ++++++++++++++++++----------------
> > 1 file changed, 18 insertions(+), 16 deletions(-)
> >
> > diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> > index 012a58959ff0..9a00a0547619 100644
> > --- a/security/integrity/ima/ima_fs.c
> > +++ b/security/integrity/ima/ima_fs.c
> > @@ -132,16 +132,12 @@ int ima_measurements_show(struct seq_file *m, void *v)
> > char *template_name;
> > u32 pcr, namelen, template_data_len; /* temporary fields */
> > bool is_ima_template = false;
> > - enum hash_algo algo;
> > int i, algo_idx;
> >
> > algo_idx = ima_sha1_idx;
> > - algo = HASH_ALGO_SHA1;
> >
> > - if (m->file != NULL) {
> > + if (m->file != NULL)
> > algo_idx = (unsigned long)file_inode(m->file)->i_private;
> > - algo = ima_algo_array[algo_idx].algo;
> > - }
> >
> > /* get entry */
> > e = qe->entry;
> > @@ -160,7 +156,8 @@ int ima_measurements_show(struct seq_file *m, void *v)
> > ima_putc(m, &pcr, sizeof(e->pcr));
> >
> > /* 2nd: template digest */
> > - ima_putc(m, e->digests[algo_idx].digest, hash_digest_size[algo]);
> > + ima_putc(m, e->digests[algo_idx].digest,
> > + ima_tpm_chip->allocated_banks[algo_idx].digest_size);
> >
> > /* 3rd: template name size */
> > namelen = !ima_canonical_fmt ? strlen(template_name) :
> > @@ -229,16 +226,12 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
> > struct ima_queue_entry *qe = v;
> > struct ima_template_entry *e;
> > char *template_name;
> > - enum hash_algo algo;
> > int i, algo_idx;
> >
> > algo_idx = ima_sha1_idx;
> > - algo = HASH_ALGO_SHA1;
> >
> > - if (m->file != NULL) {
> > + if (m->file != NULL)
> > algo_idx = (unsigned long)file_inode(m->file)->i_private;
> > - algo = ima_algo_array[algo_idx].algo;
> > - }
> >
> > /* get entry */
> > e = qe->entry;
> > @@ -252,7 +245,8 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
> > seq_printf(m, "%2d ", e->pcr);
> >
> > /* 2nd: template hash */
> > - ima_print_digest(m, e->digests[algo_idx].digest, hash_digest_size[algo]);
> > + ima_print_digest(m, e->digests[algo_idx].digest,
> > + ima_tpm_chip->allocated_banks[algo_idx].digest_size);
> >
> > /* 3th: template name */
> > seq_printf(m, " %s", template_name);
> > @@ -404,16 +398,24 @@ static int __init create_securityfs_measurement_lists(void)
> > char file_name[NAME_MAX + 1];
> > struct dentry *dentry;
> >
> > - sprintf(file_name, "ascii_runtime_measurements_%s",
> > - hash_algo_name[algo]);
> > + if (algo == HASH_ALGO__LAST)
> > + sprintf(file_name, "ascii_runtime_measurements_TPM_ALG_%x",
> > + ima_tpm_chip->allocated_banks[i].alg_id);
> > + else
> > + sprintf(file_name, "ascii_runtime_measurements_%s",
> > + hash_algo_name[algo]);
> > dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP,
> > ima_dir, (void *)(uintptr_t)i,
> > &ima_ascii_measurements_ops);
> > if (IS_ERR(dentry))
> > return PTR_ERR(dentry);
> >
> > - sprintf(file_name, "binary_runtime_measurements_%s",
> > - hash_algo_name[algo]);
> > + if (algo == HASH_ALGO__LAST)
> > + sprintf(file_name, "binary_runtime_measurements_TPM_ALG_%x",
> > + ima_tpm_chip->allocated_banks[i].alg_id);
> > + else
> > + sprintf(file_name, "binary_runtime_measurements_%s",
> > + hash_algo_name[algo]);
> > dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP,
> > ima_dir, (void *)(uintptr_t)i,
> > &ima_measurements_ops);
> >
> > ---
> > base-commit: 63804fed149a6750ffd28610c5c1c98cce6bd377
> > change-id: 20260127-ima-oob-9fa83a634d7b
> >
> > Best regards,
^ permalink raw reply
* Re: [PATCH v2 v2] evm: check return values of crypto_shash functions
From: Roberto Sassu @ 2026-02-19 9:26 UTC (permalink / raw)
To: Daniel Hodges
Cc: zohar, roberto.sassu, dmitry.kasatkin, eric.snowberg, paul,
jmorris, serge, linux-integrity, linux-security-module,
linux-kernel
In-Reply-To: <20260206024240.19059-1-git@danielhodges.dev>
On Thu, 2026-02-05 at 21:42 -0500, Daniel Hodges wrote:
> The crypto_shash_update() and crypto_shash_final() functions can fail
> and return error codes, but their return values were not being checked
> in several places in security/integrity/evm/evm_crypto.c:
>
> - hmac_add_misc() ignored returns from crypto_shash_update() and
> crypto_shash_final()
> - evm_calc_hmac_or_hash() ignored returns from crypto_shash_update()
> - evm_init_hmac() ignored returns from crypto_shash_update()
>
> If these hash operations fail silently, the resulting HMAC could be
> invalid or incomplete, which could weaken the integrity verification
> security that EVM provides.
>
> This patch converts hmac_add_misc() from void to int return type and
> adds proper error checking and propagation for all crypto_shash_*
> function calls. All callers are updated to handle the new return values.
> Additionally, error messages are logged when cryptographic operations
> fail to provide visibility into the failure rather than silently
> returning error codes.
>
> Fixes: 66dbc325afce ("evm: re-release")
> Signed-off-by: Daniel Hodges <git@danielhodges.dev>
After fixing the minor issue below:
Reviewed-by: Roberto Sassu <roberto.sassu@huawei.com>
> ---
> security/integrity/evm/evm_crypto.c | 55 ++++++++++++++++++++++-------
> 1 file changed, 42 insertions(+), 13 deletions(-)
>
> diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
> index a5e730ffda57..402eb1ca64ce 100644
> --- a/security/integrity/evm/evm_crypto.c
> +++ b/security/integrity/evm/evm_crypto.c
> @@ -139,7 +139,7 @@ static struct shash_desc *init_desc(char type, uint8_t hash_algo)
> * (Additional directory/file metadata needs to be added for more complete
> * protection.)
> */
> -static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
> +static int hmac_add_misc(struct shash_desc *desc, struct inode *inode,
> char type, char *digest)
> {
> struct h_misc {
> @@ -149,6 +149,7 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
> gid_t gid;
> umode_t mode;
> } hmac_misc;
> + int error;
>
> memset(&hmac_misc, 0, sizeof(hmac_misc));
> /* Don't include the inode or generation number in portable
> @@ -169,14 +170,28 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
> hmac_misc.uid = from_kuid(&init_user_ns, inode->i_uid);
> hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid);
> hmac_misc.mode = inode->i_mode;
> - crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc));
> + error = crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc));
> + if (error) {
> + pr_err("crypto_shash_update() failed: %d\n", error);
> + return error;
> + }
> if ((evm_hmac_attrs & EVM_ATTR_FSUUID) &&
> - type != EVM_XATTR_PORTABLE_DIGSIG)
> - crypto_shash_update(desc, (u8 *)&inode->i_sb->s_uuid, UUID_SIZE);
> - crypto_shash_final(desc, digest);
> + type != EVM_XATTR_PORTABLE_DIGSIG) {
> + error = crypto_shash_update(desc, (u8 *)&inode->i_sb->s_uuid, UUID_SIZE);
> + if (error) {
> + pr_err("crypto_shash_update() failed: %d\n", error);
> + return error;
> + }
> + }
> + error = crypto_shash_final(desc, digest);
> + if (error) {
> + pr_err("crypto_shash_final() failed: %d\n", error);
> + return error;
> + }
>
> pr_debug("hmac_misc: (%zu) [%*phN]\n", sizeof(struct h_misc),
> (int)sizeof(struct h_misc), &hmac_misc);
> + return 0;
> }
>
> /*
> @@ -260,9 +275,12 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
>
> if ((req_xattr_name && req_xattr_value)
> && !strcmp(xattr->name, req_xattr_name)) {
> - error = 0;
> - crypto_shash_update(desc, (const u8 *)req_xattr_value,
> + error = crypto_shash_update(desc, (const u8 *)req_xattr_value,
> req_xattr_value_len);
Please align this.
Thanks
Roberto
> + if (error) {
> + pr_err("crypto_shash_update() failed: %d\n", error);
> + goto out;
> + }
> if (is_ima)
> ima_present = true;
>
> @@ -286,15 +304,20 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
> pr_debug("file %s: xattr %s size mismatch (kernel: %d, user: %d)\n",
> dentry->d_name.name, xattr->name, size,
> user_space_size);
> - error = 0;
> xattr_size = size;
> - crypto_shash_update(desc, (const u8 *)xattr_value, xattr_size);
> + error = crypto_shash_update(desc, (const u8 *)xattr_value, xattr_size);
> + if (error) {
> + pr_err("crypto_shash_update() failed: %d\n", error);
> + goto out;
> + }
> if (is_ima)
> ima_present = true;
>
> dump_security_xattr(xattr->name, xattr_value, xattr_size);
> }
> - hmac_add_misc(desc, inode, type, data->digest);
> + error = hmac_add_misc(desc, inode, type, data->digest);
> + if (error)
> + goto out;
>
> if (inode != d_backing_inode(dentry) && iint) {
> if (IS_I_VERSION(inode))
> @@ -401,6 +424,7 @@ int evm_init_hmac(struct inode *inode, const struct xattr *xattrs,
> {
> struct shash_desc *desc;
> const struct xattr *xattr;
> + int error;
>
> desc = init_desc(EVM_XATTR_HMAC, HASH_ALGO_SHA1);
> if (IS_ERR(desc)) {
> @@ -412,12 +436,17 @@ int evm_init_hmac(struct inode *inode, const struct xattr *xattrs,
> if (!evm_protected_xattr(xattr->name))
> continue;
>
> - crypto_shash_update(desc, xattr->value, xattr->value_len);
> + error = crypto_shash_update(desc, xattr->value, xattr->value_len);
> + if (error) {
> + pr_err("crypto_shash_update() failed: %d\n", error);
> + goto out;
> + }
> }
>
> - hmac_add_misc(desc, inode, EVM_XATTR_HMAC, hmac_val);
> + error = hmac_add_misc(desc, inode, EVM_XATTR_HMAC, hmac_val);
> +out:
> kfree(desc);
> - return 0;
> + return error;
> }
>
> /*
^ permalink raw reply
* Re: [PATCH v5 2/9] landlock: Control pathname UNIX domain socket resolution by path
From: Mickaël Salaün @ 2026-02-19 9:45 UTC (permalink / raw)
To: Günther Noack
Cc: John Johansen, Tingmao Wang, Justin Suess, Jann Horn,
linux-security-module, Samasth Norway Ananda, Matthieu Buffet,
Mikhail Ivanov, konstantin.meskhidze, Demi Marie Obenour,
Alyssa Ross, Tahera Fahimi
In-Reply-To: <20260217.lievaS8eeng8@digikod.net>
On Wed, Feb 18, 2026 at 10:37:16AM +0100, Mickaël Salaün wrote:
> On Sun, Feb 15, 2026 at 11:51:50AM +0100, Günther Noack wrote:
> > * Add a new access right LANDLOCK_ACCESS_FS_RESOLVE_UNIX, which
> > controls the look up operations for named UNIX domain sockets. The
> > resolution happens during connect() and sendmsg() (depending on
> > socket type).
> > * Hook into the path lookup in unix_find_bsd() in af_unix.c, using a
> > LSM hook. Make policy decisions based on the new access rights
> > * Increment the Landlock ABI version.
> > * Minor test adaptions to keep the tests working.
> >
> > With this access right, access is granted if either of the following
> > conditions is met:
> >
> > * The target socket's filesystem path was allow-listed using a
> > LANDLOCK_RULE_PATH_BENEATH rule, *or*:
> > * The target socket was created in the same Landlock domain in which
> > LANDLOCK_ACCESS_FS_RESOLVE_UNIX was restricted.
> >
> > In case of a denial, connect() and sendmsg() return EACCES, which is
> > the same error as it is returned if the user does not have the write
> > bit in the traditional Unix file system permissions of that file.
> >
> > This feature was created with substantial discussion and input from
> > Justin Suess, Tingmao Wang and Mickaël Salaün.
> >
> > Cc: Tingmao Wang <m@maowtm.org>
> > Cc: Justin Suess <utilityemal77@gmail.com>
> > Cc: Mickaël Salaün <mic@digikod.net>
> > Suggested-by: Jann Horn <jannh@google.com>
> > Link: https://github.com/landlock-lsm/linux/issues/36
> > Signed-off-by: Günther Noack <gnoack3000@gmail.com>
> > ---
> > include/uapi/linux/landlock.h | 10 ++
> > security/landlock/access.h | 11 +-
> > security/landlock/audit.c | 1 +
> > security/landlock/fs.c | 102 ++++++++++++++++++-
> > security/landlock/limits.h | 2 +-
> > security/landlock/syscalls.c | 2 +-
> > tools/testing/selftests/landlock/base_test.c | 2 +-
> > tools/testing/selftests/landlock/fs_test.c | 5 +-
> > 8 files changed, 128 insertions(+), 7 deletions(-)
> > index 60ff217ab95b..8d0edf94037d 100644
> > --- a/security/landlock/audit.c
> > +++ b/security/landlock/audit.c
> > @@ -37,6 +37,7 @@ static const char *const fs_access_strings[] = {
> > [BIT_INDEX(LANDLOCK_ACCESS_FS_REFER)] = "fs.refer",
> > [BIT_INDEX(LANDLOCK_ACCESS_FS_TRUNCATE)] = "fs.truncate",
> > [BIT_INDEX(LANDLOCK_ACCESS_FS_IOCTL_DEV)] = "fs.ioctl_dev",
> > + [BIT_INDEX(LANDLOCK_ACCESS_FS_RESOLVE_UNIX)] = "fs.resolve_unix",
> > };
> >
> > static_assert(ARRAY_SIZE(fs_access_strings) == LANDLOCK_NUM_ACCESS_FS);
> > diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> > index e764470f588c..76035c6f2bf1 100644
> > --- a/security/landlock/fs.c
> > +++ b/security/landlock/fs.c
> > @@ -27,6 +27,7 @@
> > #include <linux/lsm_hooks.h>
> > #include <linux/mount.h>
> > #include <linux/namei.h>
> > +#include <linux/net.h>
> > #include <linux/path.h>
> > #include <linux/pid.h>
> > #include <linux/rcupdate.h>
> > @@ -314,7 +315,8 @@ static struct landlock_object *get_inode_object(struct inode *const inode)
> > LANDLOCK_ACCESS_FS_WRITE_FILE | \
> > LANDLOCK_ACCESS_FS_READ_FILE | \
> > LANDLOCK_ACCESS_FS_TRUNCATE | \
> > - LANDLOCK_ACCESS_FS_IOCTL_DEV)
> > + LANDLOCK_ACCESS_FS_IOCTL_DEV | \
> > + LANDLOCK_ACCESS_FS_RESOLVE_UNIX)
> > /* clang-format on */
> >
> > /*
> > @@ -1561,6 +1563,103 @@ static int hook_path_truncate(const struct path *const path)
> > return current_check_access_path(path, LANDLOCK_ACCESS_FS_TRUNCATE);
> > }
> >
> > +/**
> > + * unmask_scoped_access - Remove access right bits in @masks in all layers
> > + * where @client and @server have the same domain
> > + *
> > + * This does the same as domain_is_scoped(), but unmasks bits in @masks.
> > + * It can not return early as domain_is_scoped() does.
Why can't we use the same logic as for other scopes?
> > + *
> > + * @client: Client domain
> > + * @server: Server domain
> > + * @masks: Layer access masks to unmask
> > + * @access: Access bit that controls scoping
> > + */
> > +static void unmask_scoped_access(const struct landlock_ruleset *const client,
> > + const struct landlock_ruleset *const server,
> > + struct layer_access_masks *const masks,
> > + const access_mask_t access)
>
> This helper should be moved to task.c and factored out with
> domain_is_scoped(). This should be a dedicated patch.
Well, if domain_is_scoped() can be refactored and made generic, it would
make more sense to move it to domain.c
>
> > +{
> > + int client_layer, server_layer;
> > + const struct landlock_hierarchy *client_walker, *server_walker;
> > +
> > + if (WARN_ON_ONCE(!client))
> > + return; /* should not happen */
> > +
> > + if (!server)
> > + return; /* server has no Landlock domain; nothing to clear */
> > +
> > + client_layer = client->num_layers - 1;
> > + client_walker = client->hierarchy;
> > + server_layer = server->num_layers - 1;
> > + server_walker = server->hierarchy;
> > +
> > + /*
> > + * Clears the access bits at all layers where the client domain is the
> > + * same as the server domain. We start the walk at min(client_layer,
> > + * server_layer). The layer bits until there can not be cleared because
> > + * either the client or the server domain is missing.
> > + */
> > + for (; client_layer > server_layer; client_layer--)
> > + client_walker = client_walker->parent;
> > +
> > + for (; server_layer > client_layer; server_layer--)
> > + server_walker = server_walker->parent;
> > +
> > + for (; client_layer >= 0; client_layer--) {
> > + if (masks->access[client_layer] & access &&
> > + client_walker == server_walker)
> > + masks->access[client_layer] &= ~access;
> > +
> > + client_walker = client_walker->parent;
> > + server_walker = server_walker->parent;
> > + }
> > +}
^ permalink raw reply
* [PATCH] task: delete task_euid()
From: Alice Ryhl @ 2026-02-19 12:14 UTC (permalink / raw)
To: Paul Moore, Serge Hallyn, Jonathan Corbet, Greg Kroah-Hartman,
Shuah Khan, Alex Shi, Yanteng Si, Dongliang Mu
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich,
linux-security-module, linux-doc, linux-kernel, rust-for-linux,
Jann Horn, Alice Ryhl
task_euid() is a very weird operation. You can see how weird it is by
grepping for task_euid() - binder is its only user. task_euid() obtains
the objective effective UID - it looks at the credentials of the task
for purposes of acting on it as an object, but then accesses the
effective UID (which the credentials.7 man page describes as "[...] used
by the kernel to determine the permissions that the process will have
when accessing shared resources [...]").
Since usage in Binder has now been removed, get rid of the resulting
dead code.
Changes to the zh_CN translation was carried out with the help of
Gemini and Google Translate.
Suggested-by: Jann Horn <jannh@google.com>
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
Depends on these two changes:
https://lore.kernel.org/all/20260212-rust-uid-v1-1-deff4214c766@google.com/
https://lore.kernel.org/all/20260213-binder-uid-v1-0-7b795ae05523@google.com/
---
Documentation/security/credentials.rst | 6 ++----
Documentation/translations/zh_CN/security/credentials.rst | 6 ++----
include/linux/cred.h | 1 -
rust/helpers/task.c | 5 -----
rust/kernel/task.rs | 10 ----------
5 files changed, 4 insertions(+), 24 deletions(-)
diff --git a/Documentation/security/credentials.rst b/Documentation/security/credentials.rst
index d0191c8b8060edb7b272402c019cff941ec22743..81d3b5737d85bde9b77bff94dfb93ed8037b2302 100644
--- a/Documentation/security/credentials.rst
+++ b/Documentation/security/credentials.rst
@@ -393,16 +393,14 @@ the credentials so obtained when they're finished with.
The result of ``__task_cred()`` should not be passed directly to
``get_cred()`` as this may race with ``commit_cred()``.
-There are a couple of convenience functions to access bits of another task's
-credentials, hiding the RCU magic from the caller::
+There is a convenience function to access bits of another task's credentials,
+hiding the RCU magic from the caller::
uid_t task_uid(task) Task's real UID
- uid_t task_euid(task) Task's effective UID
If the caller is holding the RCU read lock at the time anyway, then::
__task_cred(task)->uid
- __task_cred(task)->euid
should be used instead. Similarly, if multiple aspects of a task's credentials
need to be accessed, RCU read lock should be used, ``__task_cred()`` called,
diff --git a/Documentation/translations/zh_CN/security/credentials.rst b/Documentation/translations/zh_CN/security/credentials.rst
index 88fcd9152ffe91d79fc10bfc7b2a37d301b4938a..f0b2efec342438b81be415dc513622c961bb7e59 100644
--- a/Documentation/translations/zh_CN/security/credentials.rst
+++ b/Documentation/translations/zh_CN/security/credentials.rst
@@ -337,15 +337,13 @@ const指针上操作,因此不需要进行类型转换,但需要临时放弃
``__task_cred()`` 的结果不应直接传递给 ``get_cred()`` ,
因为这可能与 ``commit_cred()`` 发生竞争条件。
-还有一些方便的函数可以访问另一个任务凭据的特定部分,将RCU操作对调用方隐藏起来::
+有一个方便的函数可用于访问另一个任务凭据的特定部分,从而对调用方隐藏RCU机制::
uid_t task_uid(task) Task's real UID
- uid_t task_euid(task) Task's effective UID
-如果调用方在此时已经持有RCU读锁,则应使用::
+如果调用方在此时已经持有RCU读锁,则应改为使用::
__task_cred(task)->uid
- __task_cred(task)->euid
类似地,如果需要访问任务凭据的多个方面,应使用RCU读锁,调用 ``__task_cred()``
函数,将结果存储在临时指针中,然后从临时指针中调用凭据的各个方面,最后释放锁。
diff --git a/include/linux/cred.h b/include/linux/cred.h
index ed1609d78cd7b16ed1434c937176495a4f38cf6e..b40ec3c72ee6673c7be5210a1667e3912cba9620 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -367,7 +367,6 @@ DEFINE_FREE(put_cred, struct cred *, if (!IS_ERR_OR_NULL(_T)) put_cred(_T))
})
#define task_uid(task) (task_cred_xxx((task), uid))
-#define task_euid(task) (task_cred_xxx((task), euid))
#define task_ucounts(task) (task_cred_xxx((task), ucounts))
#define current_cred_xxx(xxx) \
diff --git a/rust/helpers/task.c b/rust/helpers/task.c
index c0e1a06ede78c0b0641707b52a82725543e2c02c..b46b1433a67e8eb341a7ee32ca4247b304bf675f 100644
--- a/rust/helpers/task.c
+++ b/rust/helpers/task.c
@@ -28,11 +28,6 @@ __rust_helper kuid_t rust_helper_task_uid(struct task_struct *task)
return task_uid(task);
}
-__rust_helper kuid_t rust_helper_task_euid(struct task_struct *task)
-{
- return task_euid(task);
-}
-
#ifndef CONFIG_USER_NS
__rust_helper uid_t rust_helper_from_kuid(struct user_namespace *to, kuid_t uid)
{
diff --git a/rust/kernel/task.rs b/rust/kernel/task.rs
index e07d0ddd76f6917adc91ca3d17bb7719153ee17f..169ff1dde9363afc8914b431fe31f2238b213ada 100644
--- a/rust/kernel/task.rs
+++ b/rust/kernel/task.rs
@@ -218,16 +218,6 @@ pub fn uid(&self) -> Kuid {
Kuid::from_raw(unsafe { bindings::task_uid(self.as_ptr()) })
}
- /// Returns the objective effective UID of the given task.
- ///
- /// You should probably not be using this; the effective UID is normally
- /// only relevant in subjective credentials.
- #[inline]
- pub fn euid(&self) -> Kuid {
- // SAFETY: It's always safe to call `task_euid` on a valid task.
- Kuid::from_raw(unsafe { bindings::task_euid(self.as_ptr()) })
- }
-
/// Determines whether the given task has pending signals.
#[inline]
pub fn signal_pending(&self) -> bool {
---
base-commit: 2961f841b025fb234860bac26dfb7fa7cb0fb122
change-id: 20260219-remove-task-euid-19e4b00beebe
prerequisite-change-id: 20260212-rust-uid-f1b3a45c8084:v1
prerequisite-patch-id: 7ec4933af3a7f4c6bb0403c34a6dd41306836295
prerequisite-change-id: 20260213-binder-uid-a24ede5026a8:v1
prerequisite-patch-id: 7be0128bd8902879bb271d0587ac98bf242cf612
prerequisite-patch-id: 4a9d0f595d2084b3f8982a2d0d8b3df35b9fae0e
Best regards,
--
Alice Ryhl <aliceryhl@google.com>
^ permalink raw reply related
* Re: [PATCH v2 v2] evm: check return values of crypto_shash functions
From: Roberto Sassu @ 2026-02-19 12:36 UTC (permalink / raw)
To: Daniel Hodges
Cc: zohar, roberto.sassu, dmitry.kasatkin, eric.snowberg, paul,
jmorris, serge, linux-integrity, linux-security-module,
linux-kernel
In-Reply-To: <6ce273a26b396232f3ee64a980575562e766c501.camel@huaweicloud.com>
On Thu, 2026-02-19 at 10:26 +0100, Roberto Sassu wrote:
> On Thu, 2026-02-05 at 21:42 -0500, Daniel Hodges wrote:
> > The crypto_shash_update() and crypto_shash_final() functions can fail
> > and return error codes, but their return values were not being checked
> > in several places in security/integrity/evm/evm_crypto.c:
> >
> > - hmac_add_misc() ignored returns from crypto_shash_update() and
> > crypto_shash_final()
> > - evm_calc_hmac_or_hash() ignored returns from crypto_shash_update()
> > - evm_init_hmac() ignored returns from crypto_shash_update()
> >
> > If these hash operations fail silently, the resulting HMAC could be
> > invalid or incomplete, which could weaken the integrity verification
> > security that EVM provides.
> >
> > This patch converts hmac_add_misc() from void to int return type and
> > adds proper error checking and propagation for all crypto_shash_*
> > function calls. All callers are updated to handle the new return values.
> > Additionally, error messages are logged when cryptographic operations
> > fail to provide visibility into the failure rather than silently
> > returning error codes.
> >
> > Fixes: 66dbc325afce ("evm: re-release")
> > Signed-off-by: Daniel Hodges <git@danielhodges.dev>
>
> After fixing the minor issue below:
Already did it. The patch is here (after fixing a conflict with
0496fc9cdc38 "evm: Use ordered xattrs list to calculate HMAC in
evm_init_hmac()"):
https://github.com/robertosassu/linux/commit/d5aba42198b602c6de002ef02a4e6cc1d75652d7
Roberto
> Reviewed-by: Roberto Sassu <roberto.sassu@huawei.com>
>
> > ---
> > security/integrity/evm/evm_crypto.c | 55 ++++++++++++++++++++++-------
> > 1 file changed, 42 insertions(+), 13 deletions(-)
> >
> > diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
> > index a5e730ffda57..402eb1ca64ce 100644
> > --- a/security/integrity/evm/evm_crypto.c
> > +++ b/security/integrity/evm/evm_crypto.c
> > @@ -139,7 +139,7 @@ static struct shash_desc *init_desc(char type, uint8_t hash_algo)
> > * (Additional directory/file metadata needs to be added for more complete
> > * protection.)
> > */
> > -static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
> > +static int hmac_add_misc(struct shash_desc *desc, struct inode *inode,
> > char type, char *digest)
> > {
> > struct h_misc {
> > @@ -149,6 +149,7 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
> > gid_t gid;
> > umode_t mode;
> > } hmac_misc;
> > + int error;
> >
> > memset(&hmac_misc, 0, sizeof(hmac_misc));
> > /* Don't include the inode or generation number in portable
> > @@ -169,14 +170,28 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
> > hmac_misc.uid = from_kuid(&init_user_ns, inode->i_uid);
> > hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid);
> > hmac_misc.mode = inode->i_mode;
> > - crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc));
> > + error = crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc));
> > + if (error) {
> > + pr_err("crypto_shash_update() failed: %d\n", error);
> > + return error;
> > + }
> > if ((evm_hmac_attrs & EVM_ATTR_FSUUID) &&
> > - type != EVM_XATTR_PORTABLE_DIGSIG)
> > - crypto_shash_update(desc, (u8 *)&inode->i_sb->s_uuid, UUID_SIZE);
> > - crypto_shash_final(desc, digest);
> > + type != EVM_XATTR_PORTABLE_DIGSIG) {
> > + error = crypto_shash_update(desc, (u8 *)&inode->i_sb->s_uuid, UUID_SIZE);
> > + if (error) {
> > + pr_err("crypto_shash_update() failed: %d\n", error);
> > + return error;
> > + }
> > + }
> > + error = crypto_shash_final(desc, digest);
> > + if (error) {
> > + pr_err("crypto_shash_final() failed: %d\n", error);
> > + return error;
> > + }
> >
> > pr_debug("hmac_misc: (%zu) [%*phN]\n", sizeof(struct h_misc),
> > (int)sizeof(struct h_misc), &hmac_misc);
> > + return 0;
> > }
> >
> > /*
> > @@ -260,9 +275,12 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
> >
> > if ((req_xattr_name && req_xattr_value)
> > && !strcmp(xattr->name, req_xattr_name)) {
> > - error = 0;
> > - crypto_shash_update(desc, (const u8 *)req_xattr_value,
> > + error = crypto_shash_update(desc, (const u8 *)req_xattr_value,
> > req_xattr_value_len);
>
> Please align this.
>
> Thanks
>
> Roberto
>
> > + if (error) {
> > + pr_err("crypto_shash_update() failed: %d\n", error);
> > + goto out;
> > + }
> > if (is_ima)
> > ima_present = true;
> >
> > @@ -286,15 +304,20 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
> > pr_debug("file %s: xattr %s size mismatch (kernel: %d, user: %d)\n",
> > dentry->d_name.name, xattr->name, size,
> > user_space_size);
> > - error = 0;
> > xattr_size = size;
> > - crypto_shash_update(desc, (const u8 *)xattr_value, xattr_size);
> > + error = crypto_shash_update(desc, (const u8 *)xattr_value, xattr_size);
> > + if (error) {
> > + pr_err("crypto_shash_update() failed: %d\n", error);
> > + goto out;
> > + }
> > if (is_ima)
> > ima_present = true;
> >
> > dump_security_xattr(xattr->name, xattr_value, xattr_size);
> > }
> > - hmac_add_misc(desc, inode, type, data->digest);
> > + error = hmac_add_misc(desc, inode, type, data->digest);
> > + if (error)
> > + goto out;
> >
> > if (inode != d_backing_inode(dentry) && iint) {
> > if (IS_I_VERSION(inode))
> > @@ -401,6 +424,7 @@ int evm_init_hmac(struct inode *inode, const struct xattr *xattrs,
> > {
> > struct shash_desc *desc;
> > const struct xattr *xattr;
> > + int error;
> >
> > desc = init_desc(EVM_XATTR_HMAC, HASH_ALGO_SHA1);
> > if (IS_ERR(desc)) {
> > @@ -412,12 +436,17 @@ int evm_init_hmac(struct inode *inode, const struct xattr *xattrs,
> > if (!evm_protected_xattr(xattr->name))
> > continue;
> >
> > - crypto_shash_update(desc, xattr->value, xattr->value_len);
> > + error = crypto_shash_update(desc, xattr->value, xattr->value_len);
> > + if (error) {
> > + pr_err("crypto_shash_update() failed: %d\n", error);
> > + goto out;
> > + }
> > }
> >
> > - hmac_add_misc(desc, inode, EVM_XATTR_HMAC, hmac_val);
> > + error = hmac_add_misc(desc, inode, EVM_XATTR_HMAC, hmac_val);
> > +out:
> > kfree(desc);
> > - return 0;
> > + return error;
> > }
> >
> > /*
^ 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