* [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
* Re: [RFC PATCH 1/2] landlock: access_mask_subset() helper
From: Mickaël Salaün @ 2026-01-09 16:06 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-5-gnoack3000@gmail.com>
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.
> +}
> +
> /*
> * Check that a destination file hierarchy has more restrictions than a source
> * file hierarchy. This is only used for link and rename actions.
> @@ -1696,7 +1705,7 @@ static int hook_file_open(struct file *const file)
> ARRAY_SIZE(layer_masks));
> #endif /* CONFIG_AUDIT */
>
> - if ((open_access_request & allowed_access) == open_access_request)
> + if (access_mask_subset(open_access_request, allowed_access))
> return 0;
>
> /* Sets access to reflect the actual request. */
> --
> 2.52.0
>
>
^ permalink raw reply
* Re: [RFC PATCH 0/2] landlock: Refactor layer masks
From: Mickaël Salaün @ 2026-01-09 15:59 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: <20251230.d4bf391b98c5@gnoack.org>
On Tue, Dec 30, 2025 at 11:48:21AM +0100, Günther Noack wrote:
> On Tue, Dec 30, 2025 at 11:39:17AM +0100, Günther Noack wrote:
> > Tentative results with and without this patch set show that the
> > hypothesis likely holds true. The benchmark I used exercises a "worst
> > case" scenario that attempts to be bottlenecked on the affected code:
> > constructs a large number of nested directories, with one "path
> > beneath" rule each and then tries to open the innermost directory many
> > times. The benchmark is intentionally unrealistic to amplify the
> > amount of time used for the path walk logic and forces Landlock to
> > walk the full path (eventually failing the open syscall). (I'll send
> > the benchmark program in a reply to this mail for full transparency.)
>
> Please see the benchmark program below.
Thanks for the investigation!
>
> To compile it, use:
>
> cc -o benchmark_worsecase benchmark_worsecase.c
It would be useful to clean up a bit this benchmark and add it to the
selftests' Landlock directory (see seccomp_benchmark.c).
>
> Source code:
>
> ```
> #define _GNU_SOURCE
> #include <err.h>
> #include <fcntl.h>
> #include <linux/landlock.h>
> #include <stdbool.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> #include <sys/prctl.h>
> #include <sys/stat.h>
> #include <sys/syscall.h>
> #include <sys/times.h>
> #include <time.h>
> #include <unistd.h>
>
> /* Flags */
> bool use_landlock = true;
> size_t num_iterations = 100000;
> size_t num_subdirs = 10000;
>
> void usage() { puts("Usage: benchmark_worstcase [-no-landlock]"); }
>
> /*
> * Build a deep directory, enforce Landlock and return the FD to the
> * deepest dir. On any failure, exit the process with an error.
> */
> int build_directory(size_t depth) {
> const char *path = "d"; /* directory name */
>
> if (use_landlock) {
> int abi = syscall(SYS_landlock_create_ruleset, NULL, 0,
> LANDLOCK_CREATE_RULESET_VERSION);
> if (abi < 7)
> err(1, "Landlock ABI too low: got %d, wanted 7+", abi);
> }
>
> int ruleset_fd = -1;
> if (use_landlock) {
> if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0)
> err(1, "prctl");
>
> struct landlock_ruleset_attr attr = {
> .handled_access_fs = 0xffff, /* All FS access rights as of 2025-12 */
> };
> ruleset_fd = syscall(SYS_landlock_create_ruleset, &attr, sizeof(attr), 0U);
> if (ruleset_fd < 0)
> err(1, "landlock_create_ruleset");
> }
>
> int current = open(".", O_PATH);
> if (current < 0)
> err(1, "open(.)");
>
> while (depth--) {
> if (use_landlock) {
> struct landlock_path_beneath_attr attr = {
> .allowed_access = LANDLOCK_ACCESS_FS_IOCTL_DEV,
> .parent_fd = current,
> };
> if (syscall(SYS_landlock_add_rule, ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
> &attr, 0) < 0)
> err(1, "landlock_add_rule");
> }
>
> if (mkdirat(current, path, 0700) < 0)
> err(1, "mkdirat(%s)", path);
>
> int previous = current;
> current = openat(current, path, O_PATH);
> if (current < 0)
> err(1, "open(%s)", path);
>
> close(previous);
> }
>
> if (use_landlock) {
> if (syscall(SYS_landlock_restrict_self, ruleset_fd, 0) < 0)
> err(1, "landlock_restrict_self");
> }
>
> close(ruleset_fd);
> return current;
> }
>
> int main(int argc, char *argv[]) {
> for (int i = 1; i < argc; i++) {
> if (!strcmp(argv[i], "-no-landlock")) {
> use_landlock = false;
> } else if (!strcmp(argv[i], "-d")) {
> i++;
> if (i < argc)
> err(1, "expected number of subdirs after -d");
> num_subdirs = atoi(argv[i]);
> } else if (!strcmp(argv[i], "-n")) {
> i++;
> if (i < argc)
> err(1, "expected number of iterations after -n");
> num_iterations = atoi(argv[i]);
> } else {
> usage();
> errx(1, "unknown argument: %s", argv[i]);
> }
> }
>
> printf("*** Benchmark ***\n");
> printf("%zu dirs, %zu iterations, %s landlock\n", num_subdirs,
> num_iterations, use_landlock ? "with" : "without");
>
> struct tms start_time;
> if (times(&start_time) == -1)
> err(1, "times");
>
> int current = build_directory(num_subdirs);
>
> for (int i = 0; i < num_iterations; i++) {
> int fd = openat(current, ".", O_DIRECTORY);
> if (fd != -1)
> errx(1, "openat succeeded, expected error");
> }
>
> struct tms end_time;
> if (times(&end_time) == -1)
> err(1, "times");
>
> printf("*** Benchmark concluded ***\n");
> printf("System: %ld clocks\n", end_time.tms_stime - start_time.tms_stime);
> printf("User : %ld clocks\n", end_time.tms_utime - start_time.tms_utime);
> printf("Clocks per second: %d\n", CLOCKS_PER_SEC);
>
> close(current);
> }
> ```
>
^ permalink raw reply
* Re: [RFC PATCH 0/5] landlock: Pathname-based UNIX connect() control
From: Mickaël Salaün @ 2026-01-09 15:25 UTC (permalink / raw)
To: Demi Marie Obenour
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: <a5850eb9-cee3-412d-ac20-c47e3161fdd9@gmail.com>
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.
^ permalink raw reply
* Re: [RFC PATCH 0/5] landlock: Pathname-based UNIX connect() control
From: Mickaël Salaün @ 2026-01-09 15:20 UTC (permalink / raw)
To: Günther Noack
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.d4c29e22f15f@gnoack.org>
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:
> > On Thu, Jan 01, 2026 at 02:40:57PM +0100, Günther Noack wrote:
> > > ## 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]).
> > >
> > > These patches are built on Justin Suess's patch which adds the LSM
> > > hook:
> > > https://lore.kernel.org/all/20251231213314.2979118-1-utilityemal77@gmail.com/
> >
> > 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
>
> 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).
>
> (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.
^ permalink raw reply
* Re: [RFC PATCH 0/5] landlock: Pathname-based UNIX connect() control
From: Günther Noack @ 2026-01-09 14:41 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.she1eg0Ohl4u@digikod.net>
On Fri, Jan 09, 2026 at 11:37:12AM +0100, Mickaël Salaün wrote:
> On Thu, Jan 01, 2026 at 02:40:57PM +0100, Günther Noack wrote:
> > ## 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]).
> >
> > These patches are built on Justin Suess's patch which adds the LSM
> > hook:
> > https://lore.kernel.org/all/20251231213314.2979118-1-utilityemal77@gmail.com/
>
> 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).)
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?)
(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?
–Günther
^ permalink raw reply
* Re: [PATCH v5 10/36] locking/mutex: Support Clang's context analysis
From: Steven Rostedt @ 2026-01-09 13:07 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Marco Elver, 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, 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, 9 Jan 2026 07:02:49 +0100
Christoph Hellwig <hch@lst.de> 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.
Right. If tooling can't handle a simple pattern of initializing a lock than
taking it, that's a hard show stopper of adding that tooling.
-- Steve
^ permalink raw reply
* Re: [PATCH v2 0/6] Landlock: Implement scope control for pathname Unix sockets
From: Mickaël Salaün @ 2026-01-09 12:01 UTC (permalink / raw)
To: Demi Marie Obenour
Cc: Günther Noack, Tingmao Wang, Günther Noack, Alyssa Ross,
Jann Horn, Tahera Fahimi, Justin Suess, linux-security-module
In-Reply-To: <dc9b99b6-14a1-45b6-a23f-0b24143dac58@gmail.com>
On Wed, Dec 31, 2025 at 11:54:27AM -0500, Demi Marie Obenour wrote:
> On 12/30/25 18:16, Günther Noack wrote:
> > Hello!
> >
> > Thanks for sending this patch!
> >
> > On Tue, Dec 30, 2025 at 05:20:18PM +0000, Tingmao Wang wrote:
> >> Changes in v2:
> >> Fix grammar in doc, rebased on mic/next, and extracted common code from
> >> hook_unix_stream_connect and hook_unix_may_send into a separate
> >> function.
> >>
> >> The rest is the same as the v1 cover letter:
> >>
> >> This patch series extend the existing abstract Unix socket scoping to
> >> pathname (i.e. normal file-based) sockets as well, by adding a new scope
> >> bit LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET that works the same as
> >> LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET, except that restricts pathname Unix
> >> sockets. This means that a sandboxed process with this scope enabled will
> >> not be able to connect to Unix sockets created outside the sandbox via the
> >> filesystem.
> >>
> >> There is a future plan [1] for allowing specific sockets based on FS
> >> hierarchy, but this series is only determining access based on domain
> >> parent-child relationship. There is currently no way to allow specific
> >> (outside the Landlock domain) Unix sockets, and none of the existing
> >> Landlock filesystem controls apply to socket connect().
> >>
> >> With this series, we can now properly protect against things like the the
> >> following while only relying on Landlock:
> >>
> >> (running under tmux)
> >> root@6-19-0-rc1-dev-00023-g68f0b276cbeb ~# LL_FS_RO=/ LL_FS_RW= ./sandboxer bash
> >> Executing the sandboxed command...
> >> root@6-19-0-rc1-dev-00023-g68f0b276cbeb:/# cat /tmp/hi
> >> cat: /tmp/hi: No such file or directory
> >> root@6-19-0-rc1-dev-00023-g68f0b276cbeb:/# tmux new-window 'echo hi > /tmp/hi'
> >> root@6-19-0-rc1-dev-00023-g68f0b276cbeb:/# cat /tmp/hi
> >> hi
> >>
> >> The above but with Unix socket scoping enabled (both pathname and abstract
> >> sockets) - the sandboxed shell can now no longer talk to tmux due to the
> >> socket being created from outside the Landlock sandbox:
> >>
> >> (running under tmux)
> >> root@6-19-0-rc1-dev-00023-g68f0b276cbeb ~# LL_FS_RO=/ LL_FS_RW= LL_SCOPED=u:a ./sandboxer bash
> >> Executing the sandboxed command...
> >> root@6-19-0-rc1-dev-00023-g68f0b276cbeb:/# cat /tmp/hi
> >> cat: /tmp/hi: No such file or directory
> >> root@6-19-0-rc1-dev-00023-g68f0b276cbeb:/# tmux new-window 'echo hi > /tmp/hi'
> >> error connecting to /tmp/tmux-0/default (Operation not permitted)
> >> root@6-19-0-rc1-dev-00023-g68f0b276cbeb:/# cat /tmp/hi
> >> cat: /tmp/hi: No such file or directory
> >>
> >> Tmux is just one example. In a standard systemd session, `systemd-run
> >> --user` can also be used (--user will run the command in the user's
> >> session, without requiring any root privileges), and likely a lot more if
> >> running in a desktop environment with many popular applications. This
> >> change therefore makes it possible to create sandboxes without relying on
> >> additional mechanisms like seccomp to protect against such issues.
> >>
> >> These kind of issues was originally discussed on here (I took the idea for
> >> systemd-run from Demi):
> >> https://spectrum-os.org/lists/archives/spectrum-devel/00256266-26db-40cf-8f5b-f7c7064084c2@gmail.com/
> >
> > What is unclear to me from the examples and the description is: Why is
> > the boundary between allowed and denied connection targets drawn at
> > the border of the Landlock domain (the "scope") and why don't we solve
> > this with the file-system-based approach described in [1]?
> >
> > **Do we have existing use cases where a service is both offered and
> > connected to all from within the same Landlock domain, and where the
> > process enforcing the policy does not control the child process enough
> > so that it would be possible to allow-list it with a
> > LANDLOCK_ACCESS_FS_CONNECT_UNIX rule?**
Yes, with the sandboxer use case. It's similar to a container that
doesn't know all programs that could be run in it. We should be able to
create sandboxes that don't assume (nor restrict) internal IPCs (or any
kind of direct process I/O).
Landlock should make it possible to scope any kind of IPC. It's a
mental model which is easy to understand and that should be enforced by
default. Of course, we need some ways to add exceptions to this
deny-by-default policy, and that's why we also need the FS-based control
mechanism.
> >
> > If we do not have such a use case, it seems that the planned FS-based
> > control mechanism from [1] would do the same job? Long term, we might
> > be better off if we only have only one control -- as we have discussed
> > in [2], having two of these might mean that they interact in
> > unconventional and possibly confusing ways.
>
> I agree with this.
Both approaches are complementary/orthogonal and make sense long term
too. This might be the first time we can restrict the same operations,
but I'm pretty sure it will not be the last, and it's OK. Handled FS
access and scoped restrictions are two ways to describe a part of the
security policy. Having different ways makes the interface much simpler
than a generic one that would have to take into account all potential
future access controls.
One thing to keep in mind is that UAPI doesn't have to map 1:1 to the
kernel implementation, but the UAPI is stable and future proof, whereas
the kernel implementation can change a lot.
The ruleset's handled fields serve two purposes: define what should be
denied by default, and define which type of rules are valid
(compatibility). The ruleset's scoped field serve similar purposes but
it also implies implicit rules (i.e. communications with processes
inside the sandbox are allowed). Without the soped field, we would have
to create dedicated handled field per type (i.e. scope's bit) and
dedicated rule type for the related handled field, which would make the
interface more generic but also more complex, for something which is not
needed.
In a nutshell, in the case of the FS-based and scope-based unix socket
control, we should see one kind of restrictions (e.g. connect to unix
socket), which can accept two types of rules: (explicit) file path, or
(implicit) peer's scope. Access should be granted as long as a rule
matches, whatever its type.
This rationale should be explained in a commit message.
>
> > Apart from that, there are some other weaker hints that make me
> > slightly critical of this patch set:
> >
> > * We discussed the idea that a FS-based path_beneath rule would act
> > implicitly also as an exception for
> > LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET, in [2] - one possible way to
> > interpret this is that the gravity of the system's logic pulls us
> > back towards a FS-based control, and we would have to swim less
> > against the stream if we integrated the Unix connect() control in
> > that way?
>
> I agree with this as well. Having FS-based controls apply consistently
> to all types of inodes is what I would expect.
I'm not sure to understand, but I hope my explanation above will answer
this question.
>
> > * I am struggling to convince myself that we can tell users to
> > restrict LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET as a default, as we are
> > currently doing it with the other "scoped" and file system controls.
> > (The scoped signals are OK because killing out-of-domain processes
> > is clearly bad. The scoped abstract sockets are usually OK because
> > most processes do not need that feature.)
> >
> > But there are legitimate Unix services that are still needed by
> > unprivileged processes and which are designed to be safe to use.
> > For instance, systemd exposes a user database lookup service over
> > varlink [3], which can be accessed from arbitrary glibc programs
> > through a NSS module. Using this is incompatible with
> > LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET as long as we do not have the
> > FS-based control and the surprising implicit permission through a
> > path_beneath rule as discussed in [2].
> >
> > (Another example is the X11 socket, to which the same reasoning
> > should apply, I think, and which is also used by a large number of
> > programs.)
Yes, restricting a sandbox too much may not work for any possible use
cases. Some use cases would need the FS-based control before using the
scope-based one, and that's OK. Some use cases would not need the
FS-based control, and the scope-based one would be enough (e.g.
sandboxed archive manager).
> I think making FS-based controls on by default is the least surprising
> option for users. It's what I suspect most programs intend, and it
> would stop a lot of unexpected sandbox escapes.
Nothing in Landlock is on by default for compatibility reasons.
>
> Without FS-based controls, scoping is also necessary to prevent
> sandbox escapes. In my opinion, it's better to block access to
> unprivileged services than to allow connecting to any service.
Scoped-based control should be enforced in most cases, but FS-based
control might be a requirement/dependency. In any case, a sandbox
should not break legitimate use cases.
>
> X11 is a trivial sandbox escape by taking over the user's GUI.
> A sandboxed program accessing it indicates a misdesign.
I would say a *fully* sandboxed program for a desktop use case using X11
(instead of Wayland), which is not possible with Landlock *alone* yet.
Desktop environment is the worse case scenario for sandboxing, for a lot
of different reasons... but there are a lot of other use cases for
sandboxing.
> The NSS module
> is not needed by programs that do not do user database resolution,
> which I suspect includes almost all sandboxed programs.
>
> > I agree that the bug [1] has been asleep for a bit too long, and we
> > should probably pick this up soon. As we have not heard back from
> > Ryan after our last inquiry, IMHO I think it would be fair to take it
> > over.
>
> I definitely support this!
Yes, thanks for working on this.
>
> > Apologies for the difficult feedback - I do not mean to get in the way
> > of your enthusiasm here, but I would like to make sure that we don't
> > implement this as a stop-gap measure just because the other bug [1]
> > seemed more difficult and/or stuck in a github issue interaction.
> > Let's rather unblock that bug, if that is the case. :)
Receiving criticisms is part of the review process, which is a sign of
healthy development. I understand your concerns and it's good discuss
about them to make sure we are going in the right direction.
> >
> > As usual, I fully acknowledge that I might well be wrong and might
> > have missed some of the underlying reasons, in which case I will
> > happily be corrected and change my mind. :)
In any case, if someone doesn't understand something, it probably means
that this should be explained better somewhere. Such design artifacts
will be useful for future developments too.
> >
> > [1] https://github.com/landlock-lsm/linux/issues/36
> > [2] https://github.com/landlock-lsm/linux/issues/36#issuecomment-3699749541
> > [3] https://systemd.io/USER_GROUP_API/
> >
> > Have a good start into the new year!
> > –Günther
> --
> Sincerely,
> Demi Marie Obenour (she/her/hers)
^ permalink raw reply
* Re: [RFC PATCH 0/5] landlock: Pathname-based UNIX connect() control
From: Demi Marie Obenour @ 2026-01-09 11:33 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: <20260108.uoD0aesh8Uth@digikod.net>
[-- Attachment #1.1.1: Type: text/plain, Size: 2828 bytes --]
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?
--
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: [RFC PATCH 1/5] landlock/selftests: add a missing close(srv_fd) call
From: Mickaël Salaün @ 2026-01-09 10:49 UTC (permalink / raw)
To: Günther Noack
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.Thoot8ooWai7@digikod.net>
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().
> > }
> >
> > /* clang-format off */
> > --
> > 2.52.0
> >
> >
^ permalink raw reply
* Re: [RFC PATCH 1/5] landlock/selftests: add a missing close(srv_fd) call
From: Mickaël Salaün @ 2026-01-09 10:41 UTC (permalink / raw)
To: Günther Noack
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: <20260101134102.25938-2-gnoack3000@gmail.com>
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));
> }
>
> /* clang-format off */
> --
> 2.52.0
>
>
^ permalink raw reply
* Re: [RFC PATCH 0/5] landlock: Pathname-based UNIX connect() control
From: Mickaël Salaün @ 2026-01-09 10:37 UTC (permalink / raw)
To: Günther Noack
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: <20260101134102.25938-1-gnoack3000@gmail.com>
On Thu, Jan 01, 2026 at 02:40:57PM +0100, Günther Noack wrote:
> Happy New Year!
Happy New Year!
>
> This patch set introduces a file-system-based Landlock restriction
> mechanism for connecting to Unix sockets.
Thanks for this patch series, this is an important feature for
sandboxing.
>
> ## 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]).
>
> These patches are built on Justin Suess's patch which adds the LSM
> hook:
> https://lore.kernel.org/all/20251231213314.2979118-1-utilityemal77@gmail.com/
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/
^ 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