* Re: [RFC PATCH 2/2] landlock: transpose the layer masks data structure
From: Günther Noack @ 2026-01-11 20:51 UTC (permalink / raw)
To: Mickaël Salaün
Cc: linux-security-module, Tingmao Wang, Justin Suess,
Samasth Norway Ananda, Matthieu Buffet, Mikhail Ivanov,
konstantin.meskhidze
In-Reply-To: <20260109.hie6Teis2ha9@digikod.net>
On Fri, Jan 09, 2026 at 05:18:43PM +0100, Mickaël Salaün wrote:
> This looks good overall but I need to spend more time reviewing it.
>
> Because this changes may impact other ongoing patch series, I think I'll
> take this patch first to ease potential future fix backports.
>
> On Tue, Dec 30, 2025 at 11:39:21AM +0100, Günther Noack wrote:
> > The layer masks data structure tracks the requested but unfulfilled
> > access rights during an operations security check. It stores one bit
> > for each combination of access right and layer index. If the bit is
> > set, that access right is not granted (yet) in the given layer and we
> > have to traverse the path further upwards to grant it.
> >
> > Previously, the layer masks were stored as arrays mapping from access
> > right indices to layer_mask_t. The layer_mask_t value then indicates
> > all layers in which the given access right is still (tentatively)
> > denied.
> >
> > This patch introduces struct layer_access_masks instead: This struct
> > contains an array with the access_mask_t of each (tentatively) denied
> > access right in that layer.
> >
> > The hypothesis of this patch is that this simplifies the code enough
> > so that the resulting code will run faster:
> >
> > * We can use bitwise operations in multiple places where we previously
> > looped over bits individually with macros. (Should require less
> > branch speculation)
> >
> > * Code is ~160 lines smaller.
>
> What about the KUnit test lines?
Those are counted as well. The 160 lines statistic is directly from
the diffstat in the cover letter.
(I removed the test_get_layer_deny_mask KUnit test, because the
function under test was also not needed any more. Other than that, the
KUnit tests are just adapted to test the equivalent logic with the new
data structure.)
> > Other noteworthy changes:
> >
> > * Clarify deny_mask_t and the code assembling it.
> > * Document what that value looks like
> > * Make writing and reading functions specific to file system rules.
> > (It only worked for FS rules before as well, but going all the way
> > simplifies the code logic more.)
> > * In no_more_access(), call a new helper function may_refer(), which
> > only solves the asymmetric case. Previously, the code interleaved
> > the checks for the two symmetric cases in RENAME_EXCHANGE. It feels
> > that the code is clearer when renames without RENAME_EXCHANGE are
> > more obviously the normal case.
>
> It would be interesting to check the stackframe diff. You can use
> scripts/stackdelta for that, see
> https://git.kernel.org/mic/c/602acfb541195eb35584d7a3fc7d1db676f059bd
Acknowledged. I did not get around to it yet, but put it on my todo
list.
> > Signed-off-by: Günther Noack <gnoack3000@gmail.com>
> > ---
> > security/landlock/access.h | 10 +-
> > security/landlock/audit.c | 155 ++++++----------
> > security/landlock/audit.h | 3 +-
> > security/landlock/domain.c | 120 +++----------
> > security/landlock/domain.h | 6 +-
> > security/landlock/fs.c | 350 ++++++++++++++++--------------------
> > security/landlock/net.c | 10 +-
> > security/landlock/ruleset.c | 78 +++-----
> > security/landlock/ruleset.h | 18 +-
> > 9 files changed, 290 insertions(+), 460 deletions(-)
460 - 290 is 170. Well, almost 160. :)
> > diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
> > index dfcdc19ea2683..d20e28d38e9c9 100644
> > --- a/security/landlock/ruleset.c
> > +++ b/security/landlock/ruleset.c
> > @@ -622,49 +622,24 @@ landlock_find_rule(const struct landlock_ruleset *const ruleset,
> > * request are empty).
> > */
> > bool landlock_unmask_layers(const struct landlock_rule *const rule,
> > - const access_mask_t access_request,
> > - layer_mask_t (*const layer_masks)[],
> > - const size_t masks_array_size)
> > + struct layer_access_masks *masks)
> > {
> > - size_t layer_level;
> > -
> > - if (!access_request || !layer_masks)
> > + if (!masks)
> > return true;
> > if (!rule)
> > return false;
> >
> > - /*
> > - * An access is granted if, for each policy layer, at least one rule
> > - * encountered on the pathwalk grants the requested access,
> > - * regardless of its position in the layer stack. We must then check
> > - * the remaining layers for each inode, from the first added layer to
> > - * the last one. When there is multiple requested accesses, for each
> > - * policy layer, the full set of requested accesses may not be granted
> > - * by only one rule, but by the union (binary OR) of multiple rules.
> > - * E.g. /a/b <execute> + /a <read> => /a/b <execute + read>
> > - */
>
> Why removing this comment?
I did not understand why the comment was discussing this higher level
picture when the surrounding function landlock_unmask_layers() is only
concerned with a single rule. Should this comment be better moved
elsewhere?
I don't feel strongly about it and re-reading it, the comment is still
true. In doubt, I can also just put it back into the same function
again. Let me know what you prefer.
> > - for (layer_level = 0; layer_level < rule->num_layers; layer_level++) {
> > - const struct landlock_layer *const layer =
> > - &rule->layers[layer_level];
> > - const layer_mask_t layer_bit = BIT_ULL(layer->level - 1);
> > - const unsigned long access_req = access_request;
> > - unsigned long access_bit;
> > - bool is_empty;
> > + for (int i = 0; i < rule->num_layers; i++) {
> > + const struct landlock_layer *l = &rule->layers[i];
> >
> > - /*
> > - * Records in @layer_masks which layer grants access to each requested
> > - * access: bit cleared if the related layer grants access.
> > - */
> > - is_empty = true;
> > - for_each_set_bit(access_bit, &access_req, masks_array_size) {
> > - if (layer->access & BIT_ULL(access_bit))
> > - (*layer_masks)[access_bit] &= ~layer_bit;
> > - is_empty = is_empty && !(*layer_masks)[access_bit];
> > - }
> > - if (is_empty)
> > - return true;
> > + masks->access[l->level - 1] &= ~l->access;
> > }
> > - return false;
> > +
> > + for (int i = 0; i < LANDLOCK_MAX_NUM_LAYERS; i++) {
> > + if (masks->access[i])
> > + return false;
> > + }
> > + return true;
> > }
> >
> > typedef access_mask_t
–Günther
^ permalink raw reply
* Re: [RFC PATCH 1/2] landlock: access_mask_subset() helper
From: Günther Noack @ 2026-01-11 20:01 UTC (permalink / raw)
To: Mickaël Salaün
Cc: linux-security-module, Tingmao Wang, Justin Suess,
Samasth Norway Ananda, Matthieu Buffet, Mikhail Ivanov,
konstantin.meskhidze
In-Reply-To: <20260109.Te8xoaceiqu3@digikod.net>
On Fri, Jan 09, 2026 at 05:06:10PM +0100, Mickaël Salaün wrote:
> On Tue, Dec 30, 2025 at 11:39:19AM +0100, Günther Noack wrote:
> > This helper function checks whether an access_mask_t has a subset of the
> > bits enabled than another one. This expresses the intent a bit smoother
> > in the code and does not cost us anything when it gets inlined.
> >
> > Signed-off-by: Günther Noack <gnoack3000@gmail.com>
> > ---
> > security/landlock/fs.c | 11 ++++++++++-
> > 1 file changed, 10 insertions(+), 1 deletion(-)
> >
> > diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> > index fe794875ad461..b4ce03bef4b8e 100644
> > --- a/security/landlock/fs.c
> > +++ b/security/landlock/fs.c
> > @@ -398,6 +398,15 @@ static const struct access_masks any_fs = {
> > .fs = ~0,
> > };
> >
> > +/*
> > + * Returns true iff a has a subset of the bits of b.
> > + * It helps readability and gets inlined.
> > + */
> > +static bool access_mask_subset(access_mask_t a, access_mask_t b)
> > +{
> > + return (a | b) == b;
>
> I'm curious about why this switches to a binary OR instead of the
> original AND.
It is slightly more intuitive to me, but other than that, no specific reason.
(a | b) == b has the same results as (b & a) == a.
I'm not feeling strongly about it.
We can also do it the other way around if you prefer.
–Günther
^ permalink raw reply
* [PATCH] landlock: Clarify documentation for the IOCTL access right
From: Günther Noack @ 2026-01-11 17:52 UTC (permalink / raw)
To: Mickaël Salaüne
Cc: linux-security-module, Tingmao Wang, Samasth Norway Ananda,
Günther Noack
Move the description of the LANDLOCK_ACCESS_FS_IOCTL_DEV access right
together with the file access rights.
This group of access rights applies to files (in this case device
files), and they can be added to file or directory inodes using
landlock_add_rule(2). The check for that works the same for all file
access rights, including LANDLOCK_ACCESS_FS_IOCTL_DEV.
Invoking ioctl(2) on directory FDs can not currently be restricted
with Landlock. Having it grouped separately in the documentation is a
remnant from earlier revisions of the LANDLOCK_ACCESS_FS_IOCTL_DEV
patch set.
Link: https://lore.kernel.org/all/20260108.Thaex5ruach2@digikod.net/
Signed-off-by: Günther Noack <gnoack3000@gmail.com>
---
include/uapi/linux/landlock.h | 37 ++++++++++++++++-------------------
1 file changed, 17 insertions(+), 20 deletions(-)
diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
index eac65da687c1..fbd18cf60a88 100644
--- a/include/uapi/linux/landlock.h
+++ b/include/uapi/linux/landlock.h
@@ -216,6 +216,23 @@ struct landlock_net_port_attr {
* :manpage:`ftruncate(2)`, :manpage:`creat(2)`, or :manpage:`open(2)` with
* ``O_TRUNC``. This access right is available since the third version of the
* Landlock ABI.
+ * - %LANDLOCK_ACCESS_FS_IOCTL_DEV: Invoke :manpage:`ioctl(2)` commands on an opened
+ * character or block device.
+ *
+ * This access right applies to all `ioctl(2)` commands implemented by device
+ * drivers. However, the following common IOCTL commands continue to be
+ * invokable independent of the %LANDLOCK_ACCESS_FS_IOCTL_DEV right:
+ *
+ * * IOCTL commands targeting file descriptors (``FIOCLEX``, ``FIONCLEX``),
+ * * IOCTL commands targeting file descriptions (``FIONBIO``, ``FIOASYNC``),
+ * * IOCTL commands targeting file systems (``FIFREEZE``, ``FITHAW``,
+ * ``FIGETBSZ``, ``FS_IOC_GETFSUUID``, ``FS_IOC_GETFSSYSFSPATH``)
+ * * Some IOCTL commands which do not make sense when used with devices, but
+ * whose implementations are safe and return the right error codes
+ * (``FS_IOC_FIEMAP``, ``FICLONE``, ``FICLONERANGE``, ``FIDEDUPERANGE``)
+ *
+ * This access right is available since the fifth version of the Landlock
+ * ABI.
*
* 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
@@ -275,26 +292,6 @@ struct landlock_net_port_attr {
* If multiple requirements are not met, the ``EACCES`` error code takes
* precedence over ``EXDEV``.
*
- * The following access right applies both to files and directories:
- *
- * - %LANDLOCK_ACCESS_FS_IOCTL_DEV: Invoke :manpage:`ioctl(2)` commands on an opened
- * character or block device.
- *
- * This access right applies to all `ioctl(2)` commands implemented by device
- * drivers. However, the following common IOCTL commands continue to be
- * invokable independent of the %LANDLOCK_ACCESS_FS_IOCTL_DEV right:
- *
- * * IOCTL commands targeting file descriptors (``FIOCLEX``, ``FIONCLEX``),
- * * IOCTL commands targeting file descriptions (``FIONBIO``, ``FIOASYNC``),
- * * IOCTL commands targeting file systems (``FIFREEZE``, ``FITHAW``,
- * ``FIGETBSZ``, ``FS_IOC_GETFSUUID``, ``FS_IOC_GETFSSYSFSPATH``)
- * * Some IOCTL commands which do not make sense when used with devices, but
- * whose implementations are safe and return the right error codes
- * (``FS_IOC_FIEMAP``, ``FICLONE``, ``FICLONERANGE``, ``FIDEDUPERANGE``)
- *
- * This access right is available since the fifth version of the Landlock
- * ABI.
- *
* .. warning::
*
* It is currently not possible to restrict some file-related actions
--
2.52.0
^ permalink raw reply related
* Re: [RFC PATCH 0/5] landlock: Pathname-based UNIX connect() control
From: Günther Noack @ 2026-01-11 10:15 UTC (permalink / raw)
To: Mickaël Salaün
Cc: Paul Moore, 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, Kuniyuki Iwashima
In-Reply-To: <20260109.aiXa3Iesh5wa@digikod.net>
On Fri, Jan 09, 2026 at 04:20:49PM +0100, Mickaël Salaün wrote:
> On Fri, Jan 09, 2026 at 03:41:33PM +0100, Günther Noack wrote:
> > On Fri, Jan 09, 2026 at 11:37:12AM +0100, Mickaël Salaün wrote:
> > > As Kuniyuki pointed out [1], we should handle both connect and send.
> > > This would be similar to the scoped restriction from Tingmao. I guess
> > > we'll need a similar hook for the send operation. Because there is no
> > > need to differenciate between connected and disconnected unix socket in
> > > a security policy, we should have one access right for both. Any
> > > proposal for its name? Something like TRANSMIT_UNIX or EMIT_UNIX?
> > >
> > > [1] https://lore.kernel.org/all/CAAVpQUAd==+Pw02+E6UC-qwaDNm7aFg+Q9YDbWzyniShAkAhFQ@mail.gmail.com/
> >
> > Ah, thanks for pointing it out.
> >
> > The restriction as implemented in this patch set already solves this
> > for all the three cases where a Unix socket file is looked up. I
> > believe that it is happening in all the right times (everytime when
> > the lookup has to happen).
> >
> > The cases where the restriction applies are the following:
> >
> > * unix_stream_connect - when calling connect() on a stream socket
> > * unix_dgram_connect - when calling connect() on a dgram socket
> > * unix_dgram_sendmsg - when calling sendmsg() on a dgram socket
> > (per-message lookup only)
> >
> > You can find the code locations by looking for the call to
> > unix_find_other() in af_unix.c. (That function invokes either
> > unix_find_bsd() or the lookup for abstract Unix sockets.)
> >
> > In the unix_dgram_sendmsg() case, the lookup is only performed if an
> > explicit sockaddr_un was provided together with the arguments to the
> > sendmsg(). (And sendto(2) also uses the same code path as
> > sendmsg(2).)
>
> Great
FYI, V2 splits the access rights for the three UNIX access types
SOCK_STREAM, SOCK_DGRAM and SOCK_SEQPACKET.
FYI, I double checked that the logic extends cleanly to the
SOCK_SEQPACKET case as well: SOCK_SEQPACKET is implemented as a thin
layer above the existing hooks for SOCK_STREAM and SOCK_DGRAM - it
does connect(2) with the SOCK_STREAM implementation, and it does
sendmsg(2) with the SOCK_DGRAM implementation, but in the sendmsg(2)
case it removes any explicitly passed recipient addresses beforehand.
(See "unix_seqpacket_ops" in net/unix/af_unix.c) So as far as the UNIX
path lookup hook is concerned, SOCK_SEQPACKET behaves like
SOCK_STREAM, and only invokes the hook during connect(2).
> > It is true that the current name for the access right is slightly
> > misleading. How about LANDLOCK_ACCESS_FS_UNIX_SEND? (Like
> > "transmit", but a bit closer to the naming of the sendmsg(2)
> > networking API?)
>
> We should try to keep the access right naming consistent:
> LANDLOCK_ACCESS_FS_<VERB>[_NOUN]
>
> What about USE_UNIX, or FIND_UNIX (closer to the kernel function), or
> RESOLVE_UNIX? It should be clear with the name that it is not about
> listening nor receiving from a process outside of the sandbox (which
> should have its own access right BTW).
Thanks, changed in V2 to say RESOLVE_UNIX, and also split up the
access rights for the three UNIX socket types.
(For reference, the path_resolution(7) man page [1] also uses the verb
"resolve". I think both "resolve" and "lookup" are used exchangeably
for paths.)
[1] https://man7.org/linux/man-pages/man7/path_resolution.7.html
> > (I guess the other alternative would be to wire the socket type
> > information through to the unix_find_bsd() function and pass it
> > through. Would require a small change to the af_unix.c implementation,
> > but then we could tell apart LANDLOCK_ACCESS_FS_UNIX_STREAM_CONNECT
> > and LANDLOCK_ACCESS_FS_UNIX_DGRAM_SEND). WDYT?
>
> I think the hook should have the same arguments as unix_find_bsd()'s
> ones. This gives the full context of the call.
You are right, I had not recalled that the socket type information
already gets passed to unix_find_bsd() - it required not much
additional wiring at all, in the end. 👍
–Günther
^ permalink raw reply
* Re: [PATCH v2 1/5] lsm: Add hook unix_path_connect
From: Günther Noack @ 2026-01-11 9:55 UTC (permalink / raw)
To: Justin Suess
Cc: Mickaël Salaün, Paul Moore, James Morris,
Serge E . Hallyn, linux-security-module, Tingmao Wang,
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: <4bc22faa-2927-4ef9-b5dc-67a7575177e9@gmail.com>
On Sat, Jan 10, 2026 at 11:45:03AM -0500, Justin Suess wrote:
> Just for awareness,
>
> I'm considering renaming this hook to unix_socket_path_lookup, since as Günther
> pointed out this hook is not just hit on connect, but also on sendmsg.
+1 I would be in favor of this.
(In doubt, Paul Moore has the last word on LSM hook naming. I believe
that both "lookup" and "resolve" are being used exchangeably to refer
to path lookups. (e.g. see the path_resolution(7) man page [1]))
–Günther
[1] https://man7.org/linux/man-pages/man7/path_resolution.7.html
^ permalink raw reply
* Re: [PATCH v2 3/5] samples/landlock: Add support for named UNIX domain socket restrictions
From: Günther Noack @ 2026-01-11 9:50 UTC (permalink / raw)
To: Mickaël Salaün
Cc: 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: <20260110143300.71048-7-gnoack3000@gmail.com>
On Sat, Jan 10, 2026 at 03:33:00PM +0100, Günther Noack wrote:
> The access rights for UNIX domain socket lookups are 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.
Sorry, I missed a part of the discussion in V1, which was suggested by
Tingmao Wang in [1]:
You are right, the new access rights should indeed become part of
ACCESS_FILE in the sample tool. (When the sample tool is adding a
rule for a non-directory, it only applies access rights that are also
in ACCESS_FILE.)
Will add it in V3.
–Günther
[1] https://lore.kernel.org/all/423dd2ca-ecba-47cf-98a7-4d99a48939da@maowtm.org/
^ permalink raw reply
* Re: [PATCH v3] selftests: complete kselftest include centralization
From: Bala-Vignesh-Reddy @ 2026-01-10 16:00 UTC (permalink / raw)
To: jackmanb
Cc: Liam.Howlett, akpm, davem, david.shane.hunter, david, edumazet,
gnoack, horms, khalid, kuba, linux-kernel-mentees, linux-kernel,
linux-kselftest, linux-mm, linux-security-module, lorenzo.stoakes,
mhocko, mic, ming.lei, pabeni, reddybalavignesh9979,
richard.weiyang, shuah, surenb, vbabka
In-Reply-To: <DFHI984SEFV3.2JL88CLHNT2SO@google.com>
Hey Brendan,
Thanks for the report.
This issue is caused by my change that centralized the kselftest.h
include path in lib.mk, while the x86 selftests Makefile overwrites CFLAGS
with :=, so shared include path unable to find kselftest.h. The fix is to
explicity add the selftests include directory to CFLAGS in
tools/testing/selftests/x86/Makefile.
I have already submitted this:
[PATCH] selftests/x86: Add selftests include path for kselftest.h after centralization
Link: https://lore.kernel.org/lkml/20251022062948.162852-1-reddybalavignesh9979@gmail.com/
it has been tested and confirmed working.
Tested-by: Anders Roxell <anders.roxell@linaro.org>
Once that patch is merged, the x86 selftests build issue should be
resolved.
Thanks
Bala Vignesh
^ permalink raw reply
* Re: [PATCH v2 1/5] lsm: Add hook unix_path_connect
From: Justin Suess @ 2026-01-10 16:45 UTC (permalink / raw)
To: Günther Noack, Mickaël Salaün, Paul Moore,
James Morris, Serge E . Hallyn
Cc: linux-security-module, Tingmao Wang, 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: <20260110143300.71048-4-gnoack3000@gmail.com>
On 1/10/26 09:32, Günther Noack wrote:
> From: Justin Suess <utilityemal77@gmail.com>
>
> Adds an LSM hook unix_path_connect.
>
> This hook is called to check the path of a named unix socket before a
> connection is initiated.
>
> Cc: Günther Noack <gnoack3000@gmail.com>
> Signed-off-by: Justin Suess <utilityemal77@gmail.com>
> ---
> include/linux/lsm_hook_defs.h | 4 ++++
> include/linux/security.h | 11 +++++++++++
> net/unix/af_unix.c | 9 +++++++++
> 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..1dee5d8d52d2 100644
> --- a/include/linux/lsm_hook_defs.h
> +++ b/include/linux/lsm_hook_defs.h
> @@ -317,6 +317,10 @@ 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_path_connect, const struct path *path, int type, 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..382612af27a6 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_path_connect(const struct path *path, int type, int flags);
> +
> +#else /* CONFIG_SECURITY_NETWORK && CONFIG_SECURITY_PATH */
> +static inline int security_unix_path_connect(const struct path *path, int type, 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 55cdebfa0da0..3aabe2d489ae 100644
> --- a/net/unix/af_unix.c
> +++ b/net/unix/af_unix.c
> @@ -1226,6 +1226,15 @@ static struct sock *unix_find_bsd(struct sockaddr_un *sunaddr, int addr_len,
> if (!S_ISSOCK(inode->i_mode))
> 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_path_connect(&path, type, flags);
> + if (err)
> + goto path_put;
> +
> + err = -ECONNREFUSED;
> sk = unix_find_socket_byinode(inode);
> if (!sk)
> goto path_put;
> diff --git a/security/security.c b/security/security.c
> index 31a688650601..0cee3502db83 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_path_connect() - Check if a named AF_UNIX socket can connect
> + * @path: path of the socket being connected to
> + * @type: type of the socket
> + * @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_path_connect(const struct path *path, int type, int flags)
> +{
> + return call_int_hook(unix_path_connect, path, type, flags);
> +}
> +EXPORT_SYMBOL(security_unix_path_connect);
> +
> +#endif /* CONFIG_SECURITY_NETWORK && CONFIG_SECURITY_PATH */
> +
> #ifdef CONFIG_SECURITY_INFINIBAND
> /**
> * security_ib_pkey_access() - Check if access to an IB pkey is allowed
Just for awareness,
I'm considering renaming this hook to unix_socket_path_lookup, since as Günther
pointed out this hook is not just hit on connect, but also on sendmsg.
Justin
^ permalink raw reply
* Re: [RFC PATCH 3/5] samples/landlock: Add support for LANDLOCK_ACCESS_FS_CONNECT_UNIX
From: Günther Noack @ 2026-01-10 15:05 UTC (permalink / raw)
To: Justin Suess
Cc: demiobenour, fahimitahera, hi, ivanov.mikhail1, jannh,
konstantin.meskhidze, linux-security-module, m, matthieu, mic,
paul, samasth.norway.ananda
In-Reply-To: <20260101193009.4005972-1-utilityemal77@gmail.com>
Hello!
On Thu, Jan 01, 2026 at 02:30:06PM -0500, Justin Suess wrote:
> Allow users to separately specify unix socket rights,
> document the variable, and make the right optional.
>
> Signed-off-by: Justin Suess <utilityemal77@gmail.com>
> Cc: Günther Noack <gnoack3000@gmail.com>
FYI, I ended up not applying this on V2.
I am unconvinced whether further separating the groups of access
rights is a good idea for the sandboxer. This is just sample code to
be used as reference, so it is good to keep it simple. I feel that
giving it more granular control over access rights does not help
readers to understand it much further?
It is true though that it would make sense to have this feature in
more production-grade tools. 👍
–Günther
^ permalink raw reply
* [PATCH v2 5/5] landlock: Document FS access rights for pathname UNIX sockets
From: Günther Noack @ 2026-01-10 14:33 UTC (permalink / raw)
To: Mickaël Salaün
Cc: Günther Noack, 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: <20260110143300.71048-2-gnoack3000@gmail.com>
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 | 25 +++++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst
index 1d0c2c15c22e..29afde4f7e75 100644
--- a/Documentation/userspace-api/landlock.rst
+++ b/Documentation/userspace-api/landlock.rst
@@ -77,7 +77,10 @@ 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_STREAM |
+ LANDLOCK_ACCESS_FS_RESOLVE_UNIX_DGRAM |
+ LANDLOCK_ACCESS_FS_RESOLVE_UNIX_SEQPACKET,
.handled_access_net =
LANDLOCK_ACCESS_NET_BIND_TCP |
LANDLOCK_ACCESS_NET_CONNECT_TCP,
@@ -127,6 +130,17 @@ 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:
+ /*
+ * Removes LANDLOCK_ACCESS_FS_RESOLVE_UNIX_STREAM,
+ * LANDLOCK_ACCESS_FS_RESOLVE_UNIX_DGRAM and
+ * LANDLOCK_ACCESS_FS_RESOLVE_UNIX_SEQPACKET for ABI < 8
+ */
+ ruleset_attr.handled_access_fs &=
+ ~(LANDLOCK_ACCESS_FS_RESOLVE_UNIX_STREAM |
+ LANDLOCK_ACCESS_FS_RESOLVE_UNIX_DGRAM |
+ LANDLOCK_ACCESS_FS_RESOLVE_UNIX_SEQPACKET);
}
This enables the creation of an inclusive ruleset that will contain our rules.
@@ -604,6 +618,15 @@ Landlock audit events with the ``LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF``,
sys_landlock_restrict_self(). See Documentation/admin-guide/LSM/landlock.rst
for more details on audit.
+Pathname UNIX sockets (ABI < 8)
+-------------------------------
+
+Starting with the Landlock ABI version 8, it is possible to restrict
+connections to pathname :manpage:`unix(7)` sockets using the new
+``LANDLOCK_ACCESS_FS_RESOLVE_UNIX_STREAM``,
+``LANDLOCK_ACCESS_FS_RESOLVE_UNIX_DGRAM`` and
+``LANDLOCK_ACCESS_FS_RESOLVE_UNIX_SEQPACKET`` rights.
+
.. _kernel_support:
Kernel support
--
2.52.0
^ permalink raw reply related
* [PATCH v2 4/5] landlock/selftests: Test named UNIX domain socket restrictions
From: Günther Noack @ 2026-01-10 14:33 UTC (permalink / raw)
To: Mickaël Salaün
Cc: Günther Noack, 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: <20260110143300.71048-2-gnoack3000@gmail.com>
* Exercise the access rights for connect() and sendmsg() to named UNIX
domain sockets, in various combinations.
* Extract common helpers from an existing IOCTL test that
also uses pathname unix(7) sockets.
Cc: Justin Suess <utilityemal77@gmail.com>
Cc: Mickaël Salaün <mic@digikod.net>
Signed-off-by: Günther Noack <gnoack3000@gmail.com>
---
tools/testing/selftests/landlock/fs_test.c | 218 +++++++++++++++++++--
1 file changed, 202 insertions(+), 16 deletions(-)
diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
index 0cbde65e032a..e1822fa687e8 100644
--- a/tools/testing/selftests/landlock/fs_test.c
+++ b/tools/testing/selftests/landlock/fs_test.c
@@ -4360,30 +4360,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));
+ ASSERT_EQ(0, bind(fd, (struct sockaddr *)&addr, sizeof(addr)));
+
+ if (type != SOCK_DGRAM)
+ ASSERT_EQ(0, listen(fd, 10 /* qlen */));
+ return fd;
+}
+
+/*
+ * test_connect_named_unix - connect to the given named UNIX socket
+ *
+ * Return: The errno from connect(), or 0
+ */
+static int test_connect_named_unix(int fd, const char *const path)
+{
+ struct sockaddr_un addr = {
+ .sun_family = AF_UNIX,
+ };
+ strncpy(addr.sun_path, path, sizeof(addr.sun_path));
+
+ if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
+ return errno;
+ return 0;
+}
+
/* For named UNIX domain sockets, no IOCTL restrictions apply. */
TEST_F_FORK(layout1, named_unix_domain_socket_ioctl)
{
const char *const path = file1_s1d1;
int srv_fd, cli_fd, ruleset_fd;
- struct sockaddr_un srv_un = {
- .sun_family = AF_UNIX,
- };
- struct sockaddr_un cli_un = {
- .sun_family = AF_UNIX,
- };
const struct landlock_ruleset_attr attr = {
.handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV,
};
/* Sets up a server */
ASSERT_EQ(0, unlink(path));
- srv_fd = socket(AF_UNIX, SOCK_STREAM, 0);
- ASSERT_LE(0, srv_fd);
-
- strncpy(srv_un.sun_path, path, sizeof(srv_un.sun_path));
- ASSERT_EQ(0, bind(srv_fd, (struct sockaddr *)&srv_un, sizeof(srv_un)));
-
- ASSERT_EQ(0, listen(srv_fd, 10 /* qlen */));
+ srv_fd = set_up_named_unix_server(_metadata, SOCK_STREAM, path);
/* Enables Landlock. */
ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
@@ -4395,9 +4426,7 @@ TEST_F_FORK(layout1, named_unix_domain_socket_ioctl)
cli_fd = socket(AF_UNIX, SOCK_STREAM, 0);
ASSERT_LE(0, cli_fd);
- strncpy(cli_un.sun_path, path, sizeof(cli_un.sun_path));
- ASSERT_EQ(0,
- connect(cli_fd, (struct sockaddr *)&cli_un, sizeof(cli_un)));
+ ASSERT_EQ(0, test_connect_named_unix(cli_fd, path));
/* FIONREAD and other IOCTLs should not be forbidden. */
EXPECT_EQ(0, test_fionread_ioctl(cli_fd));
@@ -4572,6 +4601,163 @@ TEST_F_FORK(ioctl, handle_file_access_file)
ASSERT_EQ(0, close(file_fd));
}
+/* clang-format off */
+FIXTURE(unix_socket) {};
+
+FIXTURE_SETUP(unix_socket) {};
+
+FIXTURE_TEARDOWN(unix_socket) {};
+/* clang-format on */
+
+FIXTURE_VARIANT(unix_socket)
+{
+ const __u64 handled;
+ const __u64 allowed;
+ const int sock_type;
+ const int expected;
+ const bool use_sendto;
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(unix_socket, stream_handled_not_allowed)
+{
+ /* clang-format on */
+ .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX_STREAM,
+ .allowed = 0,
+ .sock_type = SOCK_STREAM,
+ .expected = EACCES,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(unix_socket, stream_handled_and_allowed)
+{
+ /* clang-format on */
+ .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX_STREAM,
+ .allowed = LANDLOCK_ACCESS_FS_RESOLVE_UNIX_STREAM,
+ .sock_type = SOCK_STREAM,
+ .expected = 0,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(unix_socket, dgram_handled_not_allowed)
+{
+ /* clang-format on */
+ .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX_DGRAM,
+ .allowed = 0,
+ .sock_type = SOCK_DGRAM,
+ .expected = EACCES,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(unix_socket, dgram_handled_and_allowed)
+{
+ /* clang-format on */
+ .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX_DGRAM,
+ .allowed = LANDLOCK_ACCESS_FS_RESOLVE_UNIX_DGRAM,
+ .sock_type = SOCK_DGRAM,
+ .expected = 0,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(unix_socket, dgram_sendto_handled_not_allowed)
+{
+ /* clang-format on */
+ .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX_DGRAM,
+ .allowed = 0,
+ .sock_type = SOCK_DGRAM,
+ .use_sendto = true,
+ .expected = EACCES,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(unix_socket, dgram_sendto_handled_and_allowed)
+{
+ /* clang-format on */
+ .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX_DGRAM,
+ .allowed = LANDLOCK_ACCESS_FS_RESOLVE_UNIX_DGRAM,
+ .sock_type = SOCK_DGRAM,
+ .use_sendto = true,
+ .expected = 0,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(unix_socket, seqpacket_handled_not_allowed)
+{
+ /* clang-format on */
+ .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX_SEQPACKET,
+ .allowed = 0,
+ .sock_type = SOCK_SEQPACKET,
+ .expected = EACCES,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(unix_socket, seqpacket_handled_and_allowed)
+{
+ /* clang-format on */
+ .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX_SEQPACKET,
+ .allowed = LANDLOCK_ACCESS_FS_RESOLVE_UNIX_SEQPACKET,
+ .sock_type = SOCK_SEQPACKET,
+ .expected = 0,
+};
+
+/*
+ * test_sendto_named_unix - sendto to the given named UNIX socket
+ *
+ * sendto() is equivalent to sendmsg() in this respect.
+ *
+ * Return: The errno from sendto(), or 0
+ */
+static int test_sendto_named_unix(int fd, const char *const path)
+{
+ static const char buf[] = "dummy";
+ struct sockaddr_un addr = {
+ .sun_family = AF_UNIX,
+ };
+ strncpy(addr.sun_path, path, sizeof(addr.sun_path));
+
+ if (sendto(fd, buf, sizeof(buf), 0, (struct sockaddr *)&addr,
+ sizeof(addr)) == -1)
+ return errno;
+ return 0;
+}
+
+TEST_F_FORK(unix_socket, test)
+{
+ const char *const path = "sock";
+ int cli_fd, srv_fd, ruleset_fd, res;
+ const struct rule rules[] = {
+ {
+ .path = path,
+ .access = variant->allowed,
+ },
+ {},
+ };
+
+ /* Sets up a server */
+ srv_fd = set_up_named_unix_server(_metadata, variant->sock_type, path);
+
+ /* Enables Landlock. */
+ ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
+ ASSERT_LE(0, ruleset_fd);
+ enforce_ruleset(_metadata, ruleset_fd);
+ ASSERT_EQ(0, close(ruleset_fd));
+
+ /* Sets up a client connection to it */
+ cli_fd = socket(AF_UNIX, variant->sock_type, 0);
+ ASSERT_LE(0, cli_fd);
+
+ /* Connecting or sendto to the Unix socket is denied. */
+ if (variant->use_sendto)
+ res = test_sendto_named_unix(cli_fd, path);
+ else
+ res = test_connect_named_unix(cli_fd, path);
+ EXPECT_EQ(variant->expected, res);
+
+ ASSERT_EQ(0, close(cli_fd));
+ ASSERT_EQ(0, close(srv_fd));
+ ASSERT_EQ(0, unlink(path));
+}
+
/* clang-format off */
FIXTURE(layout1_bind) {};
/* clang-format on */
--
2.52.0
^ permalink raw reply related
* [PATCH v2 3/5] samples/landlock: Add support for named UNIX domain socket restrictions
From: Günther Noack @ 2026-01-10 14:33 UTC (permalink / raw)
To: Mickaël Salaün
Cc: Günther Noack, 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: <20260110143300.71048-2-gnoack3000@gmail.com>
The access rights for UNIX domain socket lookups are 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 | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
index e7af02f98208..f7e73ba8910c 100644
--- a/samples/landlock/sandboxer.c
+++ b/samples/landlock/sandboxer.c
@@ -295,11 +295,14 @@ 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_STREAM | \
+ LANDLOCK_ACCESS_FS_RESOLVE_UNIX_DGRAM | \
+ LANDLOCK_ACCESS_FS_RESOLVE_UNIX_SEQPACKET)
/* clang-format on */
-#define LANDLOCK_ABI_LAST 7
+#define LANDLOCK_ABI_LAST 8
#define XSTR(s) #s
#define STR(s) XSTR(s)
@@ -444,6 +447,17 @@ 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:
+ /*
+ * Removes LANDLOCK_ACCESS_FS_RESOLVE_UNIX_STREAM,
+ * LANDLOCK_ACCESS_FS_RESOLVE_UNIX_DGRAM and
+ * LANDLOCK_ACCESS_FS_RESOLVE_UNIX_SEQPACKET for ABI < 8
+ */
+ ruleset_attr.handled_access_fs &=
+ ~(LANDLOCK_ACCESS_FS_RESOLVE_UNIX_STREAM |
+ LANDLOCK_ACCESS_FS_RESOLVE_UNIX_DGRAM |
+ LANDLOCK_ACCESS_FS_RESOLVE_UNIX_SEQPACKET);
+ __attribute__((fallthrough));
case LANDLOCK_ABI_LAST:
break;
default:
--
2.52.0
^ permalink raw reply related
* [PATCH v2 2/5] landlock: Control pathname UNIX domain socket resolution by path
From: Günther Noack @ 2026-01-10 14:32 UTC (permalink / raw)
To: Mickaël Salaün
Cc: Günther Noack, Justin Suess, Jann Horn,
linux-security-module, Tingmao Wang, Samasth Norway Ananda,
Matthieu Buffet, Mikhail Ivanov, konstantin.meskhidze,
Demi Marie Obenour, Alyssa Ross, Tahera Fahimi
In-Reply-To: <20260110143300.71048-2-gnoack3000@gmail.com>
* Add new access rights which control the look up operations for named
UNIX domain sockets. The resolution happens during connect() and
sendmsg() (depending on socket type).
* LANDLOCK_ACCESS_FS_RESOLVE_UNIX_STREAM
* LANDLOCK_ACCESS_FS_RESOLVE_UNIX_DGRAM
* LANDLOCK_ACCESS_FS_RESOLVE_UNIX_SEQPACKET
* 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.
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 | 2 +-
security/landlock/audit.c | 6 ++++
security/landlock/fs.c | 34 +++++++++++++++++++-
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 | 7 ++--
8 files changed, 58 insertions(+), 7 deletions(-)
diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
index f030adc462ee..455edc241c12 100644
--- a/include/uapi/linux/landlock.h
+++ b/include/uapi/linux/landlock.h
@@ -216,6 +216,13 @@ struct landlock_net_port_attr {
* :manpage:`ftruncate(2)`, :manpage:`creat(2)`, or :manpage:`open(2)` with
* ``O_TRUNC``. This access right is available since the third version of the
* Landlock ABI.
+ * - %LANDLOCK_ACCESS_FS_RESOLVE_UNIX_STREAM: Connect to named
+ * :manpage:`unix(7)` ``SOCK_STREAM`` sockets.
+ * - %LANDLOCK_ACCESS_FS_RESOLVE_UNIX_DGRAM: Send messages to named
+ * :manpage:`unix(7)` ``SOCK_DGRAM`` sockets or connect to them using
+ * :manpage:`connect(2)`.
+ * - %LANDLOCK_ACCESS_FS_RESOLVE_UNIX_SEQPACKET: Connect to named
+ * :manpage:`unix(7)` ``SOCK_SEQPACKET`` sockets.
*
* 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
@@ -321,6 +328,9 @@ 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_STREAM (1ULL << 16)
+#define LANDLOCK_ACCESS_FS_RESOLVE_UNIX_DGRAM (1ULL << 17)
+#define LANDLOCK_ACCESS_FS_RESOLVE_UNIX_SEQPACKET (1ULL << 18)
/* clang-format on */
/**
diff --git a/security/landlock/access.h b/security/landlock/access.h
index 7961c6630a2d..c7784922be3c 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);
diff --git a/security/landlock/audit.c b/security/landlock/audit.c
index e899995f1fd5..0645304e0375 100644
--- a/security/landlock/audit.c
+++ b/security/landlock/audit.c
@@ -37,6 +37,12 @@ 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_STREAM)] =
+ "fs.resolve_unix_stream",
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_RESOLVE_UNIX_DGRAM)] =
+ "fs.resolve_unix_dgram",
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_RESOLVE_UNIX_SEQPACKET)] =
+ "fs.resolve_unix_seqpacket",
};
static_assert(ARRAY_SIZE(fs_access_strings) == LANDLOCK_NUM_ACCESS_FS);
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 8205673c8b1c..94f5fc7ee9fd 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -9,6 +9,7 @@
* Copyright © 2023-2024 Google LLC
*/
+#include "linux/net.h"
#include <asm/ioctls.h>
#include <kunit/test.h>
#include <linux/atomic.h>
@@ -314,7 +315,10 @@ 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_STREAM | \
+ LANDLOCK_ACCESS_FS_RESOLVE_UNIX_DGRAM | \
+ LANDLOCK_ACCESS_FS_RESOLVE_UNIX_SEQPACKET)
/* clang-format on */
/*
@@ -1588,6 +1592,33 @@ static int hook_path_truncate(const struct path *const path)
return current_check_access_path(path, LANDLOCK_ACCESS_FS_TRUNCATE);
}
+static int hook_unix_path_connect(const struct path *const path, int type,
+ int flags)
+{
+ access_mask_t access_request = 0;
+
+ /* Lookup for the purpose of saving coredumps is OK. */
+ if (flags & SOCK_COREDUMP)
+ return 0;
+
+ switch (type) {
+ case SOCK_STREAM:
+ access_request = LANDLOCK_ACCESS_FS_RESOLVE_UNIX_STREAM;
+ break;
+ case SOCK_DGRAM:
+ access_request = LANDLOCK_ACCESS_FS_RESOLVE_UNIX_DGRAM;
+ break;
+ case SOCK_SEQPACKET:
+ access_request = LANDLOCK_ACCESS_FS_RESOLVE_UNIX_SEQPACKET;
+ break;
+ }
+
+ if (!access_request)
+ return 0;
+
+ return current_check_access_path(path, access_request);
+}
+
/* File hooks */
/**
@@ -1872,6 +1903,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_path_connect, hook_unix_path_connect),
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 65b5ff051674..1f6f864afec2 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_SEQPACKET
#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 0116e9f93ffe..66fd196be85a 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -161,7 +161,7 @@ static const struct file_operations ruleset_fops = {
* Documentation/userspace-api/landlock.rst should be updated to reflect the
* UAPI change.
*/
-const int landlock_abi_version = 7;
+const int landlock_abi_version = 8;
/**
* 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 7b69002239d7..f4b1a275d8d9 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(7, landlock_create_ruleset(NULL, 0,
+ ASSERT_EQ(8, 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..0cbde65e032a 100644
--- a/tools/testing/selftests/landlock/fs_test.c
+++ b/tools/testing/selftests/landlock/fs_test.c
@@ -575,9 +575,12 @@ 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_STREAM | \
+ LANDLOCK_ACCESS_FS_RESOLVE_UNIX_DGRAM | \
+ LANDLOCK_ACCESS_FS_RESOLVE_UNIX_SEQPACKET)
-#define ACCESS_LAST LANDLOCK_ACCESS_FS_IOCTL_DEV
+#define ACCESS_LAST LANDLOCK_ACCESS_FS_RESOLVE_UNIX_SEQPACKET
#define ACCESS_ALL ( \
ACCESS_FILE | \
--
2.52.0
^ permalink raw reply related
* [PATCH v2 1/5] lsm: Add hook unix_path_connect
From: Günther Noack @ 2026-01-10 14:32 UTC (permalink / raw)
To: Mickaël Salaün, Paul Moore, James Morris,
Serge E . Hallyn
Cc: Günther Noack, 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, Simon Horman, netdev,
Alexander Viro, Christian Brauner
In-Reply-To: <20260110143300.71048-2-gnoack3000@gmail.com>
From: Justin Suess <utilityemal77@gmail.com>
Adds an LSM hook unix_path_connect.
This hook is called to check the path of a named unix socket before a
connection is initiated.
Cc: Günther Noack <gnoack3000@gmail.com>
Signed-off-by: Justin Suess <utilityemal77@gmail.com>
---
include/linux/lsm_hook_defs.h | 4 ++++
include/linux/security.h | 11 +++++++++++
net/unix/af_unix.c | 9 +++++++++
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..1dee5d8d52d2 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -317,6 +317,10 @@ 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_path_connect, const struct path *path, int type, 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..382612af27a6 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_path_connect(const struct path *path, int type, int flags);
+
+#else /* CONFIG_SECURITY_NETWORK && CONFIG_SECURITY_PATH */
+static inline int security_unix_path_connect(const struct path *path, int type, 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 55cdebfa0da0..3aabe2d489ae 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1226,6 +1226,15 @@ static struct sock *unix_find_bsd(struct sockaddr_un *sunaddr, int addr_len,
if (!S_ISSOCK(inode->i_mode))
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_path_connect(&path, type, flags);
+ if (err)
+ goto path_put;
+
+ err = -ECONNREFUSED;
sk = unix_find_socket_byinode(inode);
if (!sk)
goto path_put;
diff --git a/security/security.c b/security/security.c
index 31a688650601..0cee3502db83 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_path_connect() - Check if a named AF_UNIX socket can connect
+ * @path: path of the socket being connected to
+ * @type: type of the socket
+ * @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_path_connect(const struct path *path, int type, int flags)
+{
+ return call_int_hook(unix_path_connect, path, type, flags);
+}
+EXPORT_SYMBOL(security_unix_path_connect);
+
+#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 related
* [PATCH v2 0/5] landlock: Pathname-based UNIX connect() control
From: Günther Noack @ 2026-01-10 14:32 UTC (permalink / raw)
To: Mickaël Salaün, Paul Moore, James Morris,
Serge E . Hallyn
Cc: Günther Noack, 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, Simon Horman, netdev,
Alexander Viro, Christian Brauner
Hello!
This patch set introduces a filesystem-based Landlock restriction
mechanism for connecting to UNIX domain sockets (or addressing them
with sendmsg(2)). It introduces a file system access right for each
type of UNIX domain socket:
* LANDLOCK_ACCESS_FS_RESOLVE_UNIX_STREAM
* LANDLOCK_ACCESS_FS_RESOLVE_UNIX_DGRAM
* LANDLOCK_ACCESS_FS_RESOLVE_UNIX_SEQPACKET
For the connection-oriented SOCK_STREAM and SOCK_SEQPACKET type
sockets, the access right makes the connect(2) operation fail with
EACCES, if denied.
SOCK_DGRAM-type UNIX sockets can be used both with connect(2), or by
passing an explicit recipient address with every sendmsg(2)
invocation. In the latter case, the Landlock check is done when an
explicit recipient address is passed to sendmsg(2) and can make
sendmsg(2) return EACCES. When UNIX datagram sockets are connected
with connect(2), a fixed recipient address is associated with the
socket and the check happens during connect(2) and may return EACCES.
## Motivation
Currently, landlocked processes can connect() to named UNIX sockets
through the BSD socket API described in unix(7), by invoking socket(2)
followed by connect(2) with a suitable struct sockname_un holding the
socket's filename. This can come as a surprise for users (e.g. in
[1]) and it can be used to escape a sandbox when a Unix service offers
command execution (some scenarios were listed by Tingmao Wang in [2]).
The original feature request is at [4].
## Alternatives and Related Work
### Alternative: Use existing LSM hooks
The existing hooks security_unix_stream_connect(),
security_unix_may_send() and security_socket_connect() do not give
access to the resolved file system path.
Resolving the file system path again within Landlock would in my
understanding produce a TOCTOU race, so making the decision based on
the struct sockaddr_un contents is not an option.
It is tempting to use the struct path that the listening socket is
bound to, which can be acquired through the existing hooks.
Unfortunately, the listening socket may have been bound from within a
different namespace, and it is therefore a path that can not actually
be referenced by the sandboxed program at the time of constructing the
Landlock policy. (More details are on the Github issue at [6] and on
the LKML at [9]).
### Related work: Scope Control for Pathname Unix Sockets
The motivation for this patch is the same as in Tingmao Wang's patch
set for "scoped" control for pathname Unix sockets [2], originally
proposed in the Github feature request [5].
In my reply to this patch set [3], I have discussed the differences
between these two approaches. On the related discussions on Github
[4] and [5], there was consensus that the scope-based control is
complimentary to the file system based control, but does not replace
it. Mickael's opening remark on [5] says:
> This scoping would be complementary to #36 which would mainly be
> about allowing a sandboxed process to connect to a more privileged
> service (identified with a path).
## Open questions in V2
Seeking feedback on:
- Feedback on the LSM hook name would be appreciated. We realize that
not all invocations of the LSM hook are related to connect(2) as the
name suggests, but some also happen during sendmsg(2).
- Feedback on the structuring of the Landlock access rights, splitting
them up by socket type. (Also naming; they are now consistently
called "RESOLVE", but could be named "CONNECT" in the stream and
seqpacket cases?)
## Credits
The feature was originally suggested by Jann Horn in [7].
Tingmao Wang and Demi Marie Obenour have taken the initiative to
revive this discussion again in [1], [4] and [5] and Tingmao Wang has
sent the patch set for the scoped access control for pathname Unix
sockets [2].
Justin Suess has sent the patch for the LSM hook in [8].
Ryan Sullivan has started on an initial implementation and has brought
up relevant discussion points on the Github issue at [4] that lead to
the current approach.
[1] https://lore.kernel.org/landlock/515ff0f4-2ab3-46de-8d1e-5c66a93c6ede@gmail.com/
[2] Tingmao Wang's "Implemnet scope control for pathname Unix sockets"
https://lore.kernel.org/all/cover.1767115163.git.m@maowtm.org/
[3] https://lore.kernel.org/all/20251230.bcae69888454@gnoack.org/
[4] Github issue for FS-based control for named Unix sockets:
https://github.com/landlock-lsm/linux/issues/36
[5] Github issue for scope-based restriction of named Unix sockets:
https://github.com/landlock-lsm/linux/issues/51
[6] https://github.com/landlock-lsm/linux/issues/36#issuecomment-2950632277
[7] https://lore.kernel.org/linux-security-module/CAG48ez3NvVnonOqKH4oRwRqbSOLO0p9djBqgvxVwn6gtGQBPcw@mail.gmail.com/
[8] Patch for the LSM hook:
https://lore.kernel.org/all/20251231213314.2979118-1-utilityemal77@gmail.com/
[9] https://lore.kernel.org/all/20260108.64bd7391e1ae@gnoack.org/
---
## Older versions of this patch set
V1: https://lore.kernel.org/all/20260101134102.25938-1-gnoack3000@gmail.com/
Changes in V2:
* Send Justin Suess's LSM hook patch together with the Landlock
implementation
* LSM hook: Pass type and flags parameters to the hook, to make the
access right more generally usable across LSMs, per suggestion from
Paul Moore (Implemented by Justin)
* Split the access right into the three types of UNIX domain sockets:
SOCK_STREAM, SOCK_DGRAM and SOCK_SEQPACKET.
* selftests: More exhaustive tests.
* Removed a minor commit from V1 which adds a missing close(fd) to a
test (it is already in the mic-next branch)
Günther Noack (4):
landlock: Control pathname UNIX domain socket resolution by path
samples/landlock: Add support for named UNIX domain socket
restrictions
landlock/selftests: Test named UNIX domain socket restrictions
landlock: Document FS access rights for pathname UNIX sockets
Justin Suess (1):
lsm: Add hook unix_path_connect
Documentation/userspace-api/landlock.rst | 25 ++-
include/linux/lsm_hook_defs.h | 4 +
include/linux/security.h | 11 +
include/uapi/linux/landlock.h | 10 +
net/unix/af_unix.c | 9 +
samples/landlock/sandboxer.c | 18 +-
security/landlock/access.h | 2 +-
security/landlock/audit.c | 6 +
security/landlock/fs.c | 34 ++-
security/landlock/limits.h | 2 +-
security/landlock/syscalls.c | 2 +-
security/security.c | 20 ++
tools/testing/selftests/landlock/base_test.c | 2 +-
tools/testing/selftests/landlock/fs_test.c | 225 +++++++++++++++++--
14 files changed, 344 insertions(+), 26 deletions(-)
--
2.52.0
^ permalink raw reply
* Re: [RFC PATCH 1/5] landlock/selftests: add a missing close(srv_fd) call
From: Günther Noack @ 2026-01-10 10:37 UTC (permalink / raw)
To: Mickaël Salaün
Cc: Paul Moore, 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: <20260109.uukiph8ii0Je@digikod.net>
On Fri, Jan 09, 2026 at 11:49:48AM +0100, Mickaël Salaün wrote:
> On Fri, Jan 09, 2026 at 11:41:30AM +0100, Mickaël Salaün wrote:
> > Good, I'll pick that in my -next branch.
> >
> > Nit: The prefix should be "selftests/landlock"
> >
> > On Thu, Jan 01, 2026 at 02:40:58PM +0100, Günther Noack wrote:
> > > Signed-off-by: Günther Noack <gnoack3000@gmail.com>
> > > ---
> > > tools/testing/selftests/landlock/fs_test.c | 1 +
> > > 1 file changed, 1 insertion(+)
> > >
> > > diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
> > > index 37a5a3df712ec..16503f2e6a481 100644
> > > --- a/tools/testing/selftests/landlock/fs_test.c
> > > +++ b/tools/testing/selftests/landlock/fs_test.c
> > > @@ -4400,6 +4400,7 @@ TEST_F_FORK(layout1, named_unix_domain_socket_ioctl)
> > > EXPECT_EQ(0, test_fionread_ioctl(cli_fd));
> > >
> > > ASSERT_EQ(0, close(cli_fd));
> > > + ASSERT_EQ(0, close(srv_fd));
>
> I'll also replace these ASSERT_EQ() with EXPECT_EQ().
Fair enough. I would normally prefer ASSERT here, because that would
be more symmetric with the corresponding setup steps, but it feels not
worth bikeshedding over this.
The selftests, both Landlock and others, are inconsistent in how they
use ASSERT and EXPECT, especially for close(). I wish we had an
easier way to do state teardown in the selftests without having to tie
it to a FIXTURE()...
–Günther
^ permalink raw reply
* Re: [PATCH v6] security: Add KUnit tests for kuid_root_in_ns and vfsuid_root_in_currentns
From: Serge E. Hallyn @ 2026-01-10 4:50 UTC (permalink / raw)
To: Ryan Foster; +Cc: serge, linux-kernel, linux-security-module, paul, selinux
In-Reply-To: <20260107215725.105822-1-foster.ryan.r@gmail.com>
On Wed, Jan 07, 2026 at 01:51:28PM -0800, Ryan Foster wrote:
>
> Here's v6 with both fixes combined. The Dec 29 version you have in caps-next
> is correct for the namespace config - v6 keeps that and adds the KUNIT=y
> dependency to fix the Intel CI build error.
>
> Changes in v6:
> - Namespace config: all three namespaces are independent children of
> init_user_ns (same as Dec 29 you reviewed)
>
> - Build fix: depends on KUNIT=y prevents link errors when KUNIT=m
>
> The Dec 30 patch accidentally reverted the namespace fix when I was adding the
> KUNIT=y part. This v6 has both fixes working together.
>
> Thanks, Ryan
>
> Add comprehensive KUnit tests for the namespace-related capability
> functions that Serge Hallyn refactored in commit 9891d2f79a9f
> ("Clarify the rootid_owns_currentns").
>
> The tests verify:
> - Basic functionality: UID 0 in init namespace, invalid vfsuid,
> non-zero UIDs
> - Actual namespace traversal: Creating user namespaces with different
> UID mappings where uid 0 maps to different kuids (e.g., 1000, 2000,
> 3000)
> - Hierarchy traversal: Testing multiple nested namespaces to verify
> correct namespace hierarchy traversal
>
> This addresses the feedback to "test the actual functionality" by
> creating real user namespaces with different values for the
> namespace's uid 0, rather than just basic input validation.
>
> The test file is included at the end of commoncap.c when
> CONFIG_SECURITY_COMMONCAP_KUNIT_TEST is enabled, following the
> standard kernel pattern (e.g., scsi_lib.c, ext4/mballoc.c). This
> allows tests to access static functions in the same compilation unit
> without modifying production code based on test configuration.
>
> The tests require CONFIG_USER_NS to be enabled since they rely on user
> namespace mapping functionality. The Kconfig dependency ensures the
> tests only build when this requirement is met.
>
> All 7 tests pass:
> - test_vfsuid_root_in_currentns_init_ns
> - test_vfsuid_root_in_currentns_invalid
> - test_vfsuid_root_in_currentns_nonzero
> - test_kuid_root_in_ns_init_ns_uid0
> - test_kuid_root_in_ns_init_ns_nonzero
> - test_kuid_root_in_ns_with_mapping
> - test_kuid_root_in_ns_with_different_mappings
>
> Updated MAINTAINER capabilities to include commoncap test
>
> Signed-off-by: Ryan Foster <foster.ryan.r@gmail.com>
Thanks, applied to git://git.kernel.org/pub/scm/linux/kernel/git/sergeh/linux.git #caps-next
^ permalink raw reply
* Re: [PATCH v5 10/36] locking/mutex: Support Clang's context analysis
From: Marco Elver @ 2026-01-10 3:23 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Bart Van Assche, Peter Zijlstra, Boqun Feng, Ingo Molnar,
Will Deacon, David S. Miller, Luc Van Oostenryck, Chris Li,
Paul E. McKenney, Alexander Potapenko, Arnd Bergmann,
Dmitry Vyukov, Eric Dumazet, Frederic Weisbecker,
Greg Kroah-Hartman, Herbert Xu, Ian Rogers, Jann Horn,
Joel Fernandes, Johannes Berg, Jonathan Corbet, Josh Triplett,
Justin Stitt, Kees Cook, Kentaro Takeda, Lukas Bulwahn,
Mark Rutland, Mathieu Desnoyers, Miguel Ojeda, Nathan Chancellor,
Neeraj Upadhyay, Nick Desaulniers, Steven Rostedt, Tetsuo Handa,
Thomas Gleixner, Thomas Graf, Uladzislau Rezki, Waiman Long,
kasan-dev, linux-crypto, linux-doc, linux-kbuild, linux-kernel,
linux-mm, linux-security-module, linux-sparse, linux-wireless,
llvm, rcu
In-Reply-To: <20260109060249.GA5259@lst.de>
On Fri, Jan 09, 2026 at 07:02AM +0100, Christoph Hellwig wrote:
> On Fri, Jan 09, 2026 at 12:26:55AM +0100, Marco Elver wrote:
> > Probably the most idiomatic option is to just factor out construction.
> > Clearly separating complex object construction from use also helps
> > readability regardless, esp. where concurrency is involved. We could
> > document such advice somewhere.
>
> Initializing and locking a mutex (or spinlock, or other primitive) is a
> not too unusual pattern, often used when inserting an object into a
> hash table or other lookup data structure. So supporting it without
> creating pointless wrapper functions would be really useful. One thing
> that would be nice to have and probably help here is to have lock
> initializers that create the lock in a held state.
Fair point. Without new APIs, we can fix it with the below patch;
essentially "promoting" the context lock to "reentrant" during
initialization scope. It's not exactly well documented on the Clang
side, but is a side-effect of how reentrancy works in the analysis:
https://github.com/llvm/llvm-project/pull/175267
------ >8 ------
From 9c9b521b286f241f849dcc4f9efbd9582dabd3cc Mon Sep 17 00:00:00 2001
From: Marco Elver <elver@google.com>
Date: Sat, 10 Jan 2026 00:47:35 +0100
Subject: [PATCH] compiler-context-analysis: Support immediate acquisition
after initialization
When a lock is initialized (e.g. mutex_init()), we assume/assert that
the context lock is held to allow initialization of guarded members
within the same scope.
However, this previously prevented actually acquiring the lock within
that same scope, as the analyzer would report a double-lock warning:
mutex_init(&mtx);
...
mutex_lock(&mtx); // acquiring mutex 'mtx' that is already held
To fix (without new init+lock APIs), we can tell the analysis to treat
the "held" context lock resulting from initialization as reentrant,
allowing subsequent acquisitions to succeed.
To do so *only* within the initialization scope, we can cast the lock
pointer to any reentrant type for the init assume/assert. Introduce a
generic reentrant context lock type `struct __ctx_lock_init` and add
`__inits_ctx_lock()` that casts the lock pointer to this type before
assuming/asserting it.
This ensures that the initial "held" state is reentrant, allowing
patterns like:
mutex_init(&lock);
...
mutex_lock(&lock);
to compile without false positives, and avoids having to make all
context lock types reentrant outside an initialization scope.
The caveat here is missing real double-lock bugs right after init scope.
However, this is a classic trade-off of avoiding false positives against
(unlikely) false negatives.
Link: https://lore.kernel.org/all/57062131-e79e-42c2-aa0b-8f931cb8cac2@acm.org/
Reported-by: Bart Van Assche <bvanassche@acm.org>
Signed-off-by: Marco Elver <elver@google.com>
---
include/linux/compiler-context-analysis.h | 12 ++++++++++++
include/linux/local_lock_internal.h | 6 +++---
include/linux/mutex.h | 2 +-
include/linux/rwlock.h | 4 ++--
include/linux/rwlock_rt.h | 2 +-
include/linux/rwsem.h | 4 ++--
include/linux/seqlock.h | 2 +-
include/linux/spinlock.h | 8 ++++----
include/linux/spinlock_rt.h | 2 +-
include/linux/ww_mutex.h | 2 +-
lib/test_context-analysis.c | 3 +++
11 files changed, 31 insertions(+), 16 deletions(-)
diff --git a/include/linux/compiler-context-analysis.h b/include/linux/compiler-context-analysis.h
index db7e0d48d8f2..e056cd6e8aaa 100644
--- a/include/linux/compiler-context-analysis.h
+++ b/include/linux/compiler-context-analysis.h
@@ -43,6 +43,14 @@
# define __assumes_ctx_lock(...) __attribute__((assert_capability(__VA_ARGS__)))
# define __assumes_shared_ctx_lock(...) __attribute__((assert_shared_capability(__VA_ARGS__)))
+/*
+ * Generic reentrant context lock type that we cast to when initializing context
+ * locks with __assumes_ctx_lock(), so that we can support guarded member
+ * initialization, but also immediate use after initialization.
+ */
+struct __ctx_lock_type(init_generic) __reentrant_ctx_lock __ctx_lock_init;
+# define __inits_ctx_lock(var) __assumes_ctx_lock((const struct __ctx_lock_init *)(var))
+
/**
* __guarded_by - struct member and globals attribute, declares variable
* only accessible within active context
@@ -120,6 +128,8 @@
__attribute__((overloadable)) __assumes_ctx_lock(var) { } \
static __always_inline void __assume_shared_ctx_lock(const struct name *var) \
__attribute__((overloadable)) __assumes_shared_ctx_lock(var) { } \
+ static __always_inline void __init_ctx_lock(const struct name *var) \
+ __attribute__((overloadable)) __inits_ctx_lock(var) { } \
struct name
/**
@@ -162,6 +172,7 @@
# define __releases_shared_ctx_lock(...)
# define __assumes_ctx_lock(...)
# define __assumes_shared_ctx_lock(...)
+# define __inits_ctx_lock(var)
# define __returns_ctx_lock(var)
# define __guarded_by(...)
# define __pt_guarded_by(...)
@@ -176,6 +187,7 @@
# define __release_shared_ctx_lock(var) do { } while (0)
# define __assume_ctx_lock(var) do { (void)(var); } while (0)
# define __assume_shared_ctx_lock(var) do { (void)(var); } while (0)
+# define __init_ctx_lock(var) do { (void)(var); } while (0)
# define context_lock_struct(name, ...) struct __VA_ARGS__ name
# define disable_context_analysis()
# define enable_context_analysis()
diff --git a/include/linux/local_lock_internal.h b/include/linux/local_lock_internal.h
index e8c4803d8db4..36b8628d09fd 100644
--- a/include/linux/local_lock_internal.h
+++ b/include/linux/local_lock_internal.h
@@ -86,13 +86,13 @@ do { \
0, LD_WAIT_CONFIG, LD_WAIT_INV, \
LD_LOCK_PERCPU); \
local_lock_debug_init(lock); \
- __assume_ctx_lock(lock); \
+ __init_ctx_lock(lock); \
} while (0)
#define __local_trylock_init(lock) \
do { \
__local_lock_init((local_lock_t *)lock); \
- __assume_ctx_lock(lock); \
+ __init_ctx_lock(lock); \
} while (0)
#define __spinlock_nested_bh_init(lock) \
@@ -104,7 +104,7 @@ do { \
0, LD_WAIT_CONFIG, LD_WAIT_INV, \
LD_LOCK_NORMAL); \
local_lock_debug_init(lock); \
- __assume_ctx_lock(lock); \
+ __init_ctx_lock(lock); \
} while (0)
#define __local_lock_acquire(lock) \
diff --git a/include/linux/mutex.h b/include/linux/mutex.h
index 89977c215cbd..5d2ef75c4fdb 100644
--- a/include/linux/mutex.h
+++ b/include/linux/mutex.h
@@ -62,7 +62,7 @@ do { \
static struct lock_class_key __key; \
\
__mutex_init((mutex), #mutex, &__key); \
- __assume_ctx_lock(mutex); \
+ __init_ctx_lock(mutex); \
} while (0)
/**
diff --git a/include/linux/rwlock.h b/include/linux/rwlock.h
index 65a5b55e1bcd..7e171634d2c4 100644
--- a/include/linux/rwlock.h
+++ b/include/linux/rwlock.h
@@ -22,11 +22,11 @@ do { \
static struct lock_class_key __key; \
\
__rwlock_init((lock), #lock, &__key); \
- __assume_ctx_lock(lock); \
+ __init_ctx_lock(lock); \
} while (0)
#else
# define rwlock_init(lock) \
- do { *(lock) = __RW_LOCK_UNLOCKED(lock); __assume_ctx_lock(lock); } while (0)
+ do { *(lock) = __RW_LOCK_UNLOCKED(lock); __init_ctx_lock(lock); } while (0)
#endif
#ifdef CONFIG_DEBUG_SPINLOCK
diff --git a/include/linux/rwlock_rt.h b/include/linux/rwlock_rt.h
index 37b387dcab21..1e087a6ce2cf 100644
--- a/include/linux/rwlock_rt.h
+++ b/include/linux/rwlock_rt.h
@@ -22,7 +22,7 @@ do { \
\
init_rwbase_rt(&(rwl)->rwbase); \
__rt_rwlock_init(rwl, #rwl, &__key); \
- __assume_ctx_lock(rwl); \
+ __init_ctx_lock(rwl); \
} while (0)
extern void rt_read_lock(rwlock_t *rwlock) __acquires_shared(rwlock);
diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h
index 8da14a08a4e1..6ea7d2a23580 100644
--- a/include/linux/rwsem.h
+++ b/include/linux/rwsem.h
@@ -121,7 +121,7 @@ do { \
static struct lock_class_key __key; \
\
__init_rwsem((sem), #sem, &__key); \
- __assume_ctx_lock(sem); \
+ __init_ctx_lock(sem); \
} while (0)
/*
@@ -175,7 +175,7 @@ do { \
static struct lock_class_key __key; \
\
__init_rwsem((sem), #sem, &__key); \
- __assume_ctx_lock(sem); \
+ __init_ctx_lock(sem); \
} while (0)
static __always_inline int rwsem_is_locked(const struct rw_semaphore *sem)
diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h
index 113320911a09..a0670adb4b6e 100644
--- a/include/linux/seqlock.h
+++ b/include/linux/seqlock.h
@@ -816,7 +816,7 @@ static __always_inline void write_seqcount_latch_end(seqcount_latch_t *s)
do { \
spin_lock_init(&(sl)->lock); \
seqcount_spinlock_init(&(sl)->seqcount, &(sl)->lock); \
- __assume_ctx_lock(sl); \
+ __init_ctx_lock(sl); \
} while (0)
/**
diff --git a/include/linux/spinlock.h b/include/linux/spinlock.h
index 396b8c5d6c1b..e50372a5f7d1 100644
--- a/include/linux/spinlock.h
+++ b/include/linux/spinlock.h
@@ -106,12 +106,12 @@ do { \
static struct lock_class_key __key; \
\
__raw_spin_lock_init((lock), #lock, &__key, LD_WAIT_SPIN); \
- __assume_ctx_lock(lock); \
+ __init_ctx_lock(lock); \
} while (0)
#else
# define raw_spin_lock_init(lock) \
- do { *(lock) = __RAW_SPIN_LOCK_UNLOCKED(lock); __assume_ctx_lock(lock); } while (0)
+ do { *(lock) = __RAW_SPIN_LOCK_UNLOCKED(lock); __init_ctx_lock(lock); } while (0)
#endif
#define raw_spin_is_locked(lock) arch_spin_is_locked(&(lock)->raw_lock)
@@ -324,7 +324,7 @@ do { \
\
__raw_spin_lock_init(spinlock_check(lock), \
#lock, &__key, LD_WAIT_CONFIG); \
- __assume_ctx_lock(lock); \
+ __init_ctx_lock(lock); \
} while (0)
#else
@@ -333,7 +333,7 @@ do { \
do { \
spinlock_check(_lock); \
*(_lock) = __SPIN_LOCK_UNLOCKED(_lock); \
- __assume_ctx_lock(_lock); \
+ __init_ctx_lock(_lock); \
} while (0)
#endif
diff --git a/include/linux/spinlock_rt.h b/include/linux/spinlock_rt.h
index 0a585768358f..154d7290bd99 100644
--- a/include/linux/spinlock_rt.h
+++ b/include/linux/spinlock_rt.h
@@ -20,7 +20,7 @@ static inline void __rt_spin_lock_init(spinlock_t *lock, const char *name,
do { \
rt_mutex_base_init(&(slock)->lock); \
__rt_spin_lock_init(slock, name, key, percpu); \
- __assume_ctx_lock(slock); \
+ __init_ctx_lock(slock); \
} while (0)
#define _spin_lock_init(slock, percpu) \
diff --git a/include/linux/ww_mutex.h b/include/linux/ww_mutex.h
index 58e959ee10e9..ecb5564ee70d 100644
--- a/include/linux/ww_mutex.h
+++ b/include/linux/ww_mutex.h
@@ -107,7 +107,7 @@ context_lock_struct(ww_acquire_ctx) {
*/
static inline void ww_mutex_init(struct ww_mutex *lock,
struct ww_class *ww_class)
- __assumes_ctx_lock(lock)
+ __inits_ctx_lock(lock)
{
ww_mutex_base_init(&lock->base, ww_class->mutex_name, &ww_class->mutex_key);
lock->ctx = NULL;
diff --git a/lib/test_context-analysis.c b/lib/test_context-analysis.c
index 1c5a381461fc..2f733b5cc650 100644
--- a/lib/test_context-analysis.c
+++ b/lib/test_context-analysis.c
@@ -165,6 +165,9 @@ static void __used test_mutex_init(struct test_mutex_data *d)
{
mutex_init(&d->mtx);
d->counter = 0;
+
+ mutex_lock(&d->mtx);
+ mutex_unlock(&d->mtx);
}
static void __used test_mutex_lock(struct test_mutex_data *d)
--
2.52.0.457.g6b5491de43-goog
^ permalink raw reply related
* Re: [PATCH v5 20/36] locking/ww_mutex: Support Clang's context analysis
From: Bart Van Assche @ 2026-01-09 21:26 UTC (permalink / raw)
To: Maarten Lankhorst
Cc: Marco Elver, Peter Zijlstra, Boqun Feng, Ingo Molnar, Will Deacon,
David S. Miller, Luc Van Oostenryck, Chris Li, Paul E. McKenney,
Alexander Potapenko, Arnd Bergmann, Christoph Hellwig,
Dmitry Vyukov, Eric Dumazet, Frederic Weisbecker,
Greg Kroah-Hartman, Herbert Xu, Ian Rogers, Jann Horn,
Joel Fernandes, Johannes Berg, Jonathan Corbet, Josh Triplett,
Justin Stitt, Kees Cook, Kentaro Takeda, Lukas Bulwahn,
Mark Rutland, Mathieu Desnoyers, Miguel Ojeda, Nathan Chancellor,
Neeraj Upadhyay, Nick Desaulniers, Steven Rostedt, Tetsuo Handa,
Thomas Gleixner, Thomas Graf, Uladzislau Rezki, Waiman Long,
kasan-dev, linux-crypto, linux-doc, linux-kbuild, linux-kernel,
linux-mm, linux-security-module, linux-sparse, linux-wireless,
llvm, rcu
In-Reply-To: <aWFt6hcLaCjQQu2c@elver.google.com>
(+Maarten)
On 1/9/26 2:06 PM, Marco Elver wrote:
> If there's 1 out of N ww_mutex users that missed ww_acquire_done()
> there's a good chance that 1 case is wrong.
$ git grep -w ww_acquire_done '**c'|wc -l
11
$ git grep -w ww_acquire_fini '**c'|wc -l
33
The above statistics show that there are more cases where
ww_acquire_done() is not called rather than cases where
ww_acquire_done() is called.
Maarten, since you introduced the ww_mutex code, do you perhaps prefer
that calling ww_acquire_done() is optional or rather that all users that
do not call ww_acquire_done() are modified such that they call
ww_acquire_done()? The full email conversation is available here:
https://lore.kernel.org/all/20251219154418.3592607-1-elver@google.com/
Thanks,
Bart.
^ permalink raw reply
* Re: [PATCH v5 20/36] locking/ww_mutex: Support Clang's context analysis
From: Marco Elver @ 2026-01-09 21:06 UTC (permalink / raw)
To: Bart Van Assche
Cc: Peter Zijlstra, Boqun Feng, Ingo Molnar, Will Deacon,
David S. Miller, Luc Van Oostenryck, Chris Li, Paul E. McKenney,
Alexander Potapenko, Arnd Bergmann, Christoph Hellwig,
Dmitry Vyukov, Eric Dumazet, Frederic Weisbecker,
Greg Kroah-Hartman, Herbert Xu, Ian Rogers, Jann Horn,
Joel Fernandes, Johannes Berg, Jonathan Corbet, Josh Triplett,
Justin Stitt, Kees Cook, Kentaro Takeda, Lukas Bulwahn,
Mark Rutland, Mathieu Desnoyers, Miguel Ojeda, Nathan Chancellor,
Neeraj Upadhyay, Nick Desaulniers, Steven Rostedt, Tetsuo Handa,
Thomas Gleixner, Thomas Graf, Uladzislau Rezki, Waiman Long,
kasan-dev, linux-crypto, linux-doc, linux-kbuild, linux-kernel,
linux-mm, linux-security-module, linux-sparse, linux-wireless,
llvm, rcu
In-Reply-To: <05c77ca1-7618-43c5-b259-d89741808479@acm.org>
On Fri, Jan 09, 2026 at 12:16PM -0800, Bart Van Assche wrote:
> On 12/19/25 8:40 AM, Marco Elver wrote:
> > Add support for Clang's context analysis for ww_mutex.
> >
> > The programming model for ww_mutex is subtly more complex than other
> > locking primitives when using ww_acquire_ctx. Encoding the respective
> > pre-conditions for ww_mutex lock/unlock based on ww_acquire_ctx state
> > using Clang's context analysis makes incorrect use of the API harder.
>
> That's a very short description. It should have been explained in the
> patch description how the ww_acquire_ctx changes affect callers of the
> ww_acquire_{init,done,fini}() functions.
How so? The API is the same (now statically enforced), and there's no
functional change at runtime. Or did I miss something?
> > static inline void ww_acquire_init(struct ww_acquire_ctx *ctx,
> > struct ww_class *ww_class)
> > + __acquires(ctx) __no_context_analysis
> > [ ... ]
> > static inline void ww_acquire_done(struct ww_acquire_ctx *ctx)
> > + __releases(ctx) __acquires_shared(ctx) __no_context_analysis
> > {
> > [ ... ]
> > static inline void ww_acquire_fini(struct ww_acquire_ctx *ctx)
> > + __releases_shared(ctx) __no_context_analysis
>
> The above changes make it mandatory to call ww_acquire_done() before
> calling ww_acquire_fini(). In Documentation/locking/ww-mutex-design.rst
> there is an example where there is no ww_acquire_done() call between
> ww_acquire_init() and ww_acquire_fini() (see also line 202).
It might be worth updating the example with what the kernel-doc
documentation recommends (below).
> The
> function dma_resv_lockdep() in drivers/dma-buf/dma-resv.c doesn't call
> ww_acquire_done() at all. Does this mean that the above annotations are
> wrong?
If there's 1 out of N ww_mutex users that missed ww_acquire_done()
there's a good chance that 1 case is wrong.
But generally, depends if we want to enforce ww_acquire_done() or not
which itself is no-op in non-lockdep builds, however, with
DEBUG_WW_MUTEXES it's no longer no-op so it might be a good idea to
enforce it to get proper lockdep checking.
> Is there a better solution than removing the __acquire() and
> __release() annotations from the above three functions?
The kernel-doc comment for ww_acquire_done() says:
/**
* ww_acquire_done - marks the end of the acquire phase
* @ctx: the acquire context
*
>> * Marks the end of the acquire phase, any further w/w mutex lock calls using
>> * this context are forbidden.
>> *
>> * Calling this function is optional, it is just useful to document w/w mutex
>> * code and clearly designated the acquire phase from actually using the locked
>> * data structures.
*/
static inline void ww_acquire_done(struct ww_acquire_ctx *ctx)
__releases(ctx) __acquires_shared(ctx) __no_context_analysis
{
#ifdef DEBUG_WW_MUTEXES
lockdep_assert_held(ctx);
DEBUG_LOCKS_WARN_ON(ctx->done_acquire);
ctx->done_acquire = 1;
#endif
}
It states it's optional, but it's unclear if that's true with
DEBUG_WW_MUTEXES builds. I'd vote for enforcing use of
ww_acquire_done(). If there's old code that's not using it, it should be
added there to get proper lockdep checking.
^ permalink raw reply
* Re: [RFC PATCH 0/5] landlock: Pathname-based UNIX connect() control
From: Demi Marie Obenour @ 2026-01-09 21:02 UTC (permalink / raw)
To: Mickaël Salaün
Cc: Günther Noack, Tingmao Wang, Paul Moore,
linux-security-module, Justin Suess, Samasth Norway Ananda,
Matthieu Buffet, Mikhail Ivanov, konstantin.meskhidze,
Alyssa Ross, Jann Horn, Tahera Fahimi
In-Reply-To: <20260109.yahQuiwu9kug@digikod.net>
[-- Attachment #1.1.1: Type: text/plain, Size: 3299 bytes --]
On 1/9/26 10:25, Mickaël Salaün wrote:
> On Fri, Jan 09, 2026 at 06:33:10AM -0500, Demi Marie Obenour wrote:
>> On 1/8/26 06:14, Mickaël Salaün wrote:
>>> On Fri, Jan 02, 2026 at 01:37:14PM -0500, Demi Marie Obenour wrote:
>>>> On 1/2/26 05:50, Günther Noack wrote:
>>>>> On Fri, Jan 02, 2026 at 05:27:40AM -0500, Demi Marie Obenour wrote:
>>>>>> On 1/2/26 05:16, Günther Noack wrote:
>>>>>>> On Thu, Jan 01, 2026 at 05:44:51PM -0500, Demi Marie Obenour wrote:
>>>>>>>> On 1/1/26 17:34, Tingmao Wang wrote:
>>>>>>>>> On 1/1/26 22:14, Demi Marie Obenour wrote:
>>>>>>>>>> [...]
>>>>>>>>>> Does this leave directory traversal as the only missing Landlock
>>>>>>>>>> filesystem access control? Ideally Landlock could provide the same
>>>>>>>>>> isolation from the filesystem that mount namespaces do.
>>>>>>>>>
>>>>>>>>> I think that level of isolation would require path walk control - see:
>>>>>>>>> https://github.com/landlock-lsm/linux/issues/9
>>>>>>>>>
>>>>>>>>> (Landlock also doesn't currently control some metadata operations - see
>>>>>>>>> the warning at the end of the "Filesystem flags" section in [1])
>>>>>>>>>
>>>>>>>>> [1]: https://docs.kernel.org/6.18/userspace-api/landlock.html#filesystem-flags
>>>>>>>>
>>>>>>>> Could this replace all of the existing hooks?
>>>>>>>
>>>>>>> If you do not need to distinguish between the different operations
>>>>>>> which Landlock offers access rights for, but you only want to limit
>>>>>>> the visibility of directory hierarchies in the file system, then yes,
>>>>>>> the path walk control described in issue 9 would be sufficient and a
>>>>>>> more complete control.
>>>>>>>
>>>>>>> The path walk control is probably among the more difficult Landlock
>>>>>>> feature requests. A simple implementation would be easy to implement
>>>>>>> technically, but it also requires a new LSM hook which will have to
>>>>>>> get called *during* path lookup, and we'd have to make sure that the
>>>>>>> performance impact stays in check. Path lookup is after all a very
>>>>>>> central facility in a OS kernel.
>>>>>>
>>>>>> What about instead using the inode-based hooks for directory searching?
>>>>>> SELinux can already restrict that.
>>>>>
>>>>> Oh, thanks, good pointer! I was under the impression that this didn't
>>>>> exist yet -- I assume you are referring to the
>>>>> security_inode_follow_link() hook, which is already happening during
>>>>> path resolution?
>>>>
>>>> I'm not familiar with existing LSM hooks, but I do know that SELinux
>>>> enforces checks on searching and reading directories and symlinks.
>>>
>>> SELinux uses inode-based hooks, which is not (directly) possible for
>>> Landlock because it is an unprivileged access control, which means it
>>> cannot rely on extended file attributes to define a security policy.
>>>
>>> See https://github.com/landlock-lsm/linux/issues/9
>>
>> Could Landlock use a side table, with the inode's address in memory
>> as the key?
>
> A struct inode is not enough because we need to resolve a file
> hierarchy, which is only possible with a struct path.
Could Landlock "piggyback" on the core kernel's own path resolution,
updating its state when each hook gets called?
--
Sincerely,
Demi Marie Obenour (she/her/hers)
[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 7253 bytes --]
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* Re: Improved guidance for LSM submissions.
From: Casey Schaufler @ 2026-01-09 19:58 UTC (permalink / raw)
To: Paul Moore, Dr. Greg; +Cc: linux-security-module, Casey Schaufler
In-Reply-To: <CAHC9VhTyvEVLGLJkkyQnSZYSj4-YHPz82BnDEUwMjU7hHdbFoA@mail.gmail.com>
On 1/9/2026 10:51 AM, Paul Moore wrote:
> On Thu, Jan 8, 2026 at 11:08 AM Dr. Greg <greg@enjellic.com> wrote:
>> What is not clear in these guidelines is how a virgin LSM should be
>> structured for initial submission. Moving forward, we believe the
>> community would benefit from having clear guidance on this issue.
>>
>> It would be helpful if the guidance covers a submission of 10-15 KLOC
>> of code and 5-8 compilation units, which seems to cover the average
>> range of sizes for LSM's that have significant coverage of the event
>> handlers/hooks.
Good day Greg, I hope you are well.
If you would review the comments I made in 2023 regarding how to
make your submission reviewable you might find that you don't need
a "formal" statement of policy. Remember that you are not submitting
your code to a chartered organization, but to a collection of system
developers who are enthusiastic about security. Many are overworked,
some are hobbyists, but all treat their time as valuable. If you can't
heed the advice you've already been given, there's no incentive for
anyone to spend their limited resources to provide it in another
format.
> I would suggest looking at the existing Linux kernel documentation on
> submitting patches, a link is provided below. The entire
> document/page is worth reading in full, but the "Separate your
> changes" section seems to be most relevant to your question above.
>
> https://docs.kernel.org/process/submitting-patches.html
>
> Beyond that general guidance, at this particular moment I do not have
> the cycles to draft a LSM specific recommendation beyond what has
> already been documented and reviewed. As usual, the mailing list
> archives are also an excellent resource that can be used to
> familiarize yourself with community norms and expectations.
>
^ permalink raw reply
* Re: [PATCH v5 20/36] locking/ww_mutex: Support Clang's context analysis
From: Bart Van Assche @ 2026-01-09 20:16 UTC (permalink / raw)
To: Marco Elver, Peter Zijlstra, Boqun Feng, Ingo Molnar, Will Deacon
Cc: David S. Miller, Luc Van Oostenryck, Chris Li, Paul E. McKenney,
Alexander Potapenko, Arnd Bergmann, Christoph Hellwig,
Dmitry Vyukov, Eric Dumazet, Frederic Weisbecker,
Greg Kroah-Hartman, Herbert Xu, Ian Rogers, Jann Horn,
Joel Fernandes, Johannes Berg, Jonathan Corbet, Josh Triplett,
Justin Stitt, Kees Cook, Kentaro Takeda, Lukas Bulwahn,
Mark Rutland, Mathieu Desnoyers, Miguel Ojeda, Nathan Chancellor,
Neeraj Upadhyay, Nick Desaulniers, Steven Rostedt, Tetsuo Handa,
Thomas Gleixner, Thomas Graf, Uladzislau Rezki, Waiman Long,
kasan-dev, linux-crypto, linux-doc, linux-kbuild, linux-kernel,
linux-mm, linux-security-module, linux-sparse, linux-wireless,
llvm, rcu
In-Reply-To: <20251219154418.3592607-21-elver@google.com>
On 12/19/25 8:40 AM, Marco Elver wrote:
> Add support for Clang's context analysis for ww_mutex.
>
> The programming model for ww_mutex is subtly more complex than other
> locking primitives when using ww_acquire_ctx. Encoding the respective
> pre-conditions for ww_mutex lock/unlock based on ww_acquire_ctx state
> using Clang's context analysis makes incorrect use of the API harder.
That's a very short description. It should have been explained in the
patch description how the ww_acquire_ctx changes affect callers of the
ww_acquire_{init,done,fini}() functions.
> static inline void ww_acquire_init(struct ww_acquire_ctx *ctx,
> struct ww_class *ww_class)
> + __acquires(ctx) __no_context_analysis
> [ ... ]
> static inline void ww_acquire_done(struct ww_acquire_ctx *ctx)
> + __releases(ctx) __acquires_shared(ctx) __no_context_analysis
> {
> [ ... ]
> static inline void ww_acquire_fini(struct ww_acquire_ctx *ctx)
> + __releases_shared(ctx) __no_context_analysis
The above changes make it mandatory to call ww_acquire_done() before
calling ww_acquire_fini(). In Documentation/locking/ww-mutex-design.rst
there is an example where there is no ww_acquire_done() call between
ww_acquire_init() and ww_acquire_fini() (see also line 202). The
function dma_resv_lockdep() in drivers/dma-buf/dma-resv.c doesn't call
ww_acquire_done() at all. Does this mean that the above annotations are
wrong? Is there a better solution than removing the __acquire() and
__release() annotations from the above three functions?
Bart.
^ permalink raw reply
* Re: Improved guidance for LSM submissions.
From: Paul Moore @ 2026-01-09 18:51 UTC (permalink / raw)
To: Dr. Greg; +Cc: linux-security-module
In-Reply-To: <20260108154604.GA14181@wind.enjellic.com>
On Thu, Jan 8, 2026 at 11:08 AM Dr. Greg <greg@enjellic.com> wrote:
>
> What is not clear in these guidelines is how a virgin LSM should be
> structured for initial submission. Moving forward, we believe the
> community would benefit from having clear guidance on this issue.
>
> It would be helpful if the guidance covers a submission of 10-15 KLOC
> of code and 5-8 compilation units, which seems to cover the average
> range of sizes for LSM's that have significant coverage of the event
> handlers/hooks.
I would suggest looking at the existing Linux kernel documentation on
submitting patches, a link is provided below. The entire
document/page is worth reading in full, but the "Separate your
changes" section seems to be most relevant to your question above.
https://docs.kernel.org/process/submitting-patches.html
Beyond that general guidance, at this particular moment I do not have
the cycles to draft a LSM specific recommendation beyond what has
already been documented and reviewed. As usual, the mailing list
archives are also an excellent resource that can be used to
familiarize yourself with community norms and expectations.
--
paul-moore.com
^ permalink raw reply
* Re: [RFC PATCH 2/2] landlock: transpose the layer masks data structure
From: Mickaël Salaün @ 2026-01-09 16:18 UTC (permalink / raw)
To: Günther Noack
Cc: linux-security-module, Tingmao Wang, Justin Suess,
Samasth Norway Ananda, Matthieu Buffet, Mikhail Ivanov,
konstantin.meskhidze
In-Reply-To: <20251230103917.10549-7-gnoack3000@gmail.com>
This looks good overall but I need to spend more time reviewing it.
Because this changes may impact other ongoing patch series, I think I'll
take this patch first to ease potential future fix backports.
On Tue, Dec 30, 2025 at 11:39:21AM +0100, Günther Noack wrote:
> The layer masks data structure tracks the requested but unfulfilled
> access rights during an operations security check. It stores one bit
> for each combination of access right and layer index. If the bit is
> set, that access right is not granted (yet) in the given layer and we
> have to traverse the path further upwards to grant it.
>
> Previously, the layer masks were stored as arrays mapping from access
> right indices to layer_mask_t. The layer_mask_t value then indicates
> all layers in which the given access right is still (tentatively)
> denied.
>
> This patch introduces struct layer_access_masks instead: This struct
> contains an array with the access_mask_t of each (tentatively) denied
> access right in that layer.
>
> The hypothesis of this patch is that this simplifies the code enough
> so that the resulting code will run faster:
>
> * We can use bitwise operations in multiple places where we previously
> looped over bits individually with macros. (Should require less
> branch speculation)
>
> * Code is ~160 lines smaller.
What about the KUnit test lines?
>
> Other noteworthy changes:
>
> * Clarify deny_mask_t and the code assembling it.
> * Document what that value looks like
> * Make writing and reading functions specific to file system rules.
> (It only worked for FS rules before as well, but going all the way
> simplifies the code logic more.)
> * In no_more_access(), call a new helper function may_refer(), which
> only solves the asymmetric case. Previously, the code interleaved
> the checks for the two symmetric cases in RENAME_EXCHANGE. It feels
> that the code is clearer when renames without RENAME_EXCHANGE are
> more obviously the normal case.
It would be interesting to check the stackframe diff. You can use
scripts/stackdelta for that, see
https://git.kernel.org/mic/c/602acfb541195eb35584d7a3fc7d1db676f059bd
>
> Signed-off-by: Günther Noack <gnoack3000@gmail.com>
> ---
> security/landlock/access.h | 10 +-
> security/landlock/audit.c | 155 ++++++----------
> security/landlock/audit.h | 3 +-
> security/landlock/domain.c | 120 +++----------
> security/landlock/domain.h | 6 +-
> security/landlock/fs.c | 350 ++++++++++++++++--------------------
> security/landlock/net.c | 10 +-
> security/landlock/ruleset.c | 78 +++-----
> security/landlock/ruleset.h | 18 +-
> 9 files changed, 290 insertions(+), 460 deletions(-)
> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
> index dfcdc19ea2683..d20e28d38e9c9 100644
> --- a/security/landlock/ruleset.c
> +++ b/security/landlock/ruleset.c
> @@ -622,49 +622,24 @@ landlock_find_rule(const struct landlock_ruleset *const ruleset,
> * request are empty).
> */
> bool landlock_unmask_layers(const struct landlock_rule *const rule,
> - const access_mask_t access_request,
> - layer_mask_t (*const layer_masks)[],
> - const size_t masks_array_size)
> + struct layer_access_masks *masks)
> {
> - size_t layer_level;
> -
> - if (!access_request || !layer_masks)
> + if (!masks)
> return true;
> if (!rule)
> return false;
>
> - /*
> - * An access is granted if, for each policy layer, at least one rule
> - * encountered on the pathwalk grants the requested access,
> - * regardless of its position in the layer stack. We must then check
> - * the remaining layers for each inode, from the first added layer to
> - * the last one. When there is multiple requested accesses, for each
> - * policy layer, the full set of requested accesses may not be granted
> - * by only one rule, but by the union (binary OR) of multiple rules.
> - * E.g. /a/b <execute> + /a <read> => /a/b <execute + read>
> - */
Why removing this comment?
> - for (layer_level = 0; layer_level < rule->num_layers; layer_level++) {
> - const struct landlock_layer *const layer =
> - &rule->layers[layer_level];
> - const layer_mask_t layer_bit = BIT_ULL(layer->level - 1);
> - const unsigned long access_req = access_request;
> - unsigned long access_bit;
> - bool is_empty;
> + for (int i = 0; i < rule->num_layers; i++) {
> + const struct landlock_layer *l = &rule->layers[i];
>
> - /*
> - * Records in @layer_masks which layer grants access to each requested
> - * access: bit cleared if the related layer grants access.
> - */
> - is_empty = true;
> - for_each_set_bit(access_bit, &access_req, masks_array_size) {
> - if (layer->access & BIT_ULL(access_bit))
> - (*layer_masks)[access_bit] &= ~layer_bit;
> - is_empty = is_empty && !(*layer_masks)[access_bit];
> - }
> - if (is_empty)
> - return true;
> + masks->access[l->level - 1] &= ~l->access;
> }
> - return false;
> +
> + for (int i = 0; i < LANDLOCK_MAX_NUM_LAYERS; i++) {
> + if (masks->access[i])
> + return false;
> + }
> + return true;
> }
>
> typedef access_mask_t
^ 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