diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-13 15:42:19 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-13 15:42:19 -0700 |
| commit | b8f82cb0d84d00c04cdbdce42f67df71b8507e8b (patch) | |
| tree | 23d83d0ecb4fbff65459886c7a4e19008a820c49 /security | |
| parent | b206a6fb9a105be198cf2dc435ffa4ad7c75ddc2 (diff) | |
| parent | 3457a5ccacd34fdd5ebd3a4745e721b5a1239690 (diff) | |
Merge tag 'landlock-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux
Pull Landlock update from Mickaël Salaün:
"This adds a new Landlock access right for pathname UNIX domain sockets
thanks to a new LSM hook, and a few fixes"
* tag 'landlock-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux: (23 commits)
landlock: Document fallocate(2) as another truncation corner case
landlock: Document FS access right for pathname UNIX sockets
selftests/landlock: Simplify ruleset creation and enforcement in fs_test
selftests/landlock: Check that coredump sockets stay unrestricted
selftests/landlock: Audit test for LANDLOCK_ACCESS_FS_RESOLVE_UNIX
selftests/landlock: Test LANDLOCK_ACCESS_FS_RESOLVE_UNIX
selftests/landlock: Replace access_fs_16 with ACCESS_ALL in fs_test
samples/landlock: Add support for named UNIX domain socket restrictions
landlock: Clarify BUILD_BUG_ON check in scoping logic
landlock: Control pathname UNIX domain socket resolution by path
landlock: Use mem_is_zero() in is_layer_masks_allowed()
lsm: Add LSM hook security_unix_find
landlock: Fix kernel-doc warning for pointer-to-array parameters
landlock: Fix formatting in tsync.c
landlock: Improve kernel-doc "Return:" section consistency
landlock: Add missing kernel-doc "Return:" sections
selftests/landlock: Fix format warning for __u64 in net_test
selftests/landlock: Skip stale records in audit_match_record()
selftests/landlock: Drain stale audit records on init
selftests/landlock: Fix socket file descriptor leaks in audit helpers
...
Diffstat (limited to 'security')
| -rw-r--r-- | security/landlock/access.h | 4 | ||||
| -rw-r--r-- | security/landlock/audit.c | 1 | ||||
| -rw-r--r-- | security/landlock/cred.c | 6 | ||||
| -rw-r--r-- | security/landlock/cred.h | 2 | ||||
| -rw-r--r-- | security/landlock/domain.c | 6 | ||||
| -rw-r--r-- | security/landlock/fs.c | 163 | ||||
| -rw-r--r-- | security/landlock/id.c | 2 | ||||
| -rw-r--r-- | security/landlock/limits.h | 2 | ||||
| -rw-r--r-- | security/landlock/ruleset.c | 14 | ||||
| -rw-r--r-- | security/landlock/ruleset.h | 2 | ||||
| -rw-r--r-- | security/landlock/syscalls.c | 33 | ||||
| -rw-r--r-- | security/landlock/task.c | 22 | ||||
| -rw-r--r-- | security/landlock/tsync.c | 124 | ||||
| -rw-r--r-- | security/security.c | 20 |
14 files changed, 284 insertions, 117 deletions
diff --git a/security/landlock/access.h b/security/landlock/access.h index 42c95747d7bd..c19d5bc13944 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); @@ -50,7 +50,7 @@ struct access_masks { access_mask_t fs : LANDLOCK_NUM_ACCESS_FS; access_mask_t net : LANDLOCK_NUM_ACCESS_NET; access_mask_t scope : LANDLOCK_NUM_SCOPE; -}; +} __packed __aligned(sizeof(u32)); union access_masks_all { struct access_masks masks; diff --git a/security/landlock/audit.c b/security/landlock/audit.c index 60ff217ab95b..8d0edf94037d 100644 --- a/security/landlock/audit.c +++ b/security/landlock/audit.c @@ -37,6 +37,7 @@ static const char *const fs_access_strings[] = { [BIT_INDEX(LANDLOCK_ACCESS_FS_REFER)] = "fs.refer", [BIT_INDEX(LANDLOCK_ACCESS_FS_TRUNCATE)] = "fs.truncate", [BIT_INDEX(LANDLOCK_ACCESS_FS_IOCTL_DEV)] = "fs.ioctl_dev", + [BIT_INDEX(LANDLOCK_ACCESS_FS_RESOLVE_UNIX)] = "fs.resolve_unix", }; static_assert(ARRAY_SIZE(fs_access_strings) == LANDLOCK_NUM_ACCESS_FS); diff --git a/security/landlock/cred.c b/security/landlock/cred.c index 0cb3edde4d18..cc419de75cd6 100644 --- a/security/landlock/cred.c +++ b/security/landlock/cred.c @@ -22,10 +22,8 @@ static void hook_cred_transfer(struct cred *const new, const struct landlock_cred_security *const old_llcred = landlock_cred(old); - if (old_llcred->domain) { - landlock_get_ruleset(old_llcred->domain); - *landlock_cred(new) = *old_llcred; - } + landlock_get_ruleset(old_llcred->domain); + *landlock_cred(new) = *old_llcred; } static int hook_cred_prepare(struct cred *const new, diff --git a/security/landlock/cred.h b/security/landlock/cred.h index c10a06727eb1..f287c56b5fd4 100644 --- a/security/landlock/cred.h +++ b/security/landlock/cred.h @@ -115,7 +115,7 @@ static inline bool landlocked(const struct task_struct *const task) * @handle_layer: returned youngest layer handling a subset of @masks. Not set * if the function returns NULL. * - * Returns: landlock_cred(@cred) if any access rights specified in @masks is + * Return: landlock_cred(@cred) if any access rights specified in @masks is * handled, or NULL otherwise. */ static inline const struct landlock_cred_security * diff --git a/security/landlock/domain.c b/security/landlock/domain.c index f0d83f43afa1..06b6bd845060 100644 --- a/security/landlock/domain.c +++ b/security/landlock/domain.c @@ -34,7 +34,7 @@ * @exe_size: Returned size of @exe_str (including the trailing null * character), if any. * - * Returns: A pointer to an allocated buffer where @exe_str point to, %NULL if + * Return: A pointer to an allocated buffer where @exe_str point to, %NULL if * there is no executable path, or an error otherwise. */ static const void *get_current_exe(const char **const exe_str, @@ -73,7 +73,7 @@ static const void *get_current_exe(const char **const exe_str, } /* - * Returns: A newly allocated object describing a domain, or an error + * Return: A newly allocated object describing a domain, or an error * otherwise. */ static struct landlock_details *get_current_details(void) @@ -114,6 +114,8 @@ static struct landlock_details *get_current_details(void) * restriction. The subjective credentials must not be in an overridden state. * * @hierarchy->parent and @hierarchy->usage should already be set. + * + * Return: 0 on success, -errno on failure. */ int landlock_init_hierarchy_log(struct landlock_hierarchy *const hierarchy) { diff --git a/security/landlock/fs.c b/security/landlock/fs.c index e764470f588c..c1ecfe239032 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -27,6 +27,7 @@ #include <linux/lsm_hooks.h> #include <linux/mount.h> #include <linux/namei.h> +#include <linux/net.h> #include <linux/path.h> #include <linux/pid.h> #include <linux/rcupdate.h> @@ -36,6 +37,7 @@ #include <linux/types.h> #include <linux/wait_bit.h> #include <linux/workqueue.h> +#include <net/af_unix.h> #include <uapi/linux/fiemap.h> #include <uapi/linux/landlock.h> @@ -119,8 +121,8 @@ static const struct landlock_object_underops landlock_fs_underops = { * Any new IOCTL commands that are implemented in fs/ioctl.c's do_vfs_ioctl() * should be considered for inclusion here. * - * Returns: true if the IOCTL @cmd can not be restricted with Landlock for - * device files. + * Return: True if the IOCTL @cmd can not be restricted with Landlock for + * device files, false otherwise. */ static __attribute_const__ bool is_masked_device_ioctl(const unsigned int cmd) { @@ -314,7 +316,8 @@ retry: LANDLOCK_ACCESS_FS_WRITE_FILE | \ LANDLOCK_ACCESS_FS_READ_FILE | \ LANDLOCK_ACCESS_FS_TRUNCATE | \ - LANDLOCK_ACCESS_FS_IOCTL_DEV) + LANDLOCK_ACCESS_FS_IOCTL_DEV | \ + LANDLOCK_ACCESS_FS_RESOLVE_UNIX) /* clang-format on */ /* @@ -428,10 +431,10 @@ static bool may_refer(const struct layer_access_masks *const src_parent, * Check that a destination file hierarchy has more restrictions than a source * file hierarchy. This is only used for link and rename actions. * - * Returns: true if child1 may be moved from parent1 to parent2 without - * increasing its access rights. If child2 is set, an additional condition is + * Return: True if child1 may be moved from parent1 to parent2 without + * increasing its access rights (if child2 is set, an additional condition is * that child2 may be used from parent2 to parent1 without increasing its access - * rights. + * rights), false otherwise. */ static bool no_more_access(const struct layer_access_masks *const parent1, const struct layer_access_masks *const child1, @@ -564,7 +567,7 @@ static void test_no_more_access(struct kunit *const test) static bool is_layer_masks_allowed(const struct layer_access_masks *masks) { - return !memchr_inv(&masks->access, 0, sizeof(masks->access)); + return mem_is_zero(&masks->access, sizeof(masks->access)); } /* @@ -734,9 +737,7 @@ static void test_is_eacces_with_write(struct kunit *const test) * checks that the collected accesses and the remaining ones are enough to * allow the request. * - * Returns: - * - true if the access request is granted; - * - false otherwise. + * Return: True if the access request is granted, false otherwise. */ static bool is_access_to_paths_allowed(const struct landlock_ruleset *const domain, @@ -1022,9 +1023,8 @@ static access_mask_t maybe_remove(const struct dentry *const dentry) * only handles walking on the same mount point and only checks one set of * accesses. * - * Returns: - * - true if all the domain access rights are allowed for @dir; - * - false if the walk reached @mnt_root. + * Return: True if all the domain access rights are allowed for @dir, false if + * the walk reached @mnt_root. */ static bool collect_domain_accesses(const struct landlock_ruleset *const domain, const struct dentry *const mnt_root, @@ -1120,10 +1120,9 @@ static bool collect_domain_accesses(const struct landlock_ruleset *const domain, * ephemeral matrices take some space on the stack, which limits the number of * layers to a deemed reasonable number: 16. * - * Returns: - * - 0 if access is allowed; - * - -EXDEV if @old_dentry would inherit new access rights from @new_dir; - * - -EACCES if file removal or creation is denied. + * Return: 0 if access is allowed, -EXDEV if @old_dentry would inherit new + * access rights from @new_dir, or -EACCES if file removal or creation is + * denied. */ static int current_check_refer_path(struct dentry *const old_dentry, const struct path *const new_dir, @@ -1561,6 +1560,133 @@ static int hook_path_truncate(const struct path *const path) return current_check_access_path(path, LANDLOCK_ACCESS_FS_TRUNCATE); } +/** + * unmask_scoped_access - Remove access right bits in @masks in all layers + * where @client and @server have the same domain + * + * This does the same as domain_is_scoped(), but unmasks bits in @masks. + * It can not return early as domain_is_scoped() does. + * + * A scoped access for a given access right bit is allowed iff, for all layer + * depths where the access bit is set, the client and server domain are the + * same. This function clears the access rights @access in @masks at all layer + * depths where the client and server domain are the same, so that, when they + * are all cleared, the access is allowed. + * + * @client: Client domain + * @server: Server domain + * @masks: Layer access masks to unmask + * @access: Access bits that control scoping + */ +static void unmask_scoped_access(const struct landlock_ruleset *const client, + const struct landlock_ruleset *const server, + struct layer_access_masks *const masks, + const access_mask_t access) +{ + int client_layer, server_layer; + const struct landlock_hierarchy *client_walker, *server_walker; + + /* This should not happen. */ + if (WARN_ON_ONCE(!client)) + return; + + /* Server has no Landlock domain; nothing to clear. */ + if (!server) + return; + + /* + * client_layer must be able to represent all numbers from + * LANDLOCK_MAX_NUM_LAYERS - 1 to -1 for the loop below to terminate. + * (It must be large enough, and it must be signed.) + */ + BUILD_BUG_ON(!is_signed_type(typeof(client_layer))); + BUILD_BUG_ON(LANDLOCK_MAX_NUM_LAYERS - 1 > + type_max(typeof(client_layer))); + + client_layer = client->num_layers - 1; + client_walker = client->hierarchy; + server_layer = server->num_layers - 1; + server_walker = server->hierarchy; + + /* + * Clears the access bits at all layers where the client domain is the + * same as the server domain. We start the walk at min(client_layer, + * server_layer). The layer bits until there can not be cleared because + * either the client or the server domain is missing. + */ + for (; client_layer > server_layer; client_layer--) + client_walker = client_walker->parent; + + for (; server_layer > client_layer; server_layer--) + server_walker = server_walker->parent; + + for (; client_layer >= 0; client_layer--) { + if (masks->access[client_layer] & access && + client_walker == server_walker) + masks->access[client_layer] &= ~access; + + client_walker = client_walker->parent; + server_walker = server_walker->parent; + } +} + +static int hook_unix_find(const struct path *const path, struct sock *other, + int flags) +{ + const struct landlock_ruleset *dom_other; + const struct landlock_cred_security *subject; + struct layer_access_masks layer_masks; + struct landlock_request request = {}; + static const struct access_masks fs_resolve_unix = { + .fs = LANDLOCK_ACCESS_FS_RESOLVE_UNIX, + }; + + /* Lookup for the purpose of saving coredumps is OK. */ + if (unlikely(flags & SOCK_COREDUMP)) + return 0; + + subject = landlock_get_applicable_subject(current_cred(), + fs_resolve_unix, NULL); + + if (!subject) + return 0; + + /* + * Ignoring return value: that the domains apply was already checked in + * landlock_get_applicable_subject() above. + */ + landlock_init_layer_masks(subject->domain, fs_resolve_unix.fs, + &layer_masks, LANDLOCK_KEY_INODE); + + /* Checks the layers in which we are connecting within the same domain. */ + unix_state_lock(other); + if (unlikely(sock_flag(other, SOCK_DEAD) || !other->sk_socket || + !other->sk_socket->file)) { + unix_state_unlock(other); + /* + * We rely on the caller to catch the (non-reversible) SOCK_DEAD + * condition and retry the lookup. If we returned an error + * here, the lookup would not get retried. + */ + return 0; + } + dom_other = landlock_cred(other->sk_socket->file->f_cred)->domain; + + /* Access to the same (or a lower) domain is always allowed. */ + unmask_scoped_access(subject->domain, dom_other, &layer_masks, + fs_resolve_unix.fs); + unix_state_unlock(other); + + /* Checks the connections to allow-listed paths. */ + if (is_access_to_paths_allowed(subject->domain, path, + fs_resolve_unix.fs, &layer_masks, + &request, NULL, 0, NULL, NULL, NULL)) + return 0; + + landlock_log_denial(subject, &request); + return -EACCES; +} + /* File hooks */ /** @@ -1568,7 +1694,7 @@ static int hook_path_truncate(const struct path *const path) * * @file: File being opened. * - * Returns the access rights that are required for opening the given file, + * Return: The access rights that are required for opening the given file, * depending on the file type and open mode. */ static access_mask_t @@ -1838,6 +1964,7 @@ static struct security_hook_list landlock_hooks[] __ro_after_init = { LSM_HOOK_INIT(path_unlink, hook_path_unlink), LSM_HOOK_INIT(path_rmdir, hook_path_rmdir), LSM_HOOK_INIT(path_truncate, hook_path_truncate), + LSM_HOOK_INIT(unix_find, hook_unix_find), LSM_HOOK_INIT(file_alloc_security, hook_file_alloc_security), LSM_HOOK_INIT(file_open, hook_file_open), diff --git a/security/landlock/id.c b/security/landlock/id.c index 838c3ed7bb82..6c8769777fdc 100644 --- a/security/landlock/id.c +++ b/security/landlock/id.c @@ -258,7 +258,7 @@ static void test_range2_rand16(struct kunit *const test) * * @number_of_ids: Number of IDs to hold. Must be greater than one. * - * Returns: The first ID in the range. + * Return: The first ID in the range. */ u64 landlock_get_id_range(size_t number_of_ids) { diff --git a/security/landlock/limits.h b/security/landlock/limits.h index eb584f47288d..b454ad73b15e 100644 --- a/security/landlock/limits.h +++ b/security/landlock/limits.h @@ -19,7 +19,7 @@ #define LANDLOCK_MAX_NUM_LAYERS 16 #define LANDLOCK_MAX_NUM_RULES U32_MAX -#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_IOCTL_DEV +#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_RESOLVE_UNIX #define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1) #define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS) diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c index 73018dc8d6c7..181df7736bb9 100644 --- a/security/landlock/ruleset.c +++ b/security/landlock/ruleset.c @@ -107,7 +107,7 @@ static bool is_object_pointer(const enum landlock_key_type key_type) static struct landlock_rule * create_rule(const struct landlock_id id, - const struct landlock_layer (*const layers)[], const u32 num_layers, + const struct landlock_layer (*layers)[], const u32 num_layers, const struct landlock_layer *const new_layer) { struct landlock_rule *new_rule; @@ -201,10 +201,12 @@ static void build_check_ruleset(void) * When merging a ruleset in a domain, or copying a domain, @layers will be * added to @ruleset as new constraints, similarly to a boolean AND between * access rights. + * + * Return: 0 on success, -errno on failure. */ static int insert_rule(struct landlock_ruleset *const ruleset, const struct landlock_id id, - const struct landlock_layer (*const layers)[], + const struct landlock_layer (*layers)[], const size_t num_layers) { struct rb_node **walker_node; @@ -530,8 +532,8 @@ void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset) * The current task is requesting to be restricted. The subjective credentials * must not be in an overridden state. cf. landlock_init_hierarchy_log(). * - * Returns the intersection of @parent and @ruleset, or returns @parent if - * @ruleset is empty, or returns a duplicate of @ruleset if @parent is empty. + * Return: A new domain merging @parent and @ruleset on success, or ERR_PTR() + * on failure. If @parent is NULL, the new domain duplicates @ruleset. */ struct landlock_ruleset * landlock_merge_ruleset(struct landlock_ruleset *const parent, @@ -622,7 +624,7 @@ landlock_find_rule(const struct landlock_ruleset *const ruleset, * @rule: A rule that grants a set of access rights for each layer * @masks: A matrix of unfulfilled access rights for each layer * - * Returns true if the request is allowed (i.e. the access rights granted all + * Return: True if the request is allowed (i.e. the access rights granted all * remaining unfulfilled access rights and masks has no leftover set bits). */ bool landlock_unmask_layers(const struct landlock_rule *const rule, @@ -672,7 +674,7 @@ get_access_mask_t(const struct landlock_ruleset *const ruleset, * @masks: Layer access masks to populate. * @key_type: The key type to switch between access masks of different types. * - * Returns: An access mask where each access right bit is set which is handled + * Return: An access mask where each access right bit is set which is handled * in any of the active layers in @domain. */ access_mask_t diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h index 9d6dc632684c..889f4b30301a 100644 --- a/security/landlock/ruleset.h +++ b/security/landlock/ruleset.h @@ -224,7 +224,7 @@ static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset) * * @domain: Landlock ruleset (used as a domain) * - * Returns: an access_masks result of the OR of all the domain's access masks. + * Return: An access_masks result of the OR of all the domain's access masks. */ static inline struct access_masks landlock_union_access_masks(const struct landlock_ruleset *const domain) diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c index 0d66a68677b7..accfd2e5a0cd 100644 --- a/security/landlock/syscalls.c +++ b/security/landlock/syscalls.c @@ -60,6 +60,8 @@ static bool is_initialized(void) * @ksize_min: Minimal required size to be copied. * @src: User space pointer or NULL. * @usize: (Alleged) size of the data pointed to by @src. + * + * Return: 0 on success, -errno on failure. */ static __always_inline int copy_min_struct_from_user(void *const dst, const size_t ksize, @@ -164,7 +166,7 @@ static const struct file_operations ruleset_fops = { * If the change involves a fix that requires userspace awareness, also update * the errata documentation in Documentation/userspace-api/landlock.rst . */ -const int landlock_abi_version = 8; +const int landlock_abi_version = 9; /** * sys_landlock_create_ruleset - Create a new ruleset @@ -178,16 +180,19 @@ const int landlock_abi_version = 8; * - %LANDLOCK_CREATE_RULESET_VERSION * - %LANDLOCK_CREATE_RULESET_ERRATA * - * This system call enables to create a new Landlock ruleset, and returns the - * related file descriptor on success. + * This system call enables to create a new Landlock ruleset. * * If %LANDLOCK_CREATE_RULESET_VERSION or %LANDLOCK_CREATE_RULESET_ERRATA is * set, then @attr must be NULL and @size must be 0. * - * Possible returned errors are: + * Return: The ruleset file descriptor on success, the Landlock ABI version if + * %LANDLOCK_CREATE_RULESET_VERSION is set, the errata value if + * %LANDLOCK_CREATE_RULESET_ERRATA is set, or -errno on failure. Possible + * returned errors are: * * - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time; - * - %EINVAL: unknown @flags, or unknown access, or unknown scope, or too small @size; + * - %EINVAL: unknown @flags, or unknown access, or unknown scope, or too small + * @size; * - %E2BIG: @attr or @size inconsistencies; * - %EFAULT: @attr or @size inconsistencies; * - %ENOMSG: empty &landlock_ruleset_attr.handled_access_fs. @@ -398,7 +403,7 @@ static int add_rule_net_port(struct landlock_ruleset *ruleset, * This system call enables to define a new rule and add it to an existing * ruleset. * - * Possible returned errors are: + * Return: 0 on success, or -errno on failure. Possible returned errors are: * * - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time; * - %EAFNOSUPPORT: @rule_type is %LANDLOCK_RULE_NET_PORT but TCP/IP is not @@ -464,7 +469,7 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd, * namespace or is running with no_new_privs. This avoids scenarios where * unprivileged tasks can affect the behavior of privileged children. * - * Possible returned errors are: + * Return: 0 on success, or -errno on failure. Possible returned errors are: * * - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time; * - %EINVAL: @flags contains an unknown bit. @@ -512,10 +517,13 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32, /* * It is allowed to set LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF with - * -1 as ruleset_fd, but no other flag must be set. + * -1 as ruleset_fd, optionally combined with + * LANDLOCK_RESTRICT_SELF_TSYNC to propagate this configuration to all + * threads. No other flag must be set. */ if (!(ruleset_fd == -1 && - flags == LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF)) { + (flags & ~LANDLOCK_RESTRICT_SELF_TSYNC) == + LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF)) { /* Gets and checks the ruleset. */ ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_READ); if (IS_ERR(ruleset)) @@ -537,9 +545,10 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32, /* * The only case when a ruleset may not be set is if - * LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF is set and ruleset_fd is -1. - * We could optimize this case by not calling commit_creds() if this flag - * was already set, but it is not worth the complexity. + * LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF is set (optionally with + * LANDLOCK_RESTRICT_SELF_TSYNC) and ruleset_fd is -1. We could + * optimize this case by not calling commit_creds() if this flag was + * already set, but it is not worth the complexity. */ if (ruleset) { /* diff --git a/security/landlock/task.c b/security/landlock/task.c index 833bc0cfe5c9..6d46042132ce 100644 --- a/security/landlock/task.c +++ b/security/landlock/task.c @@ -37,6 +37,9 @@ * * Checks if the @parent domain is less or equal to (i.e. an ancestor, which * means a subset of) the @child domain. + * + * Return: True if @parent is an ancestor of or equal to @child, false + * otherwise. */ static bool domain_scope_le(const struct landlock_ruleset *const parent, const struct landlock_ruleset *const child) @@ -79,8 +82,7 @@ static int domain_ptrace(const struct landlock_ruleset *const parent, * If the current task has Landlock rules, then the child must have at least * the same rules. Else denied. * - * Determines whether a process may access another, returning 0 if permission - * granted, -errno if denied. + * Return: 0 if permission is granted, -errno if denied. */ static int hook_ptrace_access_check(struct task_struct *const child, const unsigned int mode) @@ -129,8 +131,7 @@ static int hook_ptrace_access_check(struct task_struct *const child, * If the parent has Landlock rules, then the current task must have the same * or more rules. Else denied. * - * Determines whether the nominated task is permitted to trace the current - * process, returning 0 if permission is granted, -errno if denied. + * Return: 0 if permission is granted, -errno if denied. */ static int hook_ptrace_traceme(struct task_struct *const parent) { @@ -173,8 +174,8 @@ static int hook_ptrace_traceme(struct task_struct *const parent) * @server: IPC receiver domain. * @scope: The scope restriction criteria. * - * Returns: True if @server is in a different domain from @client, and @client - * is scoped to access @server (i.e. access should be denied). + * Return: True if @server is in a different domain from @client and @client + * is scoped to access @server (i.e. access should be denied), false otherwise. */ static bool domain_is_scoped(const struct landlock_ruleset *const client, const struct landlock_ruleset *const server, @@ -190,10 +191,13 @@ static bool domain_is_scoped(const struct landlock_ruleset *const client, client_layer = client->num_layers - 1; client_walker = client->hierarchy; /* - * client_layer must be a signed integer with greater capacity - * than client->num_layers to ensure the following loop stops. + * client_layer must be able to represent all numbers from + * LANDLOCK_MAX_NUM_LAYERS - 1 to -1 for the loop below to terminate. + * (It must be large enough, and it must be signed.) */ - BUILD_BUG_ON(sizeof(client_layer) > sizeof(client->num_layers)); + BUILD_BUG_ON(!is_signed_type(typeof(client_layer))); + BUILD_BUG_ON(LANDLOCK_MAX_NUM_LAYERS - 1 > + type_max(typeof(client_layer))); server_layer = server ? (server->num_layers - 1) : -1; server_walker = server ? server->hierarchy : NULL; diff --git a/security/landlock/tsync.c b/security/landlock/tsync.c index 4d4427ba8d93..c5730bbd9ed3 100644 --- a/security/landlock/tsync.c +++ b/security/landlock/tsync.c @@ -85,12 +85,14 @@ static void restrict_one_thread(struct tsync_shared_context *ctx) /* * Switch out old_cred with new_cred, if possible. * - * In the common case, where all threads initially point to the same - * struct cred, this optimization avoids creating separate redundant - * credentials objects for each, which would all have the same contents. + * In the common case, where all threads initially point to the + * same struct cred, this optimization avoids creating separate + * redundant credentials objects for each, which would all have + * the same contents. * - * Note: We are intentionally dropping the const qualifier here, because - * it is required by commit_creds() and abort_creds(). + * Note: We are intentionally dropping the const qualifier + * here, because it is required by commit_creds() and + * abort_creds(). */ cred = (struct cred *)get_cred(ctx->new_cred); } else { @@ -101,8 +103,8 @@ static void restrict_one_thread(struct tsync_shared_context *ctx) atomic_set(&ctx->preparation_error, -ENOMEM); /* - * Even on error, we need to adhere to the protocol and coordinate - * with concurrently running invocations. + * Even on error, we need to adhere to the protocol and + * coordinate with concurrently running invocations. */ if (atomic_dec_return(&ctx->num_preparing) == 0) complete_all(&ctx->all_prepared); @@ -135,9 +137,9 @@ static void restrict_one_thread(struct tsync_shared_context *ctx) } /* - * Make sure that all sibling tasks fulfill the no_new_privs prerequisite. - * (This is in line with Seccomp's SECCOMP_FILTER_FLAG_TSYNC logic in - * kernel/seccomp.c) + * Make sure that all sibling tasks fulfill the no_new_privs + * prerequisite. (This is in line with Seccomp's + * SECCOMP_FILTER_FLAG_TSYNC logic in kernel/seccomp.c) */ if (ctx->set_no_new_privs) task_set_no_new_privs(current); @@ -183,10 +185,8 @@ struct tsync_works { * capacity. This can legitimately happen if new threads get started after we * grew the capacity. * - * Returns: - * A pointer to the preallocated context struct, with task filled in. - * - * NULL, if we ran out of preallocated context structs. + * Return: A pointer to the preallocated context struct with task filled in, or + * NULL if preallocated context structs ran out. */ static struct tsync_work *tsync_works_provide(struct tsync_works *s, struct task_struct *task) @@ -223,16 +223,17 @@ static void tsync_works_trim(struct tsync_works *s) ctx = s->works[s->size - 1]; /* - * For consistency, remove the task from ctx so that it does not look like - * we handed it a task_work. + * For consistency, remove the task from ctx so that it does not look + * like we handed it a task_work. */ put_task_struct(ctx->task); *ctx = (typeof(*ctx)){}; /* - * Cancel the tsync_works_provide() change to recycle the reserved memory - * for the next thread, if any. This also ensures that cancel_tsync_works() - * and tsync_works_release() do not see any NULL task pointers. + * Cancel the tsync_works_provide() change to recycle the reserved + * memory for the next thread, if any. This also ensures that + * cancel_tsync_works() and tsync_works_release() do not see any NULL + * task pointers. */ s->size--; } @@ -243,11 +244,8 @@ static void tsync_works_trim(struct tsync_works *s) * On a successful return, the subsequent n calls to tsync_works_provide() are * guaranteed to succeed. (size + n <= capacity) * - * Returns: - * -ENOMEM if the (re)allocation fails - - * 0 if the allocation succeeds, partially succeeds, or no reallocation - * was needed + * Return: 0 if sufficient space for n more elements could be provided, -ENOMEM + * on allocation errors, -EOVERFLOW in case of integer overflow. */ static int tsync_works_grow_by(struct tsync_works *s, size_t n, gfp_t flags) { @@ -363,8 +361,8 @@ static size_t count_additional_threads(const struct tsync_works *works) * For each added task_work, atomically increments shared_ctx->num_preparing and * shared_ctx->num_unfinished. * - * Returns: - * true, if at least one eligible sibling thread was found + * Return: True if at least one eligible sibling thread was found, false + * otherwise. */ static bool schedule_task_work(struct tsync_works *works, struct tsync_shared_context *shared_ctx) @@ -393,17 +391,17 @@ static bool schedule_task_work(struct tsync_works *works, continue; /* - * We found a sibling thread that is not doing its task_work yet, and - * which might spawn new threads before our task work runs, so we need - * at least one more round in the outer loop. + * We found a sibling thread that is not doing its task_work + * yet, and which might spawn new threads before our task work + * runs, so we need at least one more round in the outer loop. */ found_more_threads = true; ctx = tsync_works_provide(works, thread); if (!ctx) { /* - * We ran out of preallocated contexts -- we need to try again with - * this thread at a later time! + * We ran out of preallocated contexts -- we need to + * try again with this thread at a later time! * found_more_threads is already true at this point. */ break; @@ -418,10 +416,10 @@ static bool schedule_task_work(struct tsync_works *works, err = task_work_add(thread, &ctx->work, TWA_SIGNAL); if (unlikely(err)) { /* - * task_work_add() only fails if the task is about to exit. We - * checked that earlier, but it can happen as a race. Resume - * without setting an error, as the task is probably gone in the - * next loop iteration. + * task_work_add() only fails if the task is about to + * exit. We checked that earlier, but it can happen as + * a race. Resume without setting an error, as the + * task is probably gone in the next loop iteration. */ tsync_works_trim(works); @@ -512,24 +510,25 @@ int landlock_restrict_sibling_threads(const struct cred *old_cred, * After this barrier is reached, it's safe to read * shared_ctx.preparation_error. * - * 4) reads shared_ctx.preparation_error and then either does commit_creds() - * or abort_creds(). + * 4) reads shared_ctx.preparation_error and then either does + * commit_creds() or abort_creds(). * * 5) signals that it's done altogether (barrier synchronization * "all_finished") * - * Unlike seccomp, which modifies sibling tasks directly, we do not need to - * acquire the cred_guard_mutex and sighand->siglock: + * Unlike seccomp, which modifies sibling tasks directly, we do not + * need to acquire the cred_guard_mutex and sighand->siglock: * - * - As in our case, all threads are themselves exchanging their own struct - * cred through the credentials API, no locks are needed for that. + * - As in our case, all threads are themselves exchanging their own + * struct cred through the credentials API, no locks are needed for + * that. * - Our for_each_thread() loops are protected by RCU. - * - We do not acquire a lock to keep the list of sibling threads stable - * between our for_each_thread loops. If the list of available sibling - * threads changes between these for_each_thread loops, we make up for - * that by continuing to look for threads until they are all discovered - * and have entered their task_work, where they are unable to spawn new - * threads. + * - We do not acquire a lock to keep the list of sibling threads + * stable between our for_each_thread loops. If the list of + * available sibling threads changes between these for_each_thread + * loops, we make up for that by continuing to look for threads until + * they are all discovered and have entered their task_work, where + * they are unable to spawn new threads. */ do { /* In RCU read-lock, count the threads we need. */ @@ -546,31 +545,36 @@ int landlock_restrict_sibling_threads(const struct cred *old_cred, } /* - * The "all_prepared" barrier is used locally to the loop body, this use - * of for_each_thread(). We can reset it on each loop iteration because - * all previous loop iterations are done with it already. + * The "all_prepared" barrier is used locally to the loop body, + * this use of for_each_thread(). We can reset it on each loop + * iteration because all previous loop iterations are done with + * it already. * - * num_preparing is initialized to 1 so that the counter can not go to 0 - * and mark the completion as done before all task works are registered. - * We decrement it at the end of the loop body. + * num_preparing is initialized to 1 so that the counter can + * not go to 0 and mark the completion as done before all task + * works are registered. We decrement it at the end of the + * loop body. */ atomic_set(&shared_ctx.num_preparing, 1); reinit_completion(&shared_ctx.all_prepared); /* - * In RCU read-lock, schedule task work on newly discovered sibling - * tasks. + * In RCU read-lock, schedule task work on newly discovered + * sibling tasks. */ found_more_threads = schedule_task_work(&works, &shared_ctx); /* - * Decrement num_preparing for current, to undo that we initialized it - * to 1 a few lines above. + * Decrement num_preparing for current, to undo that we + * initialized it to 1 a few lines above. */ if (atomic_dec_return(&shared_ctx.num_preparing) > 0) { if (wait_for_completion_interruptible( &shared_ctx.all_prepared)) { - /* In case of interruption, we need to retry the system call. */ + /* + * In case of interruption, we need to retry + * the system call. + */ atomic_set(&shared_ctx.preparation_error, -ERESTARTNOINTR); @@ -603,8 +607,8 @@ int landlock_restrict_sibling_threads(const struct cred *old_cred, complete_all(&shared_ctx.ready_to_commit); /* - * Decrement num_unfinished for current, to undo that we initialized it to 1 - * at the beginning. + * Decrement num_unfinished for current, to undo that we initialized it + * to 1 at the beginning. */ if (atomic_dec_return(&shared_ctx.num_unfinished) > 0) wait_for_completion(&shared_ctx.all_finished); diff --git a/security/security.c b/security/security.c index 048560ef6a1a..4e999f023651 100644 --- a/security/security.c +++ b/security/security.c @@ -4834,6 +4834,26 @@ int security_mptcp_add_subflow(struct sock *sk, struct sock *ssk) #endif /* CONFIG_SECURITY_NETWORK */ +#if defined(CONFIG_SECURITY_NETWORK) && defined(CONFIG_SECURITY_PATH) +/** + * security_unix_find() - Check if a named AF_UNIX socket can connect + * @path: path of the socket being connected to + * @other: peer sock + * @flags: flags associated with the socket + * + * This hook is called to check permissions before connecting to a named + * AF_UNIX socket. The caller does not hold any locks on @other. + * + * Return: Returns 0 if permission is granted. + */ +int security_unix_find(const struct path *path, struct sock *other, int flags) +{ + return call_int_hook(unix_find, path, other, flags); +} +EXPORT_SYMBOL(security_unix_find); + +#endif /* CONFIG_SECURITY_NETWORK && CONFIG_SECURITY_PATH */ + #ifdef CONFIG_SECURITY_INFINIBAND /** * security_ib_pkey_access() - Check if access to an IB pkey is allowed |
