Merge tag 'landlock-6.19-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux

Pull landlock fixes from Mickaël Salaün:
 "This fixes TCP handling, tests, documentation, non-audit elided code,
  and minor cosmetic changes"

* tag 'landlock-6.19-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux:
  landlock: Clarify documentation for the IOCTL access right
  selftests/landlock: Properly close a file descriptor
  landlock: Improve the comment for domain_is_scoped
  selftests/landlock: Use scoped_base_variants.h for ptrace_test
  selftests/landlock: Fix missing semicolon
  selftests/landlock: Fix typo in fs_test
  landlock: Optimize stack usage when !CONFIG_AUDIT
  landlock: Fix spelling
  landlock: Clean up hook_ptrace_access_check()
  landlock: Improve erratum documentation
  landlock: Remove useless include
  landlock: Fix wrong type usage
  selftests/landlock: NULL-terminate unix pathname addresses
  selftests/landlock: Remove invalid unix socket bind()
  selftests/landlock: Add missing connect(minimal AF_UNSPEC) test
  selftests/landlock: Fix TCP bind(AF_UNSPEC) test case
  landlock: Fix TCP handling of short AF_UNSPEC addresses
  landlock: Fix formatting
This commit is contained in:
Linus Torvalds
2026-01-18 15:15:47 -08:00
14 changed files with 170 additions and 269 deletions

View File

@@ -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)

View File

@@ -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;
/**

View File

@@ -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.

View File

@@ -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;
}
@@ -1314,7 +1321,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;
}

View File

@@ -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));

View File

@@ -23,7 +23,6 @@
#include <linux/workqueue.h>
#include "access.h"
#include "audit.h"
#include "domain.h"
#include "limits.h"
#include "object.h"

View File

@@ -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);
}
@@ -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,