From 552dbf47a85c3b0eea1d7984ce3794b8d9b20e94 Mon Sep 17 00:00:00 2001 From: Mickaël Salaün Date: Fri, 19 Dec 2025 20:38:51 +0100 Subject: landlock: Fix formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Format with clang-format -i security/landlock/*.[ch] Cc: Christian Brauner Cc: Günther Noack Cc: Mateusz Guzik Fixes: b4dbfd8653b3 ("Coccinelle-based conversion to use ->i_state accessors") Link: https://lore.kernel.org/r/20251219193855.825889-5-mic@digikod.net Reviewed-by: Günther Noack Signed-off-by: Mickaël Salaün --- security/landlock/fs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'security') diff --git a/security/landlock/fs.c b/security/landlock/fs.c index fe794875ad46..e3c3a8a9ac27 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -1314,7 +1314,8 @@ static void hook_sb_delete(struct super_block *const sb) * second call to iput() for the same Landlock object. Also * checks I_NEW because such inode cannot be tied to an object. */ - if (inode_state_read(inode) & (I_FREEING | I_WILL_FREE | I_NEW)) { + if (inode_state_read(inode) & + (I_FREEING | I_WILL_FREE | I_NEW)) { spin_unlock(&inode->i_lock); continue; } -- cgit v1.2.3 From e4d82cbce2258f454634307fdabf33aa46b61ab0 Mon Sep 17 00:00:00 2001 From: Matthieu Buffet Date: Mon, 27 Oct 2025 20:07:26 +0100 Subject: landlock: Fix TCP handling of short AF_UNSPEC addresses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit current_check_access_socket() treats AF_UNSPEC addresses as AF_INET ones, and only later adds special case handling to allow connect(AF_UNSPEC), and on IPv4 sockets bind(AF_UNSPEC+INADDR_ANY). This would be fine except AF_UNSPEC addresses can be as short as a bare AF_UNSPEC sa_family_t field, and nothing more. The AF_INET code path incorrectly enforces a length of sizeof(struct sockaddr_in) instead. Move AF_UNSPEC edge case handling up inside the switch-case, before the address is (potentially incorrectly) treated as AF_INET. Fixes: fff69fb03dde ("landlock: Support network rules with TCP bind and connect") Signed-off-by: Matthieu Buffet Link: https://lore.kernel.org/r/20251027190726.626244-4-matthieu@buffet.re Signed-off-by: Mickaël Salaün --- security/landlock/net.c | 118 +++++++++++++++++++++++++++--------------------- 1 file changed, 67 insertions(+), 51 deletions(-) (limited to 'security') diff --git a/security/landlock/net.c b/security/landlock/net.c index 1f3915a90a80..e6367e30e5b0 100644 --- a/security/landlock/net.c +++ b/security/landlock/net.c @@ -71,6 +71,61 @@ static int current_check_access_socket(struct socket *const sock, switch (address->sa_family) { case AF_UNSPEC: + if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP) { + /* + * Connecting to an address with AF_UNSPEC dissolves + * the TCP association, which have the same effect as + * closing the connection while retaining the socket + * object (i.e., the file descriptor). As for dropping + * privileges, closing connections is always allowed. + * + * For a TCP access control system, this request is + * legitimate. Let the network stack handle potential + * inconsistencies and return -EINVAL if needed. + */ + return 0; + } else if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP) { + /* + * Binding to an AF_UNSPEC address is treated + * differently by IPv4 and IPv6 sockets. The socket's + * family may change under our feet due to + * setsockopt(IPV6_ADDRFORM), but that's ok: we either + * reject entirely or require + * %LANDLOCK_ACCESS_NET_BIND_TCP for the given port, so + * it cannot be used to bypass the policy. + * + * IPv4 sockets map AF_UNSPEC to AF_INET for + * retrocompatibility for bind accesses, only if the + * address is INADDR_ANY (cf. __inet_bind). IPv6 + * sockets always reject it. + * + * Checking the address is required to not wrongfully + * return -EACCES instead of -EAFNOSUPPORT or -EINVAL. + * We could return 0 and let the network stack handle + * these checks, but it is safer to return a proper + * error and test consistency thanks to kselftest. + */ + if (sock->sk->__sk_common.skc_family == AF_INET) { + const struct sockaddr_in *const sockaddr = + (struct sockaddr_in *)address; + + if (addrlen < sizeof(struct sockaddr_in)) + return -EINVAL; + + if (sockaddr->sin_addr.s_addr != + htonl(INADDR_ANY)) + return -EAFNOSUPPORT; + } else { + if (addrlen < SIN6_LEN_RFC2133) + return -EINVAL; + else + return -EAFNOSUPPORT; + } + } else { + WARN_ON_ONCE(1); + } + /* Only for bind(AF_UNSPEC+INADDR_ANY) on IPv4 socket. */ + fallthrough; case AF_INET: { const struct sockaddr_in *addr4; @@ -119,57 +174,18 @@ static int current_check_access_socket(struct socket *const sock, return 0; } - /* Specific AF_UNSPEC handling. */ - if (address->sa_family == AF_UNSPEC) { - /* - * Connecting to an address with AF_UNSPEC dissolves the TCP - * association, which have the same effect as closing the - * connection while retaining the socket object (i.e., the file - * descriptor). As for dropping privileges, closing - * connections is always allowed. - * - * For a TCP access control system, this request is legitimate. - * Let the network stack handle potential inconsistencies and - * return -EINVAL if needed. - */ - if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP) - return 0; - - /* - * For compatibility reason, accept AF_UNSPEC for bind - * accesses (mapped to AF_INET) only if the address is - * INADDR_ANY (cf. __inet_bind). Checking the address is - * required to not wrongfully return -EACCES instead of - * -EAFNOSUPPORT. - * - * We could return 0 and let the network stack handle these - * checks, but it is safer to return a proper error and test - * consistency thanks to kselftest. - */ - if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP) { - /* addrlen has already been checked for AF_UNSPEC. */ - const struct sockaddr_in *const sockaddr = - (struct sockaddr_in *)address; - - if (sock->sk->__sk_common.skc_family != AF_INET) - return -EINVAL; - - if (sockaddr->sin_addr.s_addr != htonl(INADDR_ANY)) - return -EAFNOSUPPORT; - } - } else { - /* - * Checks sa_family consistency to not wrongfully return - * -EACCES instead of -EINVAL. Valid sa_family changes are - * only (from AF_INET or AF_INET6) to AF_UNSPEC. - * - * We could return 0 and let the network stack handle this - * check, but it is safer to return a proper error and test - * consistency thanks to kselftest. - */ - if (address->sa_family != sock->sk->__sk_common.skc_family) - return -EINVAL; - } + /* + * Checks sa_family consistency to not wrongfully return + * -EACCES instead of -EINVAL. Valid sa_family changes are + * only (from AF_INET or AF_INET6) to AF_UNSPEC. + * + * We could return 0 and let the network stack handle this + * check, but it is safer to return a proper error and test + * consistency thanks to kselftest. + */ + if (address->sa_family != sock->sk->__sk_common.skc_family && + address->sa_family != AF_UNSPEC) + return -EINVAL; id.key.data = (__force uintptr_t)port; BUILD_BUG_ON(sizeof(port) > sizeof(id.key.data)); -- cgit v1.2.3 From 29fbfa46e4287c596bdc77e2c599e3a1bbf8bb67 Mon Sep 17 00:00:00 2001 From: Tingmao Wang Date: Sat, 6 Dec 2025 17:11:06 +0000 Subject: landlock: Fix wrong type usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I think, based on my best understanding, that this type is likely a typo (even though in the end both are u16) Signed-off-by: Tingmao Wang Fixes: 2fc80c69df82 ("landlock: Log file-related denials") Reviewed-by: Günther Noack Link: https://lore.kernel.org/r/7339ad7b47f998affd84ca629a334a71f913616d.1765040503.git.m@maowtm.org Signed-off-by: Mickaël Salaün --- security/landlock/audit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/landlock/audit.c b/security/landlock/audit.c index c52d079cdb77..e899995f1fd5 100644 --- a/security/landlock/audit.c +++ b/security/landlock/audit.c @@ -191,7 +191,7 @@ static size_t get_denied_layer(const struct landlock_ruleset *const domain, long youngest_layer = -1; for_each_set_bit(access_bit, &access_req, layer_masks_size) { - const access_mask_t mask = (*layer_masks)[access_bit]; + const layer_mask_t mask = (*layer_masks)[access_bit]; long layer; if (!mask) -- cgit v1.2.3 From 60207df2ebf3b740770aa605173d2d7d19ee66b2 Mon Sep 17 00:00:00 2001 From: Mickaël Salaün Date: Fri, 19 Dec 2025 20:38:47 +0100 Subject: landlock: Remove useless include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove useless audit.h include. Cc: Günther Noack Fixes: 33e65b0d3add ("landlock: Add AUDIT_LANDLOCK_ACCESS and log ptrace denials") Link: https://lore.kernel.org/r/20251219193855.825889-1-mic@digikod.net Reviewed-by: Günther Noack Signed-off-by: Mickaël Salaün --- security/landlock/ruleset.c | 1 - 1 file changed, 1 deletion(-) (limited to 'security') diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c index dfcdc19ea268..0a5b0c76b3f7 100644 --- a/security/landlock/ruleset.c +++ b/security/landlock/ruleset.c @@ -23,7 +23,6 @@ #include #include "access.h" -#include "audit.h" #include "domain.h" #include "limits.h" #include "object.h" -- cgit v1.2.3 From 03a0ff99ef2e4958141f7b4c573722fc8f4b9539 Mon Sep 17 00:00:00 2001 From: Mickaël Salaün Date: Fri, 19 Dec 2025 20:38:48 +0100 Subject: landlock: Improve erratum documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improve description about scoped signal handling. Reported-by: Günther Noack Link: https://lore.kernel.org/r/20251219193855.825889-2-mic@digikod.net Reviewed-by: Günther Noack Signed-off-by: Mickaël Salaün --- security/landlock/errata/abi-6.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/landlock/errata/abi-6.h b/security/landlock/errata/abi-6.h index df7bc0e1fdf4..5113a829f87e 100644 --- a/security/landlock/errata/abi-6.h +++ b/security/landlock/errata/abi-6.h @@ -9,7 +9,7 @@ * This fix addresses an issue where signal scoping was overly restrictive, * preventing sandboxed threads from signaling other threads within the same * process if they belonged to different domains. Because threads are not - * security boundaries, user space might assume that any thread within the same + * security boundaries, user space might assume that all threads within the same * process can send signals between themselves (see :manpage:`nptl(7)` and * :manpage:`libpsx(3)`). Consistent with :manpage:`ptrace(2)` behavior, direct * interaction between threads of the same process should always be allowed. -- cgit v1.2.3 From aa9877d74c07045f712a4ec82105505e69cd5efe Mon Sep 17 00:00:00 2001 From: Mickaël Salaün Date: Fri, 19 Dec 2025 20:38:49 +0100 Subject: landlock: Clean up hook_ptrace_access_check() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make variable's scope minimal in hook_ptrace_access_check(). Cc: Günther Noack Link: https://lore.kernel.org/r/20251219193855.825889-3-mic@digikod.net Reviewed-by: Günther Noack Signed-off-by: Mickaël Salaün --- security/landlock/task.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'security') diff --git a/security/landlock/task.c b/security/landlock/task.c index 2385017418ca..bf4ed15a7f01 100644 --- a/security/landlock/task.c +++ b/security/landlock/task.c @@ -86,7 +86,6 @@ static int hook_ptrace_access_check(struct task_struct *const child, const unsigned int mode) { const struct landlock_cred_security *parent_subject; - const struct landlock_ruleset *child_dom; int err; /* Quick return for non-landlocked tasks. */ @@ -96,7 +95,8 @@ static int hook_ptrace_access_check(struct task_struct *const child, scoped_guard(rcu) { - child_dom = landlock_get_task_domain(child); + const struct landlock_ruleset *const child_dom = + landlock_get_task_domain(child); err = domain_ptrace(parent_subject->domain, child_dom); } -- cgit v1.2.3 From 6548fb521822a5c0a688e423df28b2248a59543b Mon Sep 17 00:00:00 2001 From: Mickaël Salaün Date: Fri, 19 Dec 2025 20:38:50 +0100 Subject: landlock: Fix spelling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Günther Noack Link: https://lore.kernel.org/r/20251219193855.825889-4-mic@digikod.net Reviewed-by: Günther Noack Signed-off-by: Mickaël Salaün --- security/landlock/domain.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/landlock/domain.h b/security/landlock/domain.h index 7fb70b25f85a..621f054c9a2b 100644 --- a/security/landlock/domain.h +++ b/security/landlock/domain.h @@ -97,7 +97,7 @@ struct landlock_hierarchy { */ atomic64_t num_denials; /** - * @id: Landlock domain ID, sets once at domain creation time. + * @id: Landlock domain ID, set once at domain creation time. */ u64 id; /** -- cgit v1.2.3 From 602acfb541195eb35584d7a3fc7d1db676f059bd Mon Sep 17 00:00:00 2001 From: Mickaël Salaün Date: Fri, 19 Dec 2025 15:22:59 +0100 Subject: landlock: Optimize stack usage when !CONFIG_AUDIT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Until now, each landlock_request struct were allocated on the stack, even if not really used, because is_access_to_paths_allowed() unconditionally modified the passed references. Even if the changed landlock_request variables are not used, the compiler is not smart enough to detect this case. To avoid this issue, explicitly disable the related code when CONFIG_AUDIT is not set, which enables elision of log_request_parent* and associated caller's stack variables thanks to dead code elimination. This makes it possible to reduce the stack frame by 32 bytes for the path_link and path_rename hooks, and by 20 bytes for most other filesystem hooks. Here is a summary of scripts/stackdelta before and after this change when CONFIG_AUDIT is disabled: current_check_refer_path 560 320 -240 current_check_access_path 328 184 -144 hook_file_open 328 184 -144 is_access_to_paths_allowed 376 360 -16 Also, add extra pointer checks to be more future-proof. Cc: Günther Noack Reported-by: Tingmao Wang Closes: https://lore.kernel.org/r/eb86863b-53b0-460b-b223-84dd31d765b9@maowtm.org Fixes: 2fc80c69df82 ("landlock: Log file-related denials") Link: https://lore.kernel.org/r/20251219142302.744917-2-mic@digikod.net Reviewed-by: Günther Noack [mic: Improve stack usage measurement accuracy with scripts/stackdelta] Signed-off-by: Mickaël Salaün --- security/landlock/fs.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'security') diff --git a/security/landlock/fs.c b/security/landlock/fs.c index e3c3a8a9ac27..8205673c8b1c 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -939,7 +939,12 @@ jump_up: } path_put(&walker_path); - if (!allowed_parent1) { + /* + * Check CONFIG_AUDIT to enable elision of log_request_parent* and + * associated caller's stack variables thanks to dead code elimination. + */ +#ifdef CONFIG_AUDIT + if (!allowed_parent1 && log_request_parent1) { log_request_parent1->type = LANDLOCK_REQUEST_FS_ACCESS; log_request_parent1->audit.type = LSM_AUDIT_DATA_PATH; log_request_parent1->audit.u.path = *path; @@ -949,7 +954,7 @@ jump_up: ARRAY_SIZE(*layer_masks_parent1); } - if (!allowed_parent2) { + if (!allowed_parent2 && log_request_parent2) { log_request_parent2->type = LANDLOCK_REQUEST_FS_ACCESS; log_request_parent2->audit.type = LSM_AUDIT_DATA_PATH; log_request_parent2->audit.u.path = *path; @@ -958,6 +963,8 @@ jump_up: log_request_parent2->layer_masks_size = ARRAY_SIZE(*layer_masks_parent2); } +#endif /* CONFIG_AUDIT */ + return allowed_parent1 && allowed_parent2; } -- cgit v1.2.3 From ef4536f15224418b327a7b5d5cae07dab042760f Mon Sep 17 00:00:00 2001 From: Tingmao Wang Date: Sun, 28 Dec 2025 01:27:35 +0000 Subject: landlock: Improve the comment for domain_is_scoped MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently it is not obvious what "scoped" mean, and the fact that the function returns true when access should be denied is slightly surprising and in need of documentation. Cc: Tahera Fahimi Signed-off-by: Tingmao Wang Link: https://lore.kernel.org/r/06393bc18aee5bc278df5ef31c64a05b742ebc10.1766885035.git.m@maowtm.org [mic: Fix formatting and improve consistency] Signed-off-by: Mickaël Salaün --- security/landlock/task.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'security') diff --git a/security/landlock/task.c b/security/landlock/task.c index bf4ed15a7f01..833bc0cfe5c9 100644 --- a/security/landlock/task.c +++ b/security/landlock/task.c @@ -166,15 +166,15 @@ static int hook_ptrace_traceme(struct task_struct *const parent) } /** - * domain_is_scoped - Checks if the client domain is scoped in the same - * domain as the server. + * domain_is_scoped - Check if an interaction from a client/sender to a + * server/receiver should be restricted based on scope controls. * * @client: IPC sender domain. * @server: IPC receiver domain. * @scope: The scope restriction criteria. * - * Returns: True if the @client domain is scoped to access the @server, - * unless the @server is also scoped in the same domain as @client. + * Returns: True if @server is in a different domain from @client, and @client + * is scoped to access @server (i.e. access should be denied). */ static bool domain_is_scoped(const struct landlock_ruleset *const client, const struct landlock_ruleset *const server, -- cgit v1.2.3