* Re: [PATCH RESEND v4 0/7] futex: Create set_robust_list2
From: André Almeida @ 2025-06-18 16:39 UTC (permalink / raw)
To: Sebastian Andrzej Siewior
Cc: Thomas Gleixner, Ingo Molnar, Peter Zijlstra, Darren Hart,
Davidlohr Bueso, Shuah Khan, Arnd Bergmann, Waiman Long,
linux-kernel, linux-kselftest, linux-api, kernel-dev
In-Reply-To: <20250618070833._qeCcHLx@linutronix.de>
Hi Sebastian,
Thanks for the feedback!
Em 18/06/2025 04:08, Sebastian Andrzej Siewior escreveu:
> On 2025-06-17 15:34:17 [-0300], André Almeida wrote:
>> This patch adds a new robust_list() syscall. The current syscall
>> can't be expanded to cover the following use case, so a new one is
>> needed. This new syscall allows users to set multiple robust lists per
>> process and to have either 32bit or 64bit pointers in the list.
>
> Thank you for the reminder. It was on my list, it slipped. Two
> questions:
> - there was a bot warning for v3 but this v4 is a RESEND. It the warning
> addressed in any way?
>
Ops, I forgot to address them. I will do it for v5.
> - You say 64bit x86-64 does not have the problem due the compat syscall.
> Arm64 has this problem. New arm64 do not provide arm32 facility. You
> introduce the syscall here. Why not introduce the compat syscall
> instead? I'm sorry if this has been answered somewhere below but this
> was one question I had while I initially skimmed over the patches.
>
The main target for this new syscall is Arm64, that can't handle 32
pointers in the current syscall, so this new interface allows the robust
list mechanism to know if it needs to do 64 or 32 bit pointer arithmetic
operations to walk in the list.
Introducing a compat syscall won't fix this, giving that it only works
in x86-64. We need an entry point for Arm64 that can handle 32 bit pointers.
I hope that it's clear now, let me know if you have more questions :)
> Sebastian
^ permalink raw reply
* Re: [PATCH RESEND v4 0/7] futex: Create set_robust_list2
From: Sebastian Andrzej Siewior @ 2025-06-18 16:56 UTC (permalink / raw)
To: André Almeida
Cc: Thomas Gleixner, Ingo Molnar, Peter Zijlstra, Darren Hart,
Davidlohr Bueso, Shuah Khan, Arnd Bergmann, Waiman Long,
linux-kernel, linux-kselftest, linux-api, kernel-dev
In-Reply-To: <fd2b5bb2-590a-47ee-8e56-965a1d09b2fc@igalia.com>
On 2025-06-18 13:39:46 [-0300], André Almeida wrote:
>
> Ops, I forgot to address them. I will do it for v5.
>
> > - You say 64bit x86-64 does not have the problem due the compat syscall.
> > Arm64 has this problem. New arm64 do not provide arm32 facility. You
> > introduce the syscall here. Why not introduce the compat syscall
> > instead? I'm sorry if this has been answered somewhere below but this
> > was one question I had while I initially skimmed over the patches.
> >
>
> The main target for this new syscall is Arm64, that can't handle 32 pointers
> in the current syscall, so this new interface allows the robust list
> mechanism to know if it needs to do 64 or 32 bit pointer arithmetic
> operations to walk in the list.
>
> Introducing a compat syscall won't fix this, giving that it only works in
> x86-64. We need an entry point for Arm64 that can handle 32 bit pointers.
I would need to dig into details to figure out why it won't work for
arm64 and works only for x86-64.
There is the set_robust_list syscall as compat which sets
::compat_robust_list. And non-compat sets ::robust_list. The 32bit
application on 64bit kernel should set ::compat_robust_list which what
your syscall provides.
That is why I don't understand the need for it so far. Maybe I am
missing a detail.
We have other architectures with 64 bit kernel and a possible 32bit
userland such as mips, s390 or powerpc which would have the same issue
then. Or there is something special about arm64 in this case which makes
it unique.
Sebastian
^ permalink raw reply
* Re: [PATCH RESEND v4 0/7] futex: Create set_robust_list2
From: Arnd Bergmann @ 2025-06-18 17:03 UTC (permalink / raw)
To: Sebastian Andrzej Siewior, André Almeida
Cc: Thomas Gleixner, Ingo Molnar, Peter Zijlstra, Darren Hart,
Davidlohr Bueso, shuah, Waiman Long, linux-kernel,
linux-kselftest, linux-api, kernel-dev
In-Reply-To: <20250618165641.bKu1_p0P@linutronix.de>
On Wed, Jun 18, 2025, at 18:56, Sebastian Andrzej Siewior wrote:
> On 2025-06-18 13:39:46 [-0300], André Almeida wrote:
>>
>> Ops, I forgot to address them. I will do it for v5.
>>
>> > - You say 64bit x86-64 does not have the problem due the compat syscall.
>> > Arm64 has this problem. New arm64 do not provide arm32 facility. You
>> > introduce the syscall here. Why not introduce the compat syscall
>> > instead? I'm sorry if this has been answered somewhere below but this
>> > was one question I had while I initially skimmed over the patches.
>> >
>>
>> The main target for this new syscall is Arm64, that can't handle 32 pointers
>> in the current syscall, so this new interface allows the robust list
>> mechanism to know if it needs to do 64 or 32 bit pointer arithmetic
>> operations to walk in the list.
>>
>> Introducing a compat syscall won't fix this, giving that it only works in
>> x86-64. We need an entry point for Arm64 that can handle 32 bit pointers.
>
> I would need to dig into details to figure out why it won't work for
> arm64 and works only for x86-64.
> There is the set_robust_list syscall as compat which sets
> ::compat_robust_list. And non-compat sets ::robust_list. The 32bit
> application on 64bit kernel should set ::compat_robust_list which what
> your syscall provides.
> That is why I don't understand the need for it so far. Maybe I am
> missing a detail.
> We have other architectures with 64 bit kernel and a possible 32bit
> userland such as mips, s390 or powerpc which would have the same issue
> then. Or there is something special about arm64 in this case which makes
> it unique.
x86 is the special case here, since it allows applications to
call both the 32-bit (compat) and 64-bit syscalls directly on
a 64-bit kernel. I think MIPS may do that as well, but the other
architectures only allow a process to call syscalls for its native
ABI, so the only way to call a compat syscall is from a 32-bit
task. On Arm and RISC-V it's also common to have CPUs that cannot
run 32-bit tasks at all, so even running your x86-32 emulator as
an arm32 or rv32 task won't work.
Arnd
^ permalink raw reply
* Re: [RFC 00/19] Kernel API Specification Framework
From: Kees Cook @ 2025-06-18 21:29 UTC (permalink / raw)
To: Sasha Levin; +Cc: linux-kernel, linux-api, workflows, tools
In-Reply-To: <20250614134858.790460-1-sashal@kernel.org>
On Sat, Jun 14, 2025 at 09:48:39AM -0400, Sasha Levin wrote:
> This patch series introduces a framework for formally specifying kernel
> APIs, addressing the long-standing challenge of maintaining stable
> interfaces between the kernel and user-space programs. As outlined in
> previous discussions about kernel ABI stability, the lack of
> machine-readable API specifications has led to inadvertent breakages and
> inconsistent validation across system calls and IOCTLs.
I'd much prefer this be more attached to the code in question, otherwise
we've go two things to update when changes happen. (Well, 3, since
kern-doc already needs updating too.)
Can't we collect error codes programmatically through control flow
analysis? Argument mapping is already present in the SYSCALL macros,
etc. Let's not repeat this info.
-Kees
--
Kees Cook
^ permalink raw reply
* Re: [RFC 00/19] Kernel API Specification Framework
From: Sasha Levin @ 2025-06-19 0:22 UTC (permalink / raw)
To: Kees Cook; +Cc: linux-kernel, linux-api, workflows, tools
In-Reply-To: <202506181428.3D086D2@keescook>
On Wed, Jun 18, 2025 at 02:29:37PM -0700, Kees Cook wrote:
>On Sat, Jun 14, 2025 at 09:48:39AM -0400, Sasha Levin wrote:
>> This patch series introduces a framework for formally specifying kernel
>> APIs, addressing the long-standing challenge of maintaining stable
>> interfaces between the kernel and user-space programs. As outlined in
>> previous discussions about kernel ABI stability, the lack of
>> machine-readable API specifications has led to inadvertent breakages and
>> inconsistent validation across system calls and IOCTLs.
>
>I'd much prefer this be more attached to the code in question, otherwise
>we've go two things to update when changes happen. (Well, 3, since
>kern-doc already needs updating too.)
>
>Can't we collect error codes programmatically through control flow
>analysis? Argument mapping is already present in the SYSCALL macros,
I'm not sure what you meant with in the control flow analysis part: we
have code to verify that the return value from the macro matches one of
the ones defined in the spec.
>etc. Let's not repeat this info.
I tried to come up with a way to get rid of the SYSCALL_DEFINEx() macro
right after the spec. I agree that it's duplication, but my macro-foo is
too weak to get rid of that SYSCALL_DEFINE() call.
Suggestions more than welcome here: I suspect that this might require a
bigger change in the code, but I'm still trying to figure it out.
--
Thanks,
Sasha
^ permalink raw reply
* [PATCH v2 0/3] lsm: introduce lsm_config_self_policy() and lsm_config_system_policy() syscalls
From: Maxime Bélair @ 2025-06-19 18:15 UTC (permalink / raw)
To: linux-security-module
Cc: john.johansen, paul, jmorris, serge, mic, kees,
stephen.smalley.work, casey, takedakn, penguin-kernel, song,
linux-api, apparmor, linux-kernel, Maxime Bélair
This patchset introduces two new syscalls: lsm_config_self_policy(),
lsm_config_system_policy() and the associated Linux Security Module hooks
security_lsm_config_*_policy(), providing a unified interface for loading
and managing LSM policies. These syscalls complement the existing per‑LSM
pseudo‑filesystem mechanism and work even when those filesystems are not
mounted or available.
With these new syscalls, users and administrators may lock down access to
the pseudo‑filesystem yet still manage LSM policies. Two tightly-scoped
entry points then replace the many file operations exposed by those
filesystems, significantly reducing the attack surface. This is
particularly useful in containers or processes already confined by
Landlock, where these pseudo‑filesystems are typically unavailable.
Because they provide a logical and unified interface, these syscalls are
simpler to use than several heterogeneous pseudo‑filesystems and avoid
edge cases such as partially loaded policies. They also eliminates VFS
overhead, yielding performance gains notably when many policies are
loaded, for instance at boot time.
This initial implementation is intentionally minimal to limit the scope
of changes. Currently, only policy loading is supported, and only
AppArmor registers this LSM hook. However, any LSM can adopt this
interface, and future patches could extend this syscall to support more
operations, such as replacing, removing, or querying loaded policies.
Landlock already provides three Landlock‑specific syscalls (e.g.
landlock_add_rule()) to restrict ambient rights for sets of processes
without touching any pseudo-filesystem. lsm_config_*_policy() generalizes
that approach to the entire LSM layer, so any module can choose to
support either or both of these syscalls, and expose its policy
operations through a uniform interface and reap the advantages outlined
above.
This patchset is available at [1], a minimal user space example
showing how to use lsm_config_system_policy with AppArmor is at [2] and a
performance benchmark of both syscalls is available at [3].
[1] https://github.com/emixam16/linux/tree/lsm_syscall
[2] https://gitlab.com/emixam16/apparmor/tree/lsm_syscall
[3] https://gitlab.com/-/snippets/4864908
--
Changes in v2
- Split lsm_manage_policy() into two distinct syscalls:
lsm_config_self_policy() and lsm_config_system_policy()
- The LSM hook now calls only the appropriate LSM (and not all LSMs)
- Add a configuration variable to limit the buffer size of these
syscalls
- AppArmor now allows stacking policies through lsm_config_self_policy()
and loading policies in any namespace through
lsm_config_system_policy()
--
Maxime Bélair (3):
Wire up lsm_config_self_policy and lsm_config_system_policy syscalls
lsm: introduce security_lsm_config_*_policy hooks
AppArmor: add support for lsm_config_self_policy and
lsm_config_system_policy
arch/alpha/kernel/syscalls/syscall.tbl | 2 +
arch/arm/tools/syscall.tbl | 2 +
arch/m68k/kernel/syscalls/syscall.tbl | 2 +
arch/microblaze/kernel/syscalls/syscall.tbl | 2 +
arch/mips/kernel/syscalls/syscall_n32.tbl | 2 +
arch/mips/kernel/syscalls/syscall_n64.tbl | 2 +
arch/mips/kernel/syscalls/syscall_o32.tbl | 2 +
arch/parisc/kernel/syscalls/syscall.tbl | 2 +
arch/powerpc/kernel/syscalls/syscall.tbl | 2 +
arch/s390/kernel/syscalls/syscall.tbl | 2 +
arch/sh/kernel/syscalls/syscall.tbl | 2 +
arch/sparc/kernel/syscalls/syscall.tbl | 2 +
arch/x86/entry/syscalls/syscall_32.tbl | 2 +
arch/x86/entry/syscalls/syscall_64.tbl | 2 +
arch/xtensa/kernel/syscalls/syscall.tbl | 2 +
include/linux/lsm_hook_defs.h | 4 ++
include/linux/security.h | 16 +++++
include/linux/syscalls.h | 5 ++
include/uapi/asm-generic/unistd.h | 6 +-
include/uapi/linux/lsm.h | 8 +++
kernel/sys_ni.c | 2 +
security/Kconfig | 22 ++++++
security/apparmor/apparmorfs.c | 31 +++++++++
security/apparmor/include/apparmorfs.h | 3 +
security/apparmor/lsm.c | 63 +++++++++++++++++
security/lsm_syscalls.c | 25 +++++++
security/security.c | 69 +++++++++++++++++++
tools/include/uapi/asm-generic/unistd.h | 6 +-
.../arch/x86/entry/syscalls/syscall_64.tbl | 2 +
29 files changed, 290 insertions(+), 2 deletions(-)
base-commit: 9c32cda43eb78f78c73aee4aa344b777714e259b
--
2.48.1
^ permalink raw reply
* [PATCH v2 2/3] lsm: introduce security_lsm_config_*_policy hooks
From: Maxime Bélair @ 2025-06-19 18:15 UTC (permalink / raw)
To: linux-security-module
Cc: john.johansen, paul, jmorris, serge, mic, kees,
stephen.smalley.work, casey, takedakn, penguin-kernel, song,
linux-api, apparmor, linux-kernel, Maxime Bélair
In-Reply-To: <20250619181600.478038-1-maxime.belair@canonical.com>
Define two new LSM hooks: security_lsm_config_self_policy and
security_lsm_config_system_policy and wire them into the corresponding
lsm_config_*_policy() syscalls so that LSMs can register a unified
interface for policy management. This initial, minimal implementation
only supports the LSM_POLICY_LOAD operation to limit changes.
Signed-off-by: Maxime Bélair <maxime.belair@canonical.com>
---
include/linux/lsm_hook_defs.h | 4 ++
include/linux/security.h | 16 ++++++++
include/uapi/linux/lsm.h | 8 ++++
security/Kconfig | 22 +++++++++++
security/lsm_syscalls.c | 17 ++++++++-
security/security.c | 69 +++++++++++++++++++++++++++++++++++
6 files changed, 134 insertions(+), 2 deletions(-)
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index bf3bbac4e02a..fca490444643 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -464,3 +464,7 @@ LSM_HOOK(int, 0, bdev_alloc_security, struct block_device *bdev)
LSM_HOOK(void, LSM_RET_VOID, bdev_free_security, struct block_device *bdev)
LSM_HOOK(int, 0, bdev_setintegrity, struct block_device *bdev,
enum lsm_integrity_type type, const void *value, size_t size)
+LSM_HOOK(int, -EINVAL, lsm_config_self_policy, u32 lsm_id, u32 op,
+ void __user *buf, size_t size, u32 flags)
+LSM_HOOK(int, -EINVAL, lsm_config_system_policy, u32 lsm_id, u32 op,
+ void __user *buf, size_t size, u32 flags)
diff --git a/include/linux/security.h b/include/linux/security.h
index cc9b54d95d22..c2158f2656fd 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -581,6 +581,11 @@ void security_bdev_free(struct block_device *bdev);
int security_bdev_setintegrity(struct block_device *bdev,
enum lsm_integrity_type type, const void *value,
size_t size);
+int security_lsm_config_self_policy(u32 lsm_id, u32 op, void __user *buf,
+ size_t size, u32 flags);
+int security_lsm_config_system_policy(u32 lsm_id, u32 op, void __user *buf,
+ size_t size, u32 flags);
+
#else /* CONFIG_SECURITY */
/**
@@ -1603,6 +1608,17 @@ static inline int security_bdev_setintegrity(struct block_device *bdev,
return 0;
}
+static int security_lsm_config_self_policy(u32 lsm_id, u32 op, void __user *buf,
+ size_t size, u32 flags)
+
+ return -EOPNOTSUPP;
+}
+
+static int security_lsm_config_system_policy(u32 lsm_id, u32 op, void __user *buf,
+ size_t size, u32 flags)
+
+ return -EOPNOTSUPP;
+}
#endif /* CONFIG_SECURITY */
#if defined(CONFIG_SECURITY) && defined(CONFIG_WATCH_QUEUE)
diff --git a/include/uapi/linux/lsm.h b/include/uapi/linux/lsm.h
index 938593dfd5da..844279f819ce 100644
--- a/include/uapi/linux/lsm.h
+++ b/include/uapi/linux/lsm.h
@@ -90,4 +90,12 @@ struct lsm_ctx {
*/
#define LSM_FLAG_SINGLE 0x0001
+/*
+ * LSM_POLICY_XXX definitions identify the different operations
+ * configure lsm policies
+ */
+
+#define LSM_POLICY_UNDEF 0
+#define LSM_POLICY_LOAD 100
+
#endif /* _UAPI_LINUX_LSM_H */
diff --git a/security/Kconfig b/security/Kconfig
index 4816fc74f81e..958be7b49a9e 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -220,6 +220,28 @@ config STATIC_USERMODEHELPER_PATH
If you wish for all usermode helper programs to be disabled,
specify an empty string here (i.e. "").
+config LSM_CONFIG_SELF_POLICY_MAX_BUFFER_SIZE
+ int "Maximum buffer size for lsm_manage_policy"
+ range 16384 1073741824
+ depends on SECURITY
+ default 4194304
+ help
+ The maximum size of the buffer argument of lsm_config_self_policy.
+
+ The default value of 4194304 (4MiB) is reasonable and should be large
+ enough to fit policies in for most cases.
+
+config LSM_CONFIG_SYSTEM_POLICY_MAX_BUFFER_SIZE
+ int "Maximum buffer size for lsm_manage_policy"
+ range 16384 1073741824
+ depends on SECURITY
+ default 4194304
+ help
+ The maximum size of the buffer argument of lsm_config_system_policy.
+
+ The default value of 4194304 (4MiB) is reasonable and should be large
+ enough to fit policies in for most cases
+
source "security/selinux/Kconfig"
source "security/smack/Kconfig"
source "security/tomoyo/Kconfig"
diff --git a/security/lsm_syscalls.c b/security/lsm_syscalls.c
index a3cb6dab8102..dd016ba6976c 100644
--- a/security/lsm_syscalls.c
+++ b/security/lsm_syscalls.c
@@ -122,11 +122,24 @@ SYSCALL_DEFINE3(lsm_list_modules, u64 __user *, ids, u32 __user *, size,
SYSCALL_DEFINE5(lsm_config_self_policy, u32, lsm_id, u32, op, void __user *,
buf, u32 __user *, size, u32, flags)
{
- return 0;
+ size_t usize;
+
+ if (get_user(usize, size))
+ return -EFAULT;
+
+ return security_lsm_config_self_policy(lsm_id, op, buf, usize, flags);
}
SYSCALL_DEFINE5(lsm_config_system_policy, u32, lsm_id, u32, op, void __user *,
buf, u32 __user *, size, u32, flags)
{
- return 0;
+ size_t usize;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (get_user(usize, size))
+ return -EFAULT;
+
+ return security_lsm_config_system_policy(lsm_id, op, buf, usize, flags);
}
diff --git a/security/security.c b/security/security.c
index fb57e8fddd91..8efea2b6e967 100644
--- a/security/security.c
+++ b/security/security.c
@@ -5883,6 +5883,75 @@ int security_bdev_setintegrity(struct block_device *bdev,
}
EXPORT_SYMBOL(security_bdev_setintegrity);
+/**
+ * security_lsm_config_self_policy() - Manage caller's LSM policies
+ * @lsm_id: id of the LSM to target
+ * @op: Operation to perform (one of the LSM_POLICY_XXX values)
+ * @buf: userspace pointer to policy data
+ * @size: size of @buf
+ * @flags: lsm policy management flags
+ *
+ * Manage the policies of a LSM for the current domain/user. This notably allows
+ * to update them even when the lsmfs is unavailable is restricted. Currently,
+ * only LSM_POLICY_LOAD is supported.
+ *
+ * Return: Returns 0 on success, error on failure.
+ */
+int security_lsm_config_self_policy(u32 lsm_id, u32 op, void __user *buf,
+ size_t size, u32 flags)
+{
+ int rc = LSM_RET_DEFAULT(lsm_config_self_policy);
+ struct lsm_static_call *scall;
+
+ if (size > (CONFIG_LSM_CONFIG_SELF_POLICY_MAX_BUFFER_SIZE))
+ return -E2BIG;
+
+ lsm_for_each_hook(scall, lsm_config_self_policy) {
+ if ((scall->hl->lsmid->id) == lsm_id) {
+ rc = scall->hl->hook.lsm_config_self_policy(lsm_id, op, buf, size, flags);
+ break;
+ }
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(security_lsm_config_self_policy);
+
+/**
+ * security_lsm_config_system_policy() - Manage system LSM policies
+ * @lsm_id: id of the lsm to target
+ * @op: Operation to perform (one of the LSM_POLICY_XXX values)
+ * @buf: userspace pointer to policy data
+ * @size: size of @buf
+ * @flags: lsm policy management flags
+ *
+ * Manage the policies of a LSM for the whole system. This notably allows
+ * to update them even when the lsmfs is unavailable is restricted. Currently,
+ * only LSM_POLICY_LOAD is supported.
+ *
+ * Return: Returns 0 on success, error on failure.
+ */
+int security_lsm_config_system_policy(u32 lsm_id, u32 op, void __user *buf,
+ size_t size, u32 flags)
+{
+ int rc = LSM_RET_DEFAULT(lsm_config_system_policy);
+ struct lsm_static_call *scall;
+
+ if (size > (CONFIG_LSM_CONFIG_SYSTEM_POLICY_MAX_BUFFER_SIZE))
+ return -E2BIG;
+
+ lsm_for_each_hook(scall, lsm_config_system_policy) {
+ if ((scall->hl->lsmid->id) == lsm_id) {
+ rc = scall->hl->hook.lsm_config_system_policy(lsm_id, op, buf, size, flags);
+ break;
+ }
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(security_lsm_config_system_policy);
+
+
#ifdef CONFIG_PERF_EVENTS
/**
* security_perf_event_open() - Check if a perf event open is allowed
--
2.48.1
^ permalink raw reply related
* [PATCH v2 1/3] Wire up lsm_config_self_policy and lsm_config_system_policy syscalls
From: Maxime Bélair @ 2025-06-19 18:15 UTC (permalink / raw)
To: linux-security-module
Cc: john.johansen, paul, jmorris, serge, mic, kees,
stephen.smalley.work, casey, takedakn, penguin-kernel, song,
linux-api, apparmor, linux-kernel, Maxime Bélair
In-Reply-To: <20250619181600.478038-1-maxime.belair@canonical.com>
Add support for the new lsm_config_self_policy and
lsm_config_system_policy syscalls, providing a unified API for loading
and modifying LSM policies, for the current user and for the entire
system, respectively without requiring the LSM’s pseudo-filesystems.
Benefits:
- Works even if the LSM pseudo-filesystem isn’t mounted or available
(e.g. in containers)
- Offers a logical and unified interface rather than multiple
heterogeneous pseudo-filesystems
- Avoids the overhead of other kernel interfaces for better efficiency
Signed-off-by: Maxime Bélair <maxime.belair@canonical.com>
---
arch/alpha/kernel/syscalls/syscall.tbl | 2 ++
arch/arm/tools/syscall.tbl | 2 ++
arch/m68k/kernel/syscalls/syscall.tbl | 2 ++
arch/microblaze/kernel/syscalls/syscall.tbl | 2 ++
arch/mips/kernel/syscalls/syscall_n32.tbl | 2 ++
arch/mips/kernel/syscalls/syscall_n64.tbl | 2 ++
arch/mips/kernel/syscalls/syscall_o32.tbl | 2 ++
arch/parisc/kernel/syscalls/syscall.tbl | 2 ++
arch/powerpc/kernel/syscalls/syscall.tbl | 2 ++
arch/s390/kernel/syscalls/syscall.tbl | 2 ++
arch/sh/kernel/syscalls/syscall.tbl | 2 ++
arch/sparc/kernel/syscalls/syscall.tbl | 2 ++
arch/x86/entry/syscalls/syscall_32.tbl | 2 ++
arch/x86/entry/syscalls/syscall_64.tbl | 2 ++
arch/xtensa/kernel/syscalls/syscall.tbl | 2 ++
include/linux/syscalls.h | 5 +++++
include/uapi/asm-generic/unistd.h | 6 +++++-
kernel/sys_ni.c | 2 ++
security/lsm_syscalls.c | 12 ++++++++++++
tools/include/uapi/asm-generic/unistd.h | 6 +++++-
tools/perf/arch/x86/entry/syscalls/syscall_64.tbl | 2 ++
21 files changed, 61 insertions(+), 2 deletions(-)
diff --git a/arch/alpha/kernel/syscalls/syscall.tbl b/arch/alpha/kernel/syscalls/syscall.tbl
index 2dd6340de6b4..4fc75352220d 100644
--- a/arch/alpha/kernel/syscalls/syscall.tbl
+++ b/arch/alpha/kernel/syscalls/syscall.tbl
@@ -507,3 +507,5 @@
575 common listxattrat sys_listxattrat
576 common removexattrat sys_removexattrat
577 common open_tree_attr sys_open_tree_attr
+578 common lsm_config_self_policy sys_lsm_config_self_policy
+579 common lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/arm/tools/syscall.tbl b/arch/arm/tools/syscall.tbl
index 27c1d5ebcd91..326483cb94a4 100644
--- a/arch/arm/tools/syscall.tbl
+++ b/arch/arm/tools/syscall.tbl
@@ -482,3 +482,5 @@
465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr
+468 common lsm_config_self_policy sys_lsm_config_self_policy
+469 common lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/m68k/kernel/syscalls/syscall.tbl b/arch/m68k/kernel/syscalls/syscall.tbl
index 9fe47112c586..d37364df1cd7 100644
--- a/arch/m68k/kernel/syscalls/syscall.tbl
+++ b/arch/m68k/kernel/syscalls/syscall.tbl
@@ -467,3 +467,5 @@
465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr
+468 common lsm_config_self_policy sys_lsm_config_self_policy
+469 common lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/microblaze/kernel/syscalls/syscall.tbl b/arch/microblaze/kernel/syscalls/syscall.tbl
index 7b6e97828e55..9d58ebfcf967 100644
--- a/arch/microblaze/kernel/syscalls/syscall.tbl
+++ b/arch/microblaze/kernel/syscalls/syscall.tbl
@@ -473,3 +473,5 @@
465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr
+468 common lsm_config_self_policy sys_lsm_config_self_policy
+469 common lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/mips/kernel/syscalls/syscall_n32.tbl b/arch/mips/kernel/syscalls/syscall_n32.tbl
index aa70e371bb54..8627b5f56280 100644
--- a/arch/mips/kernel/syscalls/syscall_n32.tbl
+++ b/arch/mips/kernel/syscalls/syscall_n32.tbl
@@ -406,3 +406,5 @@
465 n32 listxattrat sys_listxattrat
466 n32 removexattrat sys_removexattrat
467 n32 open_tree_attr sys_open_tree_attr
+468 n32 lsm_config_self_policy sys_lsm_config_self_policy
+469 n32 lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/mips/kernel/syscalls/syscall_n64.tbl b/arch/mips/kernel/syscalls/syscall_n64.tbl
index 1e8c44c7b614..813207b61f58 100644
--- a/arch/mips/kernel/syscalls/syscall_n64.tbl
+++ b/arch/mips/kernel/syscalls/syscall_n64.tbl
@@ -382,3 +382,5 @@
465 n64 listxattrat sys_listxattrat
466 n64 removexattrat sys_removexattrat
467 n64 open_tree_attr sys_open_tree_attr
+468 n64 lsm_config_self_policy sys_lsm_config_self_policy
+469 n64 lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/mips/kernel/syscalls/syscall_o32.tbl b/arch/mips/kernel/syscalls/syscall_o32.tbl
index 114a5a1a6230..9cd0946b4370 100644
--- a/arch/mips/kernel/syscalls/syscall_o32.tbl
+++ b/arch/mips/kernel/syscalls/syscall_o32.tbl
@@ -455,3 +455,5 @@
465 o32 listxattrat sys_listxattrat
466 o32 removexattrat sys_removexattrat
467 o32 open_tree_attr sys_open_tree_attr
+468 o32 lsm_config_self_policy sys_lsm_config_self_policy
+469 o32 lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl
index 94df3cb957e9..9db01dd55793 100644
--- a/arch/parisc/kernel/syscalls/syscall.tbl
+++ b/arch/parisc/kernel/syscalls/syscall.tbl
@@ -466,3 +466,5 @@
465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr
+468 common lsm_config_self_policy sys_lsm_config_self_policy
+469 common lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/powerpc/kernel/syscalls/syscall.tbl b/arch/powerpc/kernel/syscalls/syscall.tbl
index 9a084bdb8926..97714acb39ab 100644
--- a/arch/powerpc/kernel/syscalls/syscall.tbl
+++ b/arch/powerpc/kernel/syscalls/syscall.tbl
@@ -558,3 +558,5 @@
465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr
+468 common lsm_config_self_policy sys_lsm_config_self_policy
+469 common lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/s390/kernel/syscalls/syscall.tbl b/arch/s390/kernel/syscalls/syscall.tbl
index a4569b96ef06..d2b0f14fb516 100644
--- a/arch/s390/kernel/syscalls/syscall.tbl
+++ b/arch/s390/kernel/syscalls/syscall.tbl
@@ -470,3 +470,5 @@
465 common listxattrat sys_listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr sys_open_tree_attr
+468 common lsm_config_self_policy sys_lsm_config_self_policy sys_lsm_config_self_policy
+469 common lsm_config_system_policy sys_lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/sh/kernel/syscalls/syscall.tbl b/arch/sh/kernel/syscalls/syscall.tbl
index 52a7652fcff6..210d7118ce16 100644
--- a/arch/sh/kernel/syscalls/syscall.tbl
+++ b/arch/sh/kernel/syscalls/syscall.tbl
@@ -471,3 +471,5 @@
465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr
+468 common lsm_config_self_policy sys_lsm_config_self_policy
+469 common lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl
index 83e45eb6c095..494417d80680 100644
--- a/arch/sparc/kernel/syscalls/syscall.tbl
+++ b/arch/sparc/kernel/syscalls/syscall.tbl
@@ -513,3 +513,5 @@
465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr
+468 common lsm_config_self_policy sys_lsm_config_self_policy
+469 common lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index ac007ea00979..36c2c538e04f 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -473,3 +473,5 @@
465 i386 listxattrat sys_listxattrat
466 i386 removexattrat sys_removexattrat
467 i386 open_tree_attr sys_open_tree_attr
+468 i386 lsm_config_self_policy sys_lsm_config_self_policy
+469 i386 lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index cfb5ca41e30d..7eefbccfe531 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -391,6 +391,8 @@
465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr
+468 common lsm_config_self_policy sys_lsm_config_self_policy
+469 common lsm_config_system_policy sys_lsm_config_system_policy
#
# Due to a historical design error, certain syscalls are numbered differently
diff --git a/arch/xtensa/kernel/syscalls/syscall.tbl b/arch/xtensa/kernel/syscalls/syscall.tbl
index f657a77314f8..90d86a54a952 100644
--- a/arch/xtensa/kernel/syscalls/syscall.tbl
+++ b/arch/xtensa/kernel/syscalls/syscall.tbl
@@ -438,3 +438,5 @@
465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr
+468 common lsm_config_self_policy sys_lsm_config_self_policy
+469 common lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index e5603cc91963..15b0f35c42fe 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -988,6 +988,11 @@ asmlinkage long sys_lsm_get_self_attr(unsigned int attr, struct lsm_ctx __user *
asmlinkage long sys_lsm_set_self_attr(unsigned int attr, struct lsm_ctx __user *ctx,
u32 size, u32 flags);
asmlinkage long sys_lsm_list_modules(u64 __user *ids, u32 __user *size, u32 flags);
+asmlinkage long sys_lsm_config_self_policy(u32 lsm_id, u32 op, void __user *buf,
+ u32 __user *size, u32 flags);
+asmlinkage long sys_lsm_config_system_policy(u32 lsm_id, u32 op, void __user *buf,
+ u32 __user *size, u32 flags);
+
/*
* Architecture-specific system calls
diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
index 2892a45023af..34278cc6a476 100644
--- a/include/uapi/asm-generic/unistd.h
+++ b/include/uapi/asm-generic/unistd.h
@@ -851,9 +851,13 @@ __SYSCALL(__NR_listxattrat, sys_listxattrat)
__SYSCALL(__NR_removexattrat, sys_removexattrat)
#define __NR_open_tree_attr 467
__SYSCALL(__NR_open_tree_attr, sys_open_tree_attr)
+#define __NR_lsm_config_self_policy 468
+__SYSCALL(__NR_lsm_config_self_policy, lsm_config_self_policy)
+#define __NR_lsm_config_system_policy 469
+__SYSCALL(__NR_lsm_config_system_policy, lsm_config_system_policy)
#undef __NR_syscalls
-#define __NR_syscalls 468
+#define __NR_syscalls 470
/*
* 32 bit systems traditionally used different
diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
index c00a86931f8c..3ecebcd3fbe0 100644
--- a/kernel/sys_ni.c
+++ b/kernel/sys_ni.c
@@ -172,6 +172,8 @@ COND_SYSCALL_COMPAT(fadvise64_64);
COND_SYSCALL(lsm_get_self_attr);
COND_SYSCALL(lsm_set_self_attr);
COND_SYSCALL(lsm_list_modules);
+COND_SYSCALL(lsm_config_self_policy);
+COND_SYSCALL(lsm_config_system_policy);
/* CONFIG_MMU only */
COND_SYSCALL(swapon);
diff --git a/security/lsm_syscalls.c b/security/lsm_syscalls.c
index 8440948a690c..a3cb6dab8102 100644
--- a/security/lsm_syscalls.c
+++ b/security/lsm_syscalls.c
@@ -118,3 +118,15 @@ SYSCALL_DEFINE3(lsm_list_modules, u64 __user *, ids, u32 __user *, size,
return lsm_active_cnt;
}
+
+SYSCALL_DEFINE5(lsm_config_self_policy, u32, lsm_id, u32, op, void __user *,
+ buf, u32 __user *, size, u32, flags)
+{
+ return 0;
+}
+
+SYSCALL_DEFINE5(lsm_config_system_policy, u32, lsm_id, u32, op, void __user *,
+ buf, u32 __user *, size, u32, flags)
+{
+ return 0;
+}
diff --git a/tools/include/uapi/asm-generic/unistd.h b/tools/include/uapi/asm-generic/unistd.h
index 2892a45023af..34278cc6a476 100644
--- a/tools/include/uapi/asm-generic/unistd.h
+++ b/tools/include/uapi/asm-generic/unistd.h
@@ -851,9 +851,13 @@ __SYSCALL(__NR_listxattrat, sys_listxattrat)
__SYSCALL(__NR_removexattrat, sys_removexattrat)
#define __NR_open_tree_attr 467
__SYSCALL(__NR_open_tree_attr, sys_open_tree_attr)
+#define __NR_lsm_config_self_policy 468
+__SYSCALL(__NR_lsm_config_self_policy, lsm_config_self_policy)
+#define __NR_lsm_config_system_policy 469
+__SYSCALL(__NR_lsm_config_system_policy, lsm_config_system_policy)
#undef __NR_syscalls
-#define __NR_syscalls 468
+#define __NR_syscalls 470
/*
* 32 bit systems traditionally used different
diff --git a/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl b/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl
index cfb5ca41e30d..7eefbccfe531 100644
--- a/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl
@@ -391,6 +391,8 @@
465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr
+468 common lsm_config_self_policy sys_lsm_config_self_policy
+469 common lsm_config_system_policy sys_lsm_config_system_policy
#
# Due to a historical design error, certain syscalls are numbered differently
--
2.48.1
^ permalink raw reply related
* [PATCH v2 3/3] AppArmor: add support for lsm_config_self_policy and lsm_config_system_policy
From: Maxime Bélair @ 2025-06-19 18:15 UTC (permalink / raw)
To: linux-security-module
Cc: john.johansen, paul, jmorris, serge, mic, kees,
stephen.smalley.work, casey, takedakn, penguin-kernel, song,
linux-api, apparmor, linux-kernel, Maxime Bélair
In-Reply-To: <20250619181600.478038-1-maxime.belair@canonical.com>
Enable users to manage AppArmor policies through the new hooks
lsm_config_self_policy and lsm_config_system_policy.
lsm_config_self_policy allows stacking existing policies in the kernel.
This ensures that it can only further restrict the caller and can never
be used to gain new privileges.
lsm_config_system_policy allows loading or replacing AppArmor policies in
any AppArmor namespace.
Signed-off-by: Maxime Bélair <maxime.belair@canonical.com>
---
security/apparmor/apparmorfs.c | 31 +++++++++++++
security/apparmor/include/apparmorfs.h | 3 ++
security/apparmor/lsm.c | 63 ++++++++++++++++++++++++++
3 files changed, 97 insertions(+)
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 6039afae4bfc..827fe06b20ac 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -439,6 +439,37 @@ static ssize_t policy_update(u32 mask, const char __user *buf, size_t size,
return error;
}
+/**
+ * aa_profile_load_ns_name - load a profile into the current namespace identified by name
+ * @name The name of the namesapce to load the policy in. "" for root_ns
+ * @name_size size of @name. 0 For root ns
+ * @buf buffer containing the user-provided policy
+ * @size size of @buf
+ * @ppos position pointer in the file
+ *
+ * Returns: 0 on success, negative value on error
+ */
+ssize_t aa_profile_load_ns_name(char *name, size_t name_size, const void __user *buf,
+ size_t size, loff_t *ppos)
+{
+ struct aa_ns *ns;
+
+ if (name_size == 0)
+ ns = aa_get_ns(root_ns);
+ else
+ ns = aa_lookupn_ns(root_ns, name, name_size);
+
+ if (!ns)
+ return -EINVAL;
+
+ int error = policy_update(AA_MAY_LOAD_POLICY | AA_MAY_REPLACE_POLICY,
+ buf, size, ppos, ns);
+
+ aa_put_ns(ns);
+
+ return error >= 0 ? 0 : error;
+}
+
/* .load file hook fn to load policy */
static ssize_t profile_load(struct file *f, const char __user *buf, size_t size,
loff_t *pos)
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h
index 1e94904f68d9..fd415afb7659 100644
--- a/security/apparmor/include/apparmorfs.h
+++ b/security/apparmor/include/apparmorfs.h
@@ -112,6 +112,9 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent);
void __aafs_ns_rmdir(struct aa_ns *ns);
int __aafs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name,
struct dentry *dent);
+ssize_t aa_profile_load_ns_name(char *name, size_t name_len, const void __user *buf,
+ size_t size, loff_t *ppos);
+
struct aa_loaddata;
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 9b6c2f157f83..b38c4926cdc2 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -1275,6 +1275,65 @@ static int apparmor_socket_shutdown(struct socket *sock, int how)
return aa_sock_perm(OP_SHUTDOWN, AA_MAY_SHUTDOWN, sock);
}
+/**
+ * apparmor_lsm_config_self_policy - Stack a profile
+ * @buf: buffer containing the user-provided name of the profile to stack
+ * @size: size of @buf
+ *
+ * Returns: 0 on success, negative value on error
+ */
+static int apparmor_lsm_config_self_policy(u32 lsm_id, u32 op, void __user *buf,
+ size_t size, u32 flags)
+{
+ char *name = kvmalloc(size, GFP_KERNEL);
+ long name_size;
+ int ret;
+
+ if (op != LSM_POLICY_LOAD || flags)
+ return -EOPNOTSUPP;
+
+ name_size = strncpy_from_user(name, buf, size);
+ if (name_size < 0)
+ return name_size;
+
+ ret = aa_change_profile(name, AA_CHANGE_STACK);
+
+ kvfree(name);
+
+ return ret;
+}
+
+/**
+ * apparmor_lsm_config_system_policy - Load or replace a system policy
+ * @buf: user-supplied buffer in the form "<ns>\0<policy>"
+ * <ns> is the namespace to load the policy into (empty string for root)
+ * <policy> is the policy to load
+ * then '\0' then the policy to load
+ * @size: size of @buf
+ *
+ * Returns: 0 on success, negative value on error
+ */
+static int apparmor_lsm_config_system_policy(u32 lsm_id, u32 op, void __user *buf,
+ size_t size, u32 flags)
+{
+ loff_t pos = 0; // Partial writing is not currently supported
+ char name[256];
+ long name_size;
+
+ if (op != LSM_POLICY_LOAD || flags)
+ return -EOPNOTSUPP;
+
+ name_size = strncpy_from_user(name, buf, 256);
+ if (name_size < 0)
+ return name_size;
+ else if (name_size == 256)
+ return -E2BIG;
+
+ return aa_profile_load_ns_name(name, name_size, buf + name_size + 1,
+ size - name_size - 1, &pos);
+}
+
+
#ifdef CONFIG_NETWORK_SECMARK
/**
* apparmor_socket_sock_rcv_skb - check perms before associating skb to sk
@@ -1483,6 +1542,10 @@ static struct security_hook_list apparmor_hooks[] __ro_after_init = {
LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt),
LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt),
LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown),
+
+ LSM_HOOK_INIT(lsm_config_self_policy, apparmor_lsm_config_self_policy),
+ LSM_HOOK_INIT(lsm_config_system_policy,
+ apparmor_lsm_config_system_policy),
#ifdef CONFIG_NETWORK_SECMARK
LSM_HOOK_INIT(socket_sock_rcv_skb, apparmor_socket_sock_rcv_skb),
#endif
--
2.48.1
^ permalink raw reply related
* Re: [PATCH v2 2/3] lsm: introduce security_lsm_config_*_policy hooks
From: Randy Dunlap @ 2025-06-20 3:03 UTC (permalink / raw)
To: Maxime Bélair, linux-security-module
Cc: john.johansen, paul, jmorris, serge, mic, kees,
stephen.smalley.work, casey, takedakn, penguin-kernel, song,
linux-api, apparmor, linux-kernel
In-Reply-To: <20250619181600.478038-3-maxime.belair@canonical.com>
On 6/19/25 11:15 AM, Maxime Bélair wrote:
> Define two new LSM hooks: security_lsm_config_self_policy and
> security_lsm_config_system_policy and wire them into the corresponding
> lsm_config_*_policy() syscalls so that LSMs can register a unified
> interface for policy management. This initial, minimal implementation
> only supports the LSM_POLICY_LOAD operation to limit changes.
>
> Signed-off-by: Maxime Bélair <maxime.belair@canonical.com>
> ---
> include/linux/lsm_hook_defs.h | 4 ++
> include/linux/security.h | 16 ++++++++
> include/uapi/linux/lsm.h | 8 ++++
> security/Kconfig | 22 +++++++++++
> security/lsm_syscalls.c | 17 ++++++++-
> security/security.c | 69 +++++++++++++++++++++++++++++++++++
> 6 files changed, 134 insertions(+), 2 deletions(-)
>
> diff --git a/security/Kconfig b/security/Kconfig
> index 4816fc74f81e..958be7b49a9e 100644
> --- a/security/Kconfig
> +++ b/security/Kconfig
> @@ -220,6 +220,28 @@ config STATIC_USERMODEHELPER_PATH
> If you wish for all usermode helper programs to be disabled,
> specify an empty string here (i.e. "").
>
> +config LSM_CONFIG_SELF_POLICY_MAX_BUFFER_SIZE
> + int "Maximum buffer size for lsm_manage_policy"
Update function name.
> + range 16384 1073741824
> + depends on SECURITY
> + default 4194304
> + help
> + The maximum size of the buffer argument of lsm_config_self_policy.
> +
> + The default value of 4194304 (4MiB) is reasonable and should be large
> + enough to fit policies in for most cases.
> +
> +config LSM_CONFIG_SYSTEM_POLICY_MAX_BUFFER_SIZE
> + int "Maximum buffer size for lsm_manage_policy"
same here.
> + range 16384 1073741824
> + depends on SECURITY
> + default 4194304
> + help
> + The maximum size of the buffer argument of lsm_config_system_policy.
> +
> + The default value of 4194304 (4MiB) is reasonable and should be large
> + enough to fit policies in for most cases
> +
> source "security/selinux/Kconfig"
> source "security/smack/Kconfig"
> source "security/tomoyo/Kconfig"
> diff --git a/security/security.c b/security/security.c
> index fb57e8fddd91..8efea2b6e967 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -5883,6 +5883,75 @@ int security_bdev_setintegrity(struct block_device *bdev,
> }
> EXPORT_SYMBOL(security_bdev_setintegrity);
>
> +/**
> + * security_lsm_config_self_policy() - Manage caller's LSM policies
> + * @lsm_id: id of the LSM to target
> + * @op: Operation to perform (one of the LSM_POLICY_XXX values)
> + * @buf: userspace pointer to policy data
> + * @size: size of @buf
> + * @flags: lsm policy management flags
> + *
> + * Manage the policies of a LSM for the current domain/user. This notably allows
> + * to update them even when the lsmfs is unavailable is restricted. Currently,
or
?
> + * only LSM_POLICY_LOAD is supported.
> + *
> + * Return: Returns 0 on success, error on failure.
> + */
> +int security_lsm_config_self_policy(u32 lsm_id, u32 op, void __user *buf,
> + size_t size, u32 flags)
> +{
> + int rc = LSM_RET_DEFAULT(lsm_config_self_policy);
> + struct lsm_static_call *scall;
> +
> + if (size > (CONFIG_LSM_CONFIG_SELF_POLICY_MAX_BUFFER_SIZE))
> + return -E2BIG;
> +
> + lsm_for_each_hook(scall, lsm_config_self_policy) {
> + if ((scall->hl->lsmid->id) == lsm_id) {
> + rc = scall->hl->hook.lsm_config_self_policy(lsm_id, op, buf, size, flags);
> + break;
> + }
> + }
> +
> + return rc;
> +}
> +EXPORT_SYMBOL(security_lsm_config_self_policy);
> +
> +/**
> + * security_lsm_config_system_policy() - Manage system LSM policies
> + * @lsm_id: id of the lsm to target
> + * @op: Operation to perform (one of the LSM_POLICY_XXX values)
> + * @buf: userspace pointer to policy data
> + * @size: size of @buf
> + * @flags: lsm policy management flags
> + *
> + * Manage the policies of a LSM for the whole system. This notably allows
> + * to update them even when the lsmfs is unavailable is restricted. Currently,
or
?
> + * only LSM_POLICY_LOAD is supported.
> + *
> + * Return: Returns 0 on success, error on failure.
> + */
> +int security_lsm_config_system_policy(u32 lsm_id, u32 op, void __user *buf,
> + size_t size, u32 flags)
> +{
[snip]
--
~Randy
^ permalink raw reply
* Re: [PATCH v2 3/3] AppArmor: add support for lsm_config_self_policy and lsm_config_system_policy
From: Randy Dunlap @ 2025-06-20 3:09 UTC (permalink / raw)
To: Maxime Bélair, linux-security-module
Cc: john.johansen, paul, jmorris, serge, mic, kees,
stephen.smalley.work, casey, takedakn, penguin-kernel, song,
linux-api, apparmor, linux-kernel
In-Reply-To: <20250619181600.478038-4-maxime.belair@canonical.com>
On 6/19/25 11:15 AM, Maxime Bélair wrote:
> Enable users to manage AppArmor policies through the new hooks
> lsm_config_self_policy and lsm_config_system_policy.
>
> lsm_config_self_policy allows stacking existing policies in the kernel.
> This ensures that it can only further restrict the caller and can never
> be used to gain new privileges.
>
> lsm_config_system_policy allows loading or replacing AppArmor policies in
> any AppArmor namespace.
>
> Signed-off-by: Maxime Bélair <maxime.belair@canonical.com>
> ---
> security/apparmor/apparmorfs.c | 31 +++++++++++++
> security/apparmor/include/apparmorfs.h | 3 ++
> security/apparmor/lsm.c | 63 ++++++++++++++++++++++++++
> 3 files changed, 97 insertions(+)
>
> diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
> index 6039afae4bfc..827fe06b20ac 100644
> --- a/security/apparmor/apparmorfs.c
> +++ b/security/apparmor/apparmorfs.c
> @@ -439,6 +439,37 @@ static ssize_t policy_update(u32 mask, const char __user *buf, size_t size,
> return error;
> }
>
> +/**
> + * aa_profile_load_ns_name - load a profile into the current namespace identified by name
> + * @name The name of the namesapce to load the policy in. "" for root_ns
> + * @name_size size of @name. 0 For root ns
> + * @buf buffer containing the user-provided policy
> + * @size size of @buf
> + * @ppos position pointer in the file
Please use proper kernel-doc syntax above. Each @var_name should be followed
by a ':'.
> + *
> + * Returns: 0 on success, negative value on error
> + */
> +ssize_t aa_profile_load_ns_name(char *name, size_t name_size, const void __user *buf,
> + size_t size, loff_t *ppos)
> +{
> + struct aa_ns *ns;
> +
> + if (name_size == 0)
> + ns = aa_get_ns(root_ns);
> + else
> + ns = aa_lookupn_ns(root_ns, name, name_size);
> +
> + if (!ns)
> + return -EINVAL;
> +
> + int error = policy_update(AA_MAY_LOAD_POLICY | AA_MAY_REPLACE_POLICY,
> + buf, size, ppos, ns);
> +
> + aa_put_ns(ns);
> +
> + return error >= 0 ? 0 : error;
> +}
> +
> /* .load file hook fn to load policy */
> static ssize_t profile_load(struct file *f, const char __user *buf, size_t size,
> loff_t *pos)
> diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
> index 9b6c2f157f83..b38c4926cdc2 100644
> --- a/security/apparmor/lsm.c
> +++ b/security/apparmor/lsm.c
> @@ -1275,6 +1275,65 @@ static int apparmor_socket_shutdown(struct socket *sock, int how)
> return aa_sock_perm(OP_SHUTDOWN, AA_MAY_SHUTDOWN, sock);
> }
>
> +/**
> + * apparmor_lsm_config_self_policy - Stack a profile
> + * @buf: buffer containing the user-provided name of the profile to stack
> + * @size: size of @buf
Describe all function parameters in kernel-doc above.
> + *
> + * Returns: 0 on success, negative value on error
> + */
> +static int apparmor_lsm_config_self_policy(u32 lsm_id, u32 op, void __user *buf,
> + size_t size, u32 flags)
> +{
> + char *name = kvmalloc(size, GFP_KERNEL);
Check return value (i.e., name) before use?
> + long name_size;> + int ret;
> +
> + if (op != LSM_POLICY_LOAD || flags)
> + return -EOPNOTSUPP;
> +
> + name_size = strncpy_from_user(name, buf, size);
> + if (name_size < 0)
> + return name_size;
> +
> + ret = aa_change_profile(name, AA_CHANGE_STACK);
> +
> + kvfree(name);
> +
> + return ret;
> +}
> +
> +/**
> + * apparmor_lsm_config_system_policy - Load or replace a system policy
> + * @buf: user-supplied buffer in the form "<ns>\0<policy>"
> + * <ns> is the namespace to load the policy into (empty string for root)
> + * <policy> is the policy to load
> + * then '\0' then the policy to load
Misplaced line?
> + * @size: size of @buf
Please describe all function parameters in kernel-doc notation.
> + *
> + * Returns: 0 on success, negative value on error
> + */
> +static int apparmor_lsm_config_system_policy(u32 lsm_id, u32 op, void __user *buf,
> + size_t size, u32 flags)
> +{
[snip]
--
~Randy
^ permalink raw reply
* Re: [PATCH v2 2/3] lsm: introduce security_lsm_config_*_policy hooks
From: kernel test robot @ 2025-06-20 6:28 UTC (permalink / raw)
To: Maxime Bélair, linux-security-module
Cc: llvm, oe-kbuild-all, john.johansen, paul, jmorris, serge, mic,
kees, stephen.smalley.work, casey, takedakn, penguin-kernel, song,
linux-api, apparmor, linux-kernel, Maxime Bélair
In-Reply-To: <20250619181600.478038-3-maxime.belair@canonical.com>
Hi Maxime,
kernel test robot noticed the following build errors:
[auto build test ERROR on 9c32cda43eb78f78c73aee4aa344b777714e259b]
url: https://github.com/intel-lab-lkp/linux/commits/Maxime-B-lair/Wire-up-lsm_config_self_policy-and-lsm_config_system_policy-syscalls/20250620-022714
base: 9c32cda43eb78f78c73aee4aa344b777714e259b
patch link: https://lore.kernel.org/r/20250619181600.478038-3-maxime.belair%40canonical.com
patch subject: [PATCH v2 2/3] lsm: introduce security_lsm_config_*_policy hooks
config: x86_64-buildonly-randconfig-003-20250620 (https://download.01.org/0day-ci/archive/20250620/202506201415.KiEs36AG-lkp@intel.com/config)
compiler: clang version 20.1.2 (https://github.com/llvm/llvm-project 58df0ef89dd64126512e4ee27b4ac3fd8ddf6247)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250620/202506201415.KiEs36AG-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202506201415.KiEs36AG-lkp@intel.com/
All errors (new ones prefixed by >>):
In file included from kernel/fork.c:52:
>> include/linux/security.h:1614:2: error: expected function body after function declarator
1614 | return -EOPNOTSUPP;
| ^
>> include/linux/security.h:1615:1: error: extraneous closing brace ('}')
1615 | }
| ^
include/linux/security.h:1620:2: error: expected function body after function declarator
1620 | return -EOPNOTSUPP;
| ^
include/linux/security.h:1621:1: error: extraneous closing brace ('}')
1621 | }
| ^
4 errors generated.
--
In file included from kernel/sysctl.c:29:
>> include/linux/security.h:1614:2: error: expected function body after function declarator
1614 | return -EOPNOTSUPP;
| ^
>> include/linux/security.h:1615:1: error: extraneous closing brace ('}')
1615 | }
| ^
include/linux/security.h:1620:2: error: expected function body after function declarator
1620 | return -EOPNOTSUPP;
| ^
include/linux/security.h:1621:1: error: extraneous closing brace ('}')
1621 | }
| ^
In file included from kernel/sysctl.c:46:
In file included from include/linux/nfs_fs.h:31:
In file included from include/linux/sunrpc/auth.h:13:
In file included from include/linux/sunrpc/sched.h:19:
include/linux/sunrpc/xdr.h:803:46: warning: result of comparison of constant 4611686018427387903 with expression of type '__u32' (aka 'unsigned int') is always false [-Wtautological-constant-out-of-range-compare]
803 | if (U32_MAX >= SIZE_MAX / sizeof(*p) && len > SIZE_MAX / sizeof(*p))
| ~~~ ^ ~~~~~~~~~~~~~~~~~~~~~
1 warning and 4 errors generated.
--
In file included from kernel/signal.c:30:
>> include/linux/security.h:1614:2: error: expected function body after function declarator
1614 | return -EOPNOTSUPP;
| ^
>> include/linux/security.h:1615:1: error: extraneous closing brace ('}')
1615 | }
| ^
include/linux/security.h:1620:2: error: expected function body after function declarator
1620 | return -EOPNOTSUPP;
| ^
include/linux/security.h:1621:1: error: extraneous closing brace ('}')
1621 | }
| ^
kernel/signal.c:142:37: warning: array index 3 is past the end of the array (that has type 'unsigned long[1]') [-Warray-bounds]
142 | case 4: ready = signal->sig[3] &~ blocked->sig[3];
| ^ ~
arch/x86/include/asm/signal.h:24:2: note: array 'sig' declared here
24 | unsigned long sig[_NSIG_WORDS];
| ^
kernel/signal.c:142:19: warning: array index 3 is past the end of the array (that has type 'unsigned long[1]') [-Warray-bounds]
142 | case 4: ready = signal->sig[3] &~ blocked->sig[3];
| ^ ~
arch/x86/include/asm/signal.h:24:2: note: array 'sig' declared here
24 | unsigned long sig[_NSIG_WORDS];
| ^
kernel/signal.c:143:30: warning: array index 2 is past the end of the array (that has type 'unsigned long[1]') [-Warray-bounds]
143 | ready |= signal->sig[2] &~ blocked->sig[2];
| ^ ~
arch/x86/include/asm/signal.h:24:2: note: array 'sig' declared here
24 | unsigned long sig[_NSIG_WORDS];
| ^
kernel/signal.c:143:12: warning: array index 2 is past the end of the array (that has type 'unsigned long[1]') [-Warray-bounds]
143 | ready |= signal->sig[2] &~ blocked->sig[2];
| ^ ~
arch/x86/include/asm/signal.h:24:2: note: array 'sig' declared here
24 | unsigned long sig[_NSIG_WORDS];
| ^
kernel/signal.c:144:30: warning: array index 1 is past the end of the array (that has type 'unsigned long[1]') [-Warray-bounds]
144 | ready |= signal->sig[1] &~ blocked->sig[1];
| ^ ~
arch/x86/include/asm/signal.h:24:2: note: array 'sig' declared here
24 | unsigned long sig[_NSIG_WORDS];
| ^
kernel/signal.c:144:12: warning: array index 1 is past the end of the array (that has type 'unsigned long[1]') [-Warray-bounds]
144 | ready |= signal->sig[1] &~ blocked->sig[1];
| ^ ~
arch/x86/include/asm/signal.h:24:2: note: array 'sig' declared here
24 | unsigned long sig[_NSIG_WORDS];
| ^
kernel/signal.c:148:37: warning: array index 1 is past the end of the array (that has type 'unsigned long[1]') [-Warray-bounds]
148 | case 2: ready = signal->sig[1] &~ blocked->sig[1];
| ^ ~
arch/x86/include/asm/signal.h:24:2: note: array 'sig' declared here
24 | unsigned long sig[_NSIG_WORDS];
| ^
kernel/signal.c:148:19: warning: array index 1 is past the end of the array (that has type 'unsigned long[1]') [-Warray-bounds]
148 | case 2: ready = signal->sig[1] &~ blocked->sig[1];
| ^ ~
arch/x86/include/asm/signal.h:24:2: note: array 'sig' declared here
24 | unsigned long sig[_NSIG_WORDS];
| ^
8 warnings and 4 errors generated.
--
In file included from kernel/dma/swiotlb.c:53:
In file included from include/trace/events/swiotlb.h:41:
In file included from include/trace/define_trace.h:119:
In file included from include/trace/trace_events.h:21:
In file included from include/linux/trace_events.h:10:
In file included from include/linux/perf_event.h:62:
>> include/linux/security.h:1614:2: error: expected function body after function declarator
1614 | return -EOPNOTSUPP;
| ^
>> include/linux/security.h:1615:1: error: extraneous closing brace ('}')
1615 | }
| ^
include/linux/security.h:1620:2: error: expected function body after function declarator
1620 | return -EOPNOTSUPP;
| ^
include/linux/security.h:1621:1: error: extraneous closing brace ('}')
1621 | }
| ^
kernel/dma/swiotlb.c:639:20: warning: shift count >= width of type [-Wshift-count-overflow]
639 | phys_limit < DMA_BIT_MASK(64) &&
| ^~~~~~~~~~~~~~~~
include/linux/dma-mapping.h:73:54: note: expanded from macro 'DMA_BIT_MASK'
73 | #define DMA_BIT_MASK(n) (((n) == 64) ? ~0ULL : ((1ULL<<(n))-1))
| ^ ~~~
1 warning and 4 errors generated.
--
In file included from kernel/events/core.c:34:
In file included from include/linux/syscalls.h:94:
In file included from include/trace/syscall.h:7:
In file included from include/linux/trace_events.h:10:
In file included from include/linux/perf_event.h:62:
>> include/linux/security.h:1614:2: error: expected function body after function declarator
1614 | return -EOPNOTSUPP;
| ^
>> include/linux/security.h:1615:1: error: extraneous closing brace ('}')
1615 | }
| ^
include/linux/security.h:1620:2: error: expected function body after function declarator
1620 | return -EOPNOTSUPP;
| ^
include/linux/security.h:1621:1: error: extraneous closing brace ('}')
1621 | }
| ^
In file included from kernel/events/core.c:43:
include/linux/mman.h:157:9: warning: division by zero is undefined [-Wdivision-by-zero]
157 | _calc_vm_trans(flags, MAP_SYNC, VM_SYNC ) |
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/mman.h:135:21: note: expanded from macro '_calc_vm_trans'
135 | : ((x) & (bit1)) / ((bit1) / (bit2))))
| ^ ~~~~~~~~~~~~~~~~~
include/linux/mman.h:158:9: warning: division by zero is undefined [-Wdivision-by-zero]
158 | _calc_vm_trans(flags, MAP_STACK, VM_NOHUGEPAGE) |
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/mman.h:135:21: note: expanded from macro '_calc_vm_trans'
135 | : ((x) & (bit1)) / ((bit1) / (bit2))))
| ^ ~~~~~~~~~~~~~~~~~
2 warnings and 4 errors generated.
vim +1614 include/linux/security.h
1610
1611 static int security_lsm_config_self_policy(u32 lsm_id, u32 op, void __user *buf,
1612 size_t size, u32 flags)
1613
> 1614 return -EOPNOTSUPP;
> 1615 }
1616
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH v2 3/3] AppArmor: add support for lsm_config_self_policy and lsm_config_system_policy
From: kernel test robot @ 2025-06-20 6:28 UTC (permalink / raw)
To: Maxime Bélair, linux-security-module
Cc: oe-kbuild-all, john.johansen, paul, jmorris, serge, mic, kees,
stephen.smalley.work, casey, takedakn, penguin-kernel, song,
linux-api, apparmor, linux-kernel, Maxime Bélair
In-Reply-To: <20250619181600.478038-4-maxime.belair@canonical.com>
Hi Maxime,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 9c32cda43eb78f78c73aee4aa344b777714e259b]
url: https://github.com/intel-lab-lkp/linux/commits/Maxime-B-lair/Wire-up-lsm_config_self_policy-and-lsm_config_system_policy-syscalls/20250620-022714
base: 9c32cda43eb78f78c73aee4aa344b777714e259b
patch link: https://lore.kernel.org/r/20250619181600.478038-4-maxime.belair%40canonical.com
patch subject: [PATCH v2 3/3] AppArmor: add support for lsm_config_self_policy and lsm_config_system_policy
config: x86_64-buildonly-randconfig-002-20250620 (https://download.01.org/0day-ci/archive/20250620/202506201414.tHOEthTb-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250620/202506201414.tHOEthTb-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202506201414.tHOEthTb-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> security/apparmor/apparmorfs.c:454: warning: Function parameter or struct member 'name' not described in 'aa_profile_load_ns_name'
>> security/apparmor/apparmorfs.c:454: warning: Function parameter or struct member 'name_size' not described in 'aa_profile_load_ns_name'
>> security/apparmor/apparmorfs.c:454: warning: Function parameter or struct member 'buf' not described in 'aa_profile_load_ns_name'
>> security/apparmor/apparmorfs.c:454: warning: Function parameter or struct member 'size' not described in 'aa_profile_load_ns_name'
>> security/apparmor/apparmorfs.c:454: warning: Function parameter or struct member 'ppos' not described in 'aa_profile_load_ns_name'
--
>> security/apparmor/lsm.c:1287: warning: Function parameter or struct member 'lsm_id' not described in 'apparmor_lsm_config_self_policy'
>> security/apparmor/lsm.c:1287: warning: Function parameter or struct member 'op' not described in 'apparmor_lsm_config_self_policy'
>> security/apparmor/lsm.c:1287: warning: Function parameter or struct member 'flags' not described in 'apparmor_lsm_config_self_policy'
>> security/apparmor/lsm.c:1318: warning: Function parameter or struct member 'lsm_id' not described in 'apparmor_lsm_config_system_policy'
>> security/apparmor/lsm.c:1318: warning: Function parameter or struct member 'op' not described in 'apparmor_lsm_config_system_policy'
>> security/apparmor/lsm.c:1318: warning: Function parameter or struct member 'flags' not described in 'apparmor_lsm_config_system_policy'
vim +454 security/apparmor/apparmorfs.c
441
442 /**
443 * aa_profile_load_ns_name - load a profile into the current namespace identified by name
444 * @name The name of the namesapce to load the policy in. "" for root_ns
445 * @name_size size of @name. 0 For root ns
446 * @buf buffer containing the user-provided policy
447 * @size size of @buf
448 * @ppos position pointer in the file
449 *
450 * Returns: 0 on success, negative value on error
451 */
452 ssize_t aa_profile_load_ns_name(char *name, size_t name_size, const void __user *buf,
453 size_t size, loff_t *ppos)
> 454 {
455 struct aa_ns *ns;
456
457 if (name_size == 0)
458 ns = aa_get_ns(root_ns);
459 else
460 ns = aa_lookupn_ns(root_ns, name, name_size);
461
462 if (!ns)
463 return -EINVAL;
464
465 int error = policy_update(AA_MAY_LOAD_POLICY | AA_MAY_REPLACE_POLICY,
466 buf, size, ppos, ns);
467
468 aa_put_ns(ns);
469
470 return error >= 0 ? 0 : error;
471 }
472
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH v2 2/3] lsm: introduce security_lsm_config_*_policy hooks
From: kernel test robot @ 2025-06-20 10:54 UTC (permalink / raw)
To: Maxime Bélair, linux-security-module
Cc: oe-kbuild-all, john.johansen, paul, jmorris, serge, mic, kees,
stephen.smalley.work, casey, takedakn, penguin-kernel, song,
linux-api, apparmor, linux-kernel, Maxime Bélair
In-Reply-To: <20250619181600.478038-3-maxime.belair@canonical.com>
Hi Maxime,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 9c32cda43eb78f78c73aee4aa344b777714e259b]
url: https://github.com/intel-lab-lkp/linux/commits/Maxime-B-lair/Wire-up-lsm_config_self_policy-and-lsm_config_system_policy-syscalls/20250620-022714
base: 9c32cda43eb78f78c73aee4aa344b777714e259b
patch link: https://lore.kernel.org/r/20250619181600.478038-3-maxime.belair%40canonical.com
patch subject: [PATCH v2 2/3] lsm: introduce security_lsm_config_*_policy hooks
config: i386-buildonly-randconfig-006-20250620 (https://download.01.org/0day-ci/archive/20250620/202506201824.SlorGLXM-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250620/202506201824.SlorGLXM-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202506201824.SlorGLXM-lkp@intel.com/
All warnings (new ones prefixed by >>):
| ^~~~~~~~~~~~~~~
include/linux/trace_events.h:869:13: error: storage class specified for parameter 'perf_trace_destroy'
869 | extern void perf_trace_destroy(struct perf_event *event);
| ^~~~~~~~~~~~~~~~~~
include/linux/trace_events.h:870:13: error: storage class specified for parameter 'perf_trace_add'
870 | extern int perf_trace_add(struct perf_event *event, int flags);
| ^~~~~~~~~~~~~~
include/linux/trace_events.h:871:13: error: storage class specified for parameter 'perf_trace_del'
871 | extern void perf_trace_del(struct perf_event *event, int flags);
| ^~~~~~~~~~~~~~
include/linux/trace_events.h:890:13: error: storage class specified for parameter 'ftrace_profile_set_filter'
890 | extern int ftrace_profile_set_filter(struct perf_event *event, int event_id,
| ^~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/trace_events.h:892:13: error: storage class specified for parameter 'ftrace_profile_free_filter'
892 | extern void ftrace_profile_free_filter(struct perf_event *event);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/trace_events.h:935:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
935 | {
| ^
include/trace/syscall.h:25:1: warning: empty declaration
25 | struct syscall_metadata {
| ^~~~~~
include/trace/syscall.h:47:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
47 | {
| ^
In file included from include/linux/syscalls.h:104:
arch/x86/include/asm/syscall_wrapper.h:11:13: error: storage class specified for parameter '__x64_sys_ni_syscall'
11 | extern long __x64_sys_ni_syscall(const struct pt_regs *regs);
| ^~~~~~~~~~~~~~~~~~~~
arch/x86/include/asm/syscall_wrapper.h:12:13: error: storage class specified for parameter '__ia32_sys_ni_syscall'
12 | extern long __ia32_sys_ni_syscall(const struct pt_regs *regs);
| ^~~~~~~~~~~~~~~~~~~~~
include/linux/syscalls.h:211:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
211 | {
| ^
In file included from include/linux/linkage.h:8,
from include/linux/preempt.h:10:
arch/x86/include/asm/linkage.h:20:35: error: expected declaration specifiers before '__attribute__'
20 | #define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))
| ^~~~~~~~~~~~~
include/linux/syscalls.h:1220:1: note: in expansion of macro 'asmlinkage'
1220 | asmlinkage long sys_ni_posix_timers(void);
| ^~~~~~~~~~
include/linux/syscalls.h:1262:12: error: storage class specified for parameter 'do_fchownat'
1262 | extern int do_fchownat(int dfd, const char __user *filename, uid_t user,
| ^~~~~~~~~~~
include/linux/syscalls.h:1267:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
1267 | {
| ^
include/linux/syscalls.h:1273:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
1273 | {
| ^
include/linux/syscalls.h:1281:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
1281 | {
| ^
include/linux/syscalls.h:1288:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
1288 | {
| ^
include/linux/syscalls.h:1293:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
1293 | {
| ^
block/ioprio.c:34:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
34 | {
| ^
arch/x86/include/asm/syscall_wrapper.h:224:21: error: storage class specified for parameter '__se_sys_ioprio_set'
224 | static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \
| ^~~~~~~~
include/linux/syscalls.h:235:9: note: in expansion of macro '__SYSCALL_DEFINEx'
235 | __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
| ^~~~~~~~~~~~~~~~~
include/linux/syscalls.h:226:36: note: in expansion of macro 'SYSCALL_DEFINEx'
226 | #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
| ^~~~~~~~~~~~~~~
block/ioprio.c:69:1: note: in expansion of macro 'SYSCALL_DEFINE3'
69 | SYSCALL_DEFINE3(ioprio_set, int, which, int, who, int, ioprio)
| ^~~~~~~~~~~~~~~
arch/x86/include/asm/syscall_wrapper.h:225:28: error: storage class specified for parameter '__do_sys_ioprio_set'
225 | static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\
| ^~~~~~~~
include/linux/syscalls.h:235:9: note: in expansion of macro '__SYSCALL_DEFINEx'
235 | __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
| ^~~~~~~~~~~~~~~~~
include/linux/syscalls.h:226:36: note: in expansion of macro 'SYSCALL_DEFINEx'
226 | #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
| ^~~~~~~~~~~~~~~
block/ioprio.c:69:1: note: in expansion of macro 'SYSCALL_DEFINE3'
69 | SYSCALL_DEFINE3(ioprio_set, int, which, int, who, int, ioprio)
| ^~~~~~~~~~~~~~~
arch/x86/include/asm/syscall_wrapper.h:225:28: warning: parameter '__do_sys_ioprio_set' declared 'inline'
225 | static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\
| ^~~~~~~~
include/linux/syscalls.h:235:9: note: in expansion of macro '__SYSCALL_DEFINEx'
235 | __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
| ^~~~~~~~~~~~~~~~~
include/linux/syscalls.h:226:36: note: in expansion of macro 'SYSCALL_DEFINEx'
226 | #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
| ^~~~~~~~~~~~~~~
block/ioprio.c:69:1: note: in expansion of macro 'SYSCALL_DEFINE3'
69 | SYSCALL_DEFINE3(ioprio_set, int, which, int, who, int, ioprio)
| ^~~~~~~~~~~~~~~
>> block/ioprio.c:69:1: warning: 'gnu_inline' attribute ignored [-Wattributes]
arch/x86/include/asm/syscall_wrapper.h:225:28: error: 'no_instrument_function' attribute applies only to functions
225 | static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\
| ^~~~~~~~
include/linux/syscalls.h:235:9: note: in expansion of macro '__SYSCALL_DEFINEx'
235 | __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
| ^~~~~~~~~~~~~~~~~
include/linux/syscalls.h:226:36: note: in expansion of macro 'SYSCALL_DEFINEx'
226 | #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
| ^~~~~~~~~~~~~~~
block/ioprio.c:69:1: note: in expansion of macro 'SYSCALL_DEFINE3'
69 | SYSCALL_DEFINE3(ioprio_set, int, which, int, who, int, ioprio)
| ^~~~~~~~~~~~~~~
arch/x86/include/asm/syscall_wrapper.h:93:55: error: expected declaration specifiers before ';' token
93 | ALLOW_ERROR_INJECTION(__##abi##_##name, ERRNO); \
| ^
arch/x86/include/asm/syscall_wrapper.h:128:9: note: in expansion of macro '__SYS_STUBx'
128 | __SYS_STUBx(ia32, sys##name, \
| ^~~~~~~~~~~
arch/x86/include/asm/syscall_wrapper.h:227:9: note: in expansion of macro '__IA32_SYS_STUBx'
227 | __IA32_SYS_STUBx(x, name, __VA_ARGS__) \
| ^~~~~~~~~~~~~~~~
include/linux/syscalls.h:235:9: note: in expansion of macro '__SYSCALL_DEFINEx'
235 | __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
| ^~~~~~~~~~~~~~~~~
include/linux/syscalls.h:226:36: note: in expansion of macro 'SYSCALL_DEFINEx'
226 | #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
| ^~~~~~~~~~~~~~~
block/ioprio.c:69:1: note: in expansion of macro 'SYSCALL_DEFINE3'
69 | SYSCALL_DEFINE3(ioprio_set, int, which, int, who, int, ioprio)
| ^~~~~~~~~~~~~~~
arch/x86/include/asm/syscall_wrapper.h:95:9: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
95 | { \
| ^
arch/x86/include/asm/syscall_wrapper.h:128:9: note: in expansion of macro '__SYS_STUBx'
128 | __SYS_STUBx(ia32, sys##name, \
| ^~~~~~~~~~~
arch/x86/include/asm/syscall_wrapper.h:227:9: note: in expansion of macro '__IA32_SYS_STUBx'
227 | __IA32_SYS_STUBx(x, name, __VA_ARGS__) \
| ^~~~~~~~~~~~~~~~
include/linux/syscalls.h:235:9: note: in expansion of macro '__SYSCALL_DEFINEx'
235 | __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
| ^~~~~~~~~~~~~~~~~
include/linux/syscalls.h:226:36: note: in expansion of macro 'SYSCALL_DEFINEx'
226 | #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
| ^~~~~~~~~~~~~~~
block/ioprio.c:69:1: note: in expansion of macro 'SYSCALL_DEFINE3'
69 | SYSCALL_DEFINE3(ioprio_set, int, which, int, who, int, ioprio)
| ^~~~~~~~~~~~~~~
arch/x86/include/asm/syscall_wrapper.h:229:9: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
229 | { \
| ^
include/linux/syscalls.h:235:9: note: in expansion of macro '__SYSCALL_DEFINEx'
235 | __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
| ^~~~~~~~~~~~~~~~~
include/linux/syscalls.h:226:36: note: in expansion of macro 'SYSCALL_DEFINEx'
226 | #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
| ^~~~~~~~~~~~~~~
block/ioprio.c:69:1: note: in expansion of macro 'SYSCALL_DEFINE3'
69 | SYSCALL_DEFINE3(ioprio_set, int, which, int, who, int, ioprio)
| ^~~~~~~~~~~~~~~
block/ioprio.c:70:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
70 | {
| ^
block/ioprio.c:143:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
143 | {
| ^
block/ioprio.c:163:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
163 | {
| ^
block/ioprio.c:180:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
180 | {
| ^
arch/x86/include/asm/syscall_wrapper.h:224:21: error: storage class specified for parameter '__se_sys_ioprio_get'
224 | static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \
| ^~~~~~~~
include/linux/syscalls.h:235:9: note: in expansion of macro '__SYSCALL_DEFINEx'
235 | __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
| ^~~~~~~~~~~~~~~~~
include/linux/syscalls.h:225:36: note: in expansion of macro 'SYSCALL_DEFINEx'
225 | #define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
| ^~~~~~~~~~~~~~~
block/ioprio.c:184:1: note: in expansion of macro 'SYSCALL_DEFINE2'
184 | SYSCALL_DEFINE2(ioprio_get, int, which, int, who)
| ^~~~~~~~~~~~~~~
arch/x86/include/asm/syscall_wrapper.h:225:28: error: storage class specified for parameter '__do_sys_ioprio_get'
225 | static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\
| ^~~~~~~~
include/linux/syscalls.h:235:9: note: in expansion of macro '__SYSCALL_DEFINEx'
235 | __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
| ^~~~~~~~~~~~~~~~~
include/linux/syscalls.h:225:36: note: in expansion of macro 'SYSCALL_DEFINEx'
225 | #define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
| ^~~~~~~~~~~~~~~
block/ioprio.c:184:1: note: in expansion of macro 'SYSCALL_DEFINE2'
184 | SYSCALL_DEFINE2(ioprio_get, int, which, int, who)
| ^~~~~~~~~~~~~~~
arch/x86/include/asm/syscall_wrapper.h:225:28: warning: parameter '__do_sys_ioprio_get' declared 'inline'
225 | static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\
| ^~~~~~~~
include/linux/syscalls.h:235:9: note: in expansion of macro '__SYSCALL_DEFINEx'
--
include/linux/init_syscalls.h:7:12: error: section attribute not allowed for 'init_chroot'
7 | int __init init_chroot(const char *filename);
| ^~~~~~~~~~~
include/linux/init_syscalls.h:8:12: error: section attribute not allowed for 'init_chown'
8 | int __init init_chown(const char *filename, uid_t user, gid_t group, int flags);
| ^~~~~~~~~~
include/linux/init_syscalls.h:9:12: error: section attribute not allowed for 'init_chmod'
9 | int __init init_chmod(const char *filename, umode_t mode);
| ^~~~~~~~~~
include/linux/init_syscalls.h:10:12: error: section attribute not allowed for 'init_eaccess'
10 | int __init init_eaccess(const char *filename);
| ^~~~~~~~~~~~
include/linux/init_syscalls.h:11:12: error: section attribute not allowed for 'init_stat'
11 | int __init init_stat(const char *filename, struct kstat *stat, int flags);
| ^~~~~~~~~
include/linux/init_syscalls.h:12:12: error: section attribute not allowed for 'init_mknod'
12 | int __init init_mknod(const char *filename, umode_t mode, unsigned int dev);
| ^~~~~~~~~~
include/linux/init_syscalls.h:13:12: error: section attribute not allowed for 'init_link'
13 | int __init init_link(const char *oldname, const char *newname);
| ^~~~~~~~~
include/linux/init_syscalls.h:14:12: error: section attribute not allowed for 'init_symlink'
14 | int __init init_symlink(const char *oldname, const char *newname);
| ^~~~~~~~~~~~
include/linux/init_syscalls.h:15:12: error: section attribute not allowed for 'init_unlink'
15 | int __init init_unlink(const char *pathname);
| ^~~~~~~~~~~
include/linux/init_syscalls.h:16:12: error: section attribute not allowed for 'init_mkdir'
16 | int __init init_mkdir(const char *pathname, umode_t mode);
| ^~~~~~~~~~
include/linux/init_syscalls.h:17:12: error: section attribute not allowed for 'init_rmdir'
17 | int __init init_rmdir(const char *pathname);
| ^~~~~~~~~~
include/linux/init_syscalls.h:18:12: error: section attribute not allowed for 'init_utimes'
18 | int __init init_utimes(char *filename, struct timespec64 *ts);
| ^~~~~~~~~~~
include/linux/init_syscalls.h:19:12: error: section attribute not allowed for 'init_dup'
19 | int __init init_dup(struct file *file);
| ^~~~~~~~
In file included from init/do_mounts.h:12:
include/linux/task_work.h:8:16: error: storage class specified for parameter 'task_work_func_t'
8 | typedef void (*task_work_func_t)(struct callback_head *);
| ^~~~~~~~~~~~~~~~
include/linux/task_work.h:11:45: error: expected declaration specifiers or '...' before 'task_work_func_t'
11 | init_task_work(struct callback_head *twork, task_work_func_t func)
| ^~~~~~~~~~~~~~~~
include/linux/task_work.h:16:1: warning: empty declaration
16 | enum task_work_notify_mode {
| ^~~~
include/linux/task_work.h:25:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
25 | {
| ^
include/linux/task_work.h:34:67: error: expected declaration specifiers or '...' before 'task_work_func_t'
34 | struct callback_head *task_work_cancel_func(struct task_struct *, task_work_func_t);
| ^~~~~~~~~~~~~~~~
include/linux/task_work.h:39:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
39 | {
| ^
init/do_mounts.h:17:12: error: storage class specified for parameter 'root_mountflags'
17 | extern int root_mountflags;
| ^~~~~~~~~~~~~~~
init/do_mounts.h:20:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
20 | {
| ^
init/do_mounts.h:32:39: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
32 | static inline int rd_load_disk(int n) { return 0; }
| ^
init/do_mounts.h:33:45: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
33 | static inline int rd_load_image(char *from) { return 0; }
| ^
init/do_mounts.h:38:13: error: section attribute not allowed for 'initrd_load'
38 | bool __init initrd_load(char *root_device_name);
| ^~~~~~~~~~~
init/do_mounts.h:49:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
49 | {
| ^
init/do_mounts_initrd.c:17:21: error: storage class specified for parameter 'real_root_dev'
17 | static unsigned int real_root_dev; /* do_proc_dointvec cannot handle kdev_t */
| ^~~~~~~~~~~~~
init/do_mounts_initrd.c:18:23: error: storage class specified for parameter 'mount_initrd'
18 | static int __initdata mount_initrd = 1;
| ^~~~~~~~~~~~
init/do_mounts_initrd.c:18:1: error: parameter 'mount_initrd' is initialized
18 | static int __initdata mount_initrd = 1;
| ^~~~~~
init/do_mounts_initrd.c:18:23: error: section attribute not allowed for 'mount_initrd'
18 | static int __initdata mount_initrd = 1;
| ^~~~~~~~~~~~
init/do_mounts_initrd.c:20:13: error: section attribute not allowed for 'phys_initrd_start'
20 | phys_addr_t phys_initrd_start __initdata;
| ^~~~~~~~~~~~~~~~~
init/do_mounts_initrd.c:21:15: error: section attribute not allowed for 'phys_initrd_size'
21 | unsigned long phys_initrd_size __initdata;
| ^~~~~~~~~~~~~~~~
init/do_mounts_initrd.c:24:31: error: storage class specified for parameter 'kern_do_mounts_initrd_table'
24 | static const struct ctl_table kern_do_mounts_initrd_table[] = {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
init/do_mounts_initrd.c:24:21: error: parameter 'kern_do_mounts_initrd_table' is initialized
24 | static const struct ctl_table kern_do_mounts_initrd_table[] = {
| ^~~~~~~~~
>> init/do_mounts_initrd.c:25:9: warning: braces around scalar initializer
25 | {
| ^
init/do_mounts_initrd.c:25:9: note: (near initialization for 'kern_do_mounts_initrd_table')
init/do_mounts_initrd.c:26:17: error: field name not in record or union initializer
26 | .procname = "real-root-dev",
| ^
init/do_mounts_initrd.c:26:17: note: (near initialization for 'kern_do_mounts_initrd_table')
init/do_mounts_initrd.c:26:35: error: initialization of 'const struct ctl_table *' from incompatible pointer type 'char *' [-Werror=incompatible-pointer-types]
26 | .procname = "real-root-dev",
| ^~~~~~~~~~~~~~~
init/do_mounts_initrd.c:26:35: note: (near initialization for 'kern_do_mounts_initrd_table')
init/do_mounts_initrd.c:27:17: error: field name not in record or union initializer
27 | .data = &real_root_dev,
| ^
init/do_mounts_initrd.c:27:17: note: (near initialization for 'kern_do_mounts_initrd_table')
>> init/do_mounts_initrd.c:27:35: warning: excess elements in scalar initializer
27 | .data = &real_root_dev,
| ^
init/do_mounts_initrd.c:27:35: note: (near initialization for 'kern_do_mounts_initrd_table')
init/do_mounts_initrd.c:28:17: error: field name not in record or union initializer
28 | .maxlen = sizeof(int),
| ^
init/do_mounts_initrd.c:28:17: note: (near initialization for 'kern_do_mounts_initrd_table')
init/do_mounts_initrd.c:28:35: warning: excess elements in scalar initializer
28 | .maxlen = sizeof(int),
| ^~~~~~
init/do_mounts_initrd.c:28:35: note: (near initialization for 'kern_do_mounts_initrd_table')
init/do_mounts_initrd.c:29:17: error: field name not in record or union initializer
29 | .mode = 0644,
| ^
init/do_mounts_initrd.c:29:17: note: (near initialization for 'kern_do_mounts_initrd_table')
init/do_mounts_initrd.c:29:35: warning: excess elements in scalar initializer
29 | .mode = 0644,
| ^~~~
init/do_mounts_initrd.c:29:35: note: (near initialization for 'kern_do_mounts_initrd_table')
init/do_mounts_initrd.c:30:17: error: field name not in record or union initializer
30 | .proc_handler = proc_dointvec,
| ^
init/do_mounts_initrd.c:30:17: note: (near initialization for 'kern_do_mounts_initrd_table')
init/do_mounts_initrd.c:30:35: warning: excess elements in scalar initializer
30 | .proc_handler = proc_dointvec,
| ^~~~~~~~~~~~~
init/do_mounts_initrd.c:30:35: note: (near initialization for 'kern_do_mounts_initrd_table')
init/do_mounts_initrd.c:35:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
35 | {
| ^
include/linux/compiler.h:166:45: error: storage class specified for parameter '__UNIQUE_ID___addressable_kernel_do_mounts_initrd_sysctls_init369'
166 | #define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
| ^~~~~~~~~~~~
include/linux/compiler_types.h:83:23: note: in definition of macro '___PASTE'
83 | #define ___PASTE(a,b) a##b
| ^
include/linux/compiler.h:166:29: note: in expansion of macro '__PASTE'
166 | #define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
| ^~~~~~~
include/linux/compiler_types.h:84:22: note: in expansion of macro '___PASTE'
84 | #define __PASTE(a,b) ___PASTE(a,b)
| ^~~~~~~~
include/linux/compiler.h:166:37: note: in expansion of macro '__PASTE'
166 | #define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
| ^~~~~~~
include/linux/compiler.h:286:9: note: in expansion of macro '__UNIQUE_ID'
286 | __UNIQUE_ID(__PASTE(__addressable_,sym)) = (void *)(uintptr_t)&sym;
| ^~~~~~~~~~~
include/linux/compiler.h:289:9: note: in expansion of macro '___ADDRESSABLE'
289 | ___ADDRESSABLE(sym, __section(".discard.addressable"))
| ^~~~~~~~~~~~~~
include/linux/init.h:256:9: note: in expansion of macro '__ADDRESSABLE'
256 | __ADDRESSABLE(fn)
| ^~~~~~~~~~~~~
include/linux/init.h:261:9: note: in expansion of macro '__define_initcall_stub'
261 | __define_initcall_stub(__stub, fn) \
| ^~~~~~~~~~~~~~~~~~~~~~
include/linux/init.h:274:9: note: in expansion of macro '____define_initcall'
274 | ____define_initcall(fn, \
| ^~~~~~~~~~~~~~~~~~~
include/linux/init.h:280:9: note: in expansion of macro '__unique_initcall'
280 | __unique_initcall(fn, id, __sec, __initcall_id(fn))
| ^~~~~~~~~~~~~~~~~
include/linux/init.h:282:35: note: in expansion of macro '___define_initcall'
282 | #define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
| ^~~~~~~~~~~~~~~~~~
include/linux/init.h:313:41: note: in expansion of macro '__define_initcall'
313 | #define late_initcall(fn) __define_initcall(fn, 7)
| ^~~~~~~~~~~~~~~~~
init/do_mounts_initrd.c:39:1: note: in expansion of macro 'late_initcall'
39 | late_initcall(kernel_do_mounts_initrd_sysctls_init);
| ^~~~~~~~~~~~~
init/do_mounts_initrd.c:39:1: error: parameter '__UNIQUE_ID___addressable_kernel_do_mounts_initrd_sysctls_init369' is initialized
>> init/do_mounts_initrd.c:39:1: warning: 'used' attribute ignored [-Wattributes]
include/linux/compiler.h:166:45: error: section attribute not allowed for '__UNIQUE_ID___addressable_kernel_do_mounts_initrd_sysctls_init369'
166 | #define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
| ^~~~~~~~~~~~
include/linux/compiler_types.h:83:23: note: in definition of macro '___PASTE'
83 | #define ___PASTE(a,b) a##b
| ^
include/linux/compiler.h:166:29: note: in expansion of macro '__PASTE'
166 | #define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
| ^~~~~~~
include/linux/compiler_types.h:84:22: note: in expansion of macro '___PASTE'
84 | #define __PASTE(a,b) ___PASTE(a,b)
| ^~~~~~~~
include/linux/compiler.h:166:37: note: in expansion of macro '__PASTE'
166 | #define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
| ^~~~~~~
include/linux/compiler.h:286:9: note: in expansion of macro '__UNIQUE_ID'
286 | __UNIQUE_ID(__PASTE(__addressable_,sym)) = (void *)(uintptr_t)&sym;
| ^~~~~~~~~~~
include/linux/compiler.h:289:9: note: in expansion of macro '___ADDRESSABLE'
289 | ___ADDRESSABLE(sym, __section(".discard.addressable"))
| ^~~~~~~~~~~~~~
include/linux/init.h:256:9: note: in expansion of macro '__ADDRESSABLE'
256 | __ADDRESSABLE(fn)
| ^~~~~~~~~~~~~
include/linux/init.h:261:9: note: in expansion of macro '__define_initcall_stub'
261 | __define_initcall_stub(__stub, fn) \
| ^~~~~~~~~~~~~~~~~~~~~~
include/linux/init.h:274:9: note: in expansion of macro '____define_initcall'
274 | ____define_initcall(fn, \
| ^~~~~~~~~~~~~~~~~~~
include/linux/init.h:280:9: note: in expansion of macro '__unique_initcall'
280 | __unique_initcall(fn, id, __sec, __initcall_id(fn))
| ^~~~~~~~~~~~~~~~~
include/linux/init.h:282:35: note: in expansion of macro '___define_initcall'
282 | #define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
| ^~~~~~~~~~~~~~~~~~
include/linux/init.h:313:41: note: in expansion of macro '__define_initcall'
313 | #define late_initcall(fn) __define_initcall(fn, 7)
| ^~~~~~~~~~~~~~~~~
init/do_mounts_initrd.c:39:1: note: in expansion of macro 'late_initcall'
39 | late_initcall(kernel_do_mounts_initrd_sysctls_init);
| ^~~~~~~~~~~~~
In file included from include/linux/array_size.h:5,
from include/linux/kernel.h:16:
init/do_mounts_initrd.c:39:15: error: 'kernel_do_mounts_initrd_sysctls_init' undeclared (first use in this function)
39 | late_initcall(kernel_do_mounts_initrd_sysctls_init);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler.h:286:72: note: in definition of macro '___ADDRESSABLE'
286 | __UNIQUE_ID(__PASTE(__addressable_,sym)) = (void *)(uintptr_t)&sym;
| ^~~
include/linux/init.h:256:9: note: in expansion of macro '__ADDRESSABLE'
256 | __ADDRESSABLE(fn)
| ^~~~~~~~~~~~~
include/linux/init.h:261:9: note: in expansion of macro '__define_initcall_stub'
261 | __define_initcall_stub(__stub, fn) \
| ^~~~~~~~~~~~~~~~~~~~~~
include/linux/init.h:274:9: note: in expansion of macro '____define_initcall'
274 | ____define_initcall(fn, \
| ^~~~~~~~~~~~~~~~~~~
include/linux/init.h:280:9: note: in expansion of macro '__unique_initcall'
280 | __unique_initcall(fn, id, __sec, __initcall_id(fn))
| ^~~~~~~~~~~~~~~~~
include/linux/init.h:282:35: note: in expansion of macro '___define_initcall'
282 | #define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
| ^~~~~~~~~~~~~~~~~~
include/linux/init.h:313:41: note: in expansion of macro '__define_initcall'
313 | #define late_initcall(fn) __define_initcall(fn, 7)
| ^~~~~~~~~~~~~~~~~
init/do_mounts_initrd.c:39:1: note: in expansion of macro 'late_initcall'
39 | late_initcall(kernel_do_mounts_initrd_sysctls_init);
| ^~~~~~~~~~~~~
init/do_mounts_initrd.c:39:15: note: each undeclared identifier is reported only once for each function it appears in
39 | late_initcall(kernel_do_mounts_initrd_sysctls_init);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler.h:286:72: note: in definition of macro '___ADDRESSABLE'
286 | __UNIQUE_ID(__PASTE(__addressable_,sym)) = (void *)(uintptr_t)&sym;
| ^~~
include/linux/init.h:256:9: note: in expansion of macro '__ADDRESSABLE'
256 | __ADDRESSABLE(fn)
| ^~~~~~~~~~~~~
include/linux/init.h:261:9: note: in expansion of macro '__define_initcall_stub'
261 | __define_initcall_stub(__stub, fn) \
| ^~~~~~~~~~~~~~~~~~~~~~
include/linux/init.h:274:9: note: in expansion of macro '____define_initcall'
274 | ____define_initcall(fn, \
| ^~~~~~~~~~~~~~~~~~~
include/linux/init.h:280:9: note: in expansion of macro '__unique_initcall'
280 | __unique_initcall(fn, id, __sec, __initcall_id(fn))
| ^~~~~~~~~~~~~~~~~
include/linux/init.h:282:35: note: in expansion of macro '___define_initcall'
282 | #define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
| ^~~~~~~~~~~~~~~~~~
include/linux/init.h:313:41: note: in expansion of macro '__define_initcall'
313 | #define late_initcall(fn) __define_initcall(fn, 7)
| ^~~~~~~~~~~~~~~~~
init/do_mounts_initrd.c:39:1: note: in expansion of macro 'late_initcall'
39 | late_initcall(kernel_do_mounts_initrd_sysctls_init);
| ^~~~~~~~~~~~~
In file included from include/linux/printk.h:6,
from include/linux/kernel.h:31:
..
vim +16 include/linux/stddef.h
6e218287432472 Richard Knutsson 2006-09-30 14
^1da177e4c3f41 Linus Torvalds 2005-04-16 15 #undef offsetof
14e83077d55ff4 Rasmus Villemoes 2022-03-23 @16 #define offsetof(TYPE, MEMBER) __builtin_offsetof(TYPE, MEMBER)
3876488444e712 Denys Vlasenko 2015-03-09 17
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [RFC 00/19] Kernel API Specification Framework
From: Dmitry Vyukov @ 2025-06-23 13:28 UTC (permalink / raw)
To: sashal; +Cc: kees, elver, linux-api, linux-kernel, tools, workflows
In-Reply-To: <aFNYQkbEctT6N0Hb@lappy>
Nice!
A bag of assorted comments:
1. I share the same concern of duplicating info.
If there are lots of duplication it may lead to failure of the whole effort
since folks won't update these and/or they will get out of sync.
If a syscall arg is e.g. umode_t, we already know that it's an integer
of that enum type, and that it's an input arg.
In syzkaller we have a Clang-tool:
https://github.com/google/syzkaller/blob/master/tools/syz-declextract/clangtool/declextract.cpp
that extracts a bunch of interfaces automatically:
https://raw.githubusercontent.com/google/syzkaller/refs/heads/master/sys/linux/auto.txt
Though, oviously that won't have user-readable string descriptions, can't be used as a source
of truth, and may be challenging to integrate into kernel build process.
Though, extracting some of that info automatically may be nice.
2. Does this framework ensure that the specified info about args is correct?
E.g. number of syscall args, and their types match the actual ones?
If such things are not tested/validated during build, I afraid they will be
riddled with bugs over time.
3. To reduce duplication we could use more type information, e.g. I was always
frustrated that close is just:
SYSCALL_DEFINE1(close, unsigned int, fd)
whereas if we would do:
typedef int fd_t;
SYSCALL_DEFINE1(close, fd_t, fd)
then all semantic info about the arg is already in the code.
4. If we specify e.g. error return values here with descirptions,
can that be used as the source of truth to generate man pages?
That would eliminate some duplication.
5. We have a long standing dream that kernel developers add fuzzing descirpions
along with new kernel interfaces. So far we got very few contributions to syzkaller
from kernel developers. This framework can serve as the way to do it, which is nice.
6. What's the goal of validation of the input arguments?
Kernel code must do this validation anyway, right.
Any non-trivial validation is hard, e.g. even for open the validation function
for file name would need to have access to flags and check file precense for
some flags combinations. That may add significant amount of non-trivial code
that duplicates main syscall logic, and that logic may also have bugs and
memory leaks.
7. One of the most useful uses of this framework that I see if testing kernel
behavior correctness. I wonder what properties we can test with these descirptions,
and if we can add more useful info for that purpose.
Argument validation does not help here (it's userspace bugs at best).
Return values potentially may be useful, e.g. if we see a return value that's
not specified, potentially it's a kernel bug.
Side-effects specification potentially can be used to detect logical kernel bugs,
e.g. if a syscall does not claim to change fs state, but it does, it's a bug.
Though, a more useful check should be failure/concurrency atomicity.
Namely, if a syscall claims to not alter state on failure, it shouldn't do so.
Concurrency atomicity means linearizability of concurrent syscalls
(side-effects match one of 2 possible orders of syscalls).
But for these we would need to add additional flags to the descriptions
that say that a syscall supports failure/concurrency atomicity.
8. It would be useful to have a mapping of file_operations to actual files in fs.
Otherwise the exposed info is not very actionable, since there is no way to understand
what actual file/fd the ioctl's can be applied to.
9. I see that syscalls and ioctls say:
KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE)
Can't we make this implicit? Are there any other options?
Similarly an ioctl description says it releases a mutex (.released = true,),
all ioctls/syscalls must release all acquired mutexes, no?
Generally, the less verbose the descriptions are, the higher chances of their survival.
+Marco also works static compiler-enforced lock checking annotations,
I wonder if they can be used to describe this in a more useful way.
^ permalink raw reply
* Re: [RFC 00/19] Kernel API Specification Framework
From: Cyril Hrubis @ 2025-06-24 14:06 UTC (permalink / raw)
To: Dmitry Vyukov
Cc: sashal, kees, elver, linux-api, linux-kernel, tools, workflows
In-Reply-To: <20250623132803.26760-1-dvyukov@google.com>
Hi!
> 6. What's the goal of validation of the input arguments?
> Kernel code must do this validation anyway, right.
> Any non-trivial validation is hard, e.g. even for open the validation function
> for file name would need to have access to flags and check file precense for
> some flags combinations. That may add significant amount of non-trivial code
> that duplicates main syscall logic, and that logic may also have bugs and
> memory leaks.
I was looking at that part and thinking that we could generate (at least
some) automated conformance tests based on this information. We could
make sure that invalid parameters are properly rejected. For open(),
some combinations would be difficuilt to model though, e.g. for
O_DIRECTORY the pathname is supposed to be a path to a directory and
also the file descriptor returned has different properties. Also O_CREAT
requires third parameter and changes which kinds of filepaths are
invalid. Demultiplexing syscalls like this is going to be difficult to
get right.
As for testing purposes, most of the time it would be enough just to say
something as "this parameter is an existing file". If we have this
information in a machine parseable format we can generate automatic
tests for various error conditions e.g. ELOOP, EACESS, ENAMETOOLONG,
ENOENT, ...
For paths we could have something as:
file:existing
file:notexisting
file:replaced|nonexisting
file:nonexisting|existing
dir:existing
dir:nonexisting
Then for open() syscall we can do:
flags=O_DIRECTORY path=dir:existing
flags=O_CREAT path=file:nonexisting|existing
flags=O_CREAT|O_EXCL path=file:nonexisting
...
You may wonder if such kind of tests are useful at all, since quite a
few of these errors are checked for and generated from a common
functions. There are at least two cases I can think of. First of all it
makes sure that errors are stable when particular function/subsystem is
rewritten. And it can also make sure that errors are consistent across
different implementation of the same functionality e.g. filesystems. I
remember that some of the less used FUSE filesystems returned puzzling
errors in certain corner cases.
Maybe it would be more useful to steer this towards a system that
annotates better the types for the syscall parameters and return values.
Something that would be an extension to a C types with a description on
how particular string or integer is interpreted.
> Side-effects specification potentially can be used to detect logical kernel bugs,
> e.g. if a syscall does not claim to change fs state, but it does, it's a bug.
> Though, a more useful check should be failure/concurrency atomicity.
> Namely, if a syscall claims to not alter state on failure, it shouldn't do so.
> Concurrency atomicity means linearizability of concurrent syscalls
> (side-effects match one of 2 possible orders of syscalls).
> But for these we would need to add additional flags to the descriptions
> that say that a syscall supports failure/concurrency atomicity.
>
> 8. It would be useful to have a mapping of file_operations to actual files in fs.
> Otherwise the exposed info is not very actionable, since there is no way to understand
> what actual file/fd the ioctl's can be applied to.
+1 There are many different kinds of file descriptors and they differ
wildy in what operations they support.
Maybe we would need a subclass for a file descriptor, something as:
fd:file
fd:timerfd
fd:pidfs
...
--
Cyril Hrubis
chrubis@suse.cz
^ permalink raw reply
* Re: [RFC 00/19] Kernel API Specification Framework
From: Dmitry Vyukov @ 2025-06-24 14:30 UTC (permalink / raw)
To: Cyril Hrubis
Cc: sashal, kees, elver, linux-api, linux-kernel, tools, workflows
In-Reply-To: <aFqw5-PO4MjsEdYU@yuki.lan>
On Tue, 24 Jun 2025 at 16:05, Cyril Hrubis <chrubis@suse.cz> wrote:
>
> Hi!
> > 6. What's the goal of validation of the input arguments?
> > Kernel code must do this validation anyway, right.
> > Any non-trivial validation is hard, e.g. even for open the validation function
> > for file name would need to have access to flags and check file precense for
> > some flags combinations. That may add significant amount of non-trivial code
> > that duplicates main syscall logic, and that logic may also have bugs and
> > memory leaks.
>
> I was looking at that part and thinking that we could generate (at least
> some) automated conformance tests based on this information. We could
> make sure that invalid parameters are properly rejected. For open(),
> some combinations would be difficuilt to model though, e.g. for
> O_DIRECTORY the pathname is supposed to be a path to a directory and
> also the file descriptor returned has different properties. Also O_CREAT
> requires third parameter and changes which kinds of filepaths are
> invalid. Demultiplexing syscalls like this is going to be difficult to
> get right.
>
> As for testing purposes, most of the time it would be enough just to say
> something as "this parameter is an existing file". If we have this
> information in a machine parseable format we can generate automatic
> tests for various error conditions e.g. ELOOP, EACESS, ENAMETOOLONG,
> ENOENT, ...
>
> For paths we could have something as:
>
> file:existing
> file:notexisting
> file:replaced|nonexisting
> file:nonexisting|existing
> dir:existing
> dir:nonexisting
>
> Then for open() syscall we can do:
>
> flags=O_DIRECTORY path=dir:existing
> flags=O_CREAT path=file:nonexisting|existing
> flags=O_CREAT|O_EXCL path=file:nonexisting
> ...
>
> You may wonder if such kind of tests are useful at all, since quite a
> few of these errors are checked for and generated from a common
> functions. There are at least two cases I can think of. First of all it
> makes sure that errors are stable when particular function/subsystem is
> rewritten. And it can also make sure that errors are consistent across
> different implementation of the same functionality e.g. filesystems. I
> remember that some of the less used FUSE filesystems returned puzzling
> errors in certain corner cases.
I am not following how this is related to the validation part of the
patch series. Can you elaborate?
Generation of such conformance tests would need info about parameter
types and their semantic meaning, not the validation part.
The conformance tests should test that actual syscall checking of
arguments, not the validation added by this framework.
> Maybe it would be more useful to steer this towards a system that
> annotates better the types for the syscall parameters and return values.
> Something that would be an extension to a C types with a description on
> how particular string or integer is interpreted.
+1
> > Side-effects specification potentially can be used to detect logical kernel bugs,
> > e.g. if a syscall does not claim to change fs state, but it does, it's a bug.
> > Though, a more useful check should be failure/concurrency atomicity.
> > Namely, if a syscall claims to not alter state on failure, it shouldn't do so.
> > Concurrency atomicity means linearizability of concurrent syscalls
> > (side-effects match one of 2 possible orders of syscalls).
> > But for these we would need to add additional flags to the descriptions
> > that say that a syscall supports failure/concurrency atomicity.
> >
> > 8. It would be useful to have a mapping of file_operations to actual files in fs.
> > Otherwise the exposed info is not very actionable, since there is no way to understand
> > what actual file/fd the ioctl's can be applied to.
>
> +1 There are many different kinds of file descriptors and they differ
> wildy in what operations they support.
>
> Maybe we would need a subclass for a file descriptor, something as:
>
> fd:file
> fd:timerfd
> fd:pidfs
FWIW syzkaller has this for the purpose of automatic generation of test inputs.
^ permalink raw reply
* [PATCH v3 0/3] lsm: introduce lsm_config_self_policy() and lsm_config_system_policy() syscalls
From: Maxime Bélair @ 2025-06-24 14:30 UTC (permalink / raw)
To: linux-security-module
Cc: john.johansen, paul, jmorris, serge, mic, kees,
stephen.smalley.work, casey, takedakn, penguin-kernel, song,
rdunlap, linux-api, apparmor, linux-kernel, Maxime Bélair
This patchset introduces two new syscalls: lsm_config_self_policy(),
lsm_config_system_policy() and the associated Linux Security Module hooks
security_lsm_config_*_policy(), providing a unified interface for loading
and managing LSM policies. These syscalls complement the existing per‑LSM
pseudo‑filesystem mechanism and work even when those filesystems are not
mounted or available.
With these new syscalls, users and administrators may lock down access to
the pseudo‑filesystem yet still manage LSM policies. Two tightly-scoped
entry points then replace the many file operations exposed by those
filesystems, significantly reducing the attack surface. This is
particularly useful in containers or processes already confined by
Landlock, where these pseudo‑filesystems are typically unavailable.
Because they provide a logical and unified interface, these syscalls are
simpler to use than several heterogeneous pseudo‑filesystems and avoid
edge cases such as partially loaded policies. They also eliminates VFS
overhead, yielding performance gains notably when many policies are
loaded, for instance at boot time.
This initial implementation is intentionally minimal to limit the scope
of changes. Currently, only policy loading is supported, and only
AppArmor registers this LSM hook. However, any LSM can adopt this
interface, and future patches could extend this syscall to support more
operations, such as replacing, removing, or querying loaded policies.
Landlock already provides three Landlock‑specific syscalls (e.g.
landlock_add_rule()) to restrict ambient rights for sets of processes
without touching any pseudo-filesystem. lsm_config_*_policy() generalizes
that approach to the entire LSM layer, so any module can choose to
support either or both of these syscalls, and expose its policy
operations through a uniform interface and reap the advantages outlined
above.
This patchset is available at [1], a minimal user space example
showing how to use lsm_config_system_policy with AppArmor is at [2] and a
performance benchmark of both syscalls is available at [3].
[1] https://github.com/emixam16/linux/tree/lsm_syscall
[2] https://gitlab.com/emixam16/apparmor/tree/lsm_syscall
[3] https://gitlab.com/-/snippets/4864908
---
Changes in v3
- Fix typos
Changes in v2
- Split lsm_manage_policy() into two distinct syscalls:
lsm_config_self_policy() and lsm_config_system_policy()
- The LSM hook now calls only the appropriate LSM (and not all LSMs)
- Add a configuration variable to limit the buffer size of these
syscalls
- AppArmor now allows stacking policies through lsm_config_self_policy()
and loading policies in any namespace through
lsm_config_system_policy()
---
Maxime Bélair (3):
Wire up lsm_config_self_policy and lsm_config_system_policy syscalls
lsm: introduce security_lsm_config_*_policy hooks
AppArmor: add support for lsm_config_self_policy and
lsm_config_system_policy
arch/alpha/kernel/syscalls/syscall.tbl | 2 +
arch/arm/tools/syscall.tbl | 2 +
arch/m68k/kernel/syscalls/syscall.tbl | 2 +
arch/microblaze/kernel/syscalls/syscall.tbl | 2 +
arch/mips/kernel/syscalls/syscall_n32.tbl | 2 +
arch/mips/kernel/syscalls/syscall_n64.tbl | 2 +
arch/mips/kernel/syscalls/syscall_o32.tbl | 2 +
arch/parisc/kernel/syscalls/syscall.tbl | 2 +
arch/powerpc/kernel/syscalls/syscall.tbl | 2 +
arch/s390/kernel/syscalls/syscall.tbl | 2 +
arch/sh/kernel/syscalls/syscall.tbl | 2 +
arch/sparc/kernel/syscalls/syscall.tbl | 2 +
arch/x86/entry/syscalls/syscall_32.tbl | 2 +
arch/x86/entry/syscalls/syscall_64.tbl | 2 +
arch/xtensa/kernel/syscalls/syscall.tbl | 2 +
include/linux/lsm_hook_defs.h | 4 ++
include/linux/security.h | 18 +++++
include/linux/syscalls.h | 5 ++
include/uapi/asm-generic/unistd.h | 6 +-
include/uapi/linux/lsm.h | 8 +++
kernel/sys_ni.c | 2 +
security/Kconfig | 22 ++++++
security/apparmor/apparmorfs.c | 31 ++++++++
security/apparmor/include/apparmorfs.h | 3 +
security/apparmor/lsm.c | 71 +++++++++++++++++++
security/lsm_syscalls.c | 25 +++++++
security/security.c | 69 ++++++++++++++++++
tools/include/uapi/asm-generic/unistd.h | 6 +-
.../arch/x86/entry/syscalls/syscall_64.tbl | 2 +
29 files changed, 300 insertions(+), 2 deletions(-)
base-commit: 9c32cda43eb78f78c73aee4aa344b777714e259b
--
2.48.1
^ permalink raw reply
* [PATCH v3 1/3] Wire up lsm_config_self_policy and lsm_config_system_policy syscalls
From: Maxime Bélair @ 2025-06-24 14:30 UTC (permalink / raw)
To: linux-security-module
Cc: john.johansen, paul, jmorris, serge, mic, kees,
stephen.smalley.work, casey, takedakn, penguin-kernel, song,
rdunlap, linux-api, apparmor, linux-kernel, Maxime Bélair
In-Reply-To: <20250624143211.436045-1-maxime.belair@canonical.com>
Add support for the new lsm_config_self_policy and
lsm_config_system_policy syscalls, providing a unified API for loading
and modifying LSM policies, for the current user and for the entire
system, respectively without requiring the LSM’s pseudo-filesystems.
Benefits:
- Works even if the LSM pseudo-filesystem isn’t mounted or available
(e.g. in containers)
- Offers a logical and unified interface rather than multiple
heterogeneous pseudo-filesystems
- Avoids the overhead of other kernel interfaces for better efficiency
Signed-off-by: Maxime Bélair <maxime.belair@canonical.com>
---
arch/alpha/kernel/syscalls/syscall.tbl | 2 ++
arch/arm/tools/syscall.tbl | 2 ++
arch/m68k/kernel/syscalls/syscall.tbl | 2 ++
arch/microblaze/kernel/syscalls/syscall.tbl | 2 ++
arch/mips/kernel/syscalls/syscall_n32.tbl | 2 ++
arch/mips/kernel/syscalls/syscall_n64.tbl | 2 ++
arch/mips/kernel/syscalls/syscall_o32.tbl | 2 ++
arch/parisc/kernel/syscalls/syscall.tbl | 2 ++
arch/powerpc/kernel/syscalls/syscall.tbl | 2 ++
arch/s390/kernel/syscalls/syscall.tbl | 2 ++
arch/sh/kernel/syscalls/syscall.tbl | 2 ++
arch/sparc/kernel/syscalls/syscall.tbl | 2 ++
arch/x86/entry/syscalls/syscall_32.tbl | 2 ++
arch/x86/entry/syscalls/syscall_64.tbl | 2 ++
arch/xtensa/kernel/syscalls/syscall.tbl | 2 ++
include/linux/syscalls.h | 5 +++++
include/uapi/asm-generic/unistd.h | 6 +++++-
kernel/sys_ni.c | 2 ++
security/lsm_syscalls.c | 12 ++++++++++++
tools/include/uapi/asm-generic/unistd.h | 6 +++++-
tools/perf/arch/x86/entry/syscalls/syscall_64.tbl | 2 ++
21 files changed, 61 insertions(+), 2 deletions(-)
diff --git a/arch/alpha/kernel/syscalls/syscall.tbl b/arch/alpha/kernel/syscalls/syscall.tbl
index 2dd6340de6b4..4fc75352220d 100644
--- a/arch/alpha/kernel/syscalls/syscall.tbl
+++ b/arch/alpha/kernel/syscalls/syscall.tbl
@@ -507,3 +507,5 @@
575 common listxattrat sys_listxattrat
576 common removexattrat sys_removexattrat
577 common open_tree_attr sys_open_tree_attr
+578 common lsm_config_self_policy sys_lsm_config_self_policy
+579 common lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/arm/tools/syscall.tbl b/arch/arm/tools/syscall.tbl
index 27c1d5ebcd91..326483cb94a4 100644
--- a/arch/arm/tools/syscall.tbl
+++ b/arch/arm/tools/syscall.tbl
@@ -482,3 +482,5 @@
465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr
+468 common lsm_config_self_policy sys_lsm_config_self_policy
+469 common lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/m68k/kernel/syscalls/syscall.tbl b/arch/m68k/kernel/syscalls/syscall.tbl
index 9fe47112c586..d37364df1cd7 100644
--- a/arch/m68k/kernel/syscalls/syscall.tbl
+++ b/arch/m68k/kernel/syscalls/syscall.tbl
@@ -467,3 +467,5 @@
465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr
+468 common lsm_config_self_policy sys_lsm_config_self_policy
+469 common lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/microblaze/kernel/syscalls/syscall.tbl b/arch/microblaze/kernel/syscalls/syscall.tbl
index 7b6e97828e55..9d58ebfcf967 100644
--- a/arch/microblaze/kernel/syscalls/syscall.tbl
+++ b/arch/microblaze/kernel/syscalls/syscall.tbl
@@ -473,3 +473,5 @@
465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr
+468 common lsm_config_self_policy sys_lsm_config_self_policy
+469 common lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/mips/kernel/syscalls/syscall_n32.tbl b/arch/mips/kernel/syscalls/syscall_n32.tbl
index aa70e371bb54..8627b5f56280 100644
--- a/arch/mips/kernel/syscalls/syscall_n32.tbl
+++ b/arch/mips/kernel/syscalls/syscall_n32.tbl
@@ -406,3 +406,5 @@
465 n32 listxattrat sys_listxattrat
466 n32 removexattrat sys_removexattrat
467 n32 open_tree_attr sys_open_tree_attr
+468 n32 lsm_config_self_policy sys_lsm_config_self_policy
+469 n32 lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/mips/kernel/syscalls/syscall_n64.tbl b/arch/mips/kernel/syscalls/syscall_n64.tbl
index 1e8c44c7b614..813207b61f58 100644
--- a/arch/mips/kernel/syscalls/syscall_n64.tbl
+++ b/arch/mips/kernel/syscalls/syscall_n64.tbl
@@ -382,3 +382,5 @@
465 n64 listxattrat sys_listxattrat
466 n64 removexattrat sys_removexattrat
467 n64 open_tree_attr sys_open_tree_attr
+468 n64 lsm_config_self_policy sys_lsm_config_self_policy
+469 n64 lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/mips/kernel/syscalls/syscall_o32.tbl b/arch/mips/kernel/syscalls/syscall_o32.tbl
index 114a5a1a6230..9cd0946b4370 100644
--- a/arch/mips/kernel/syscalls/syscall_o32.tbl
+++ b/arch/mips/kernel/syscalls/syscall_o32.tbl
@@ -455,3 +455,5 @@
465 o32 listxattrat sys_listxattrat
466 o32 removexattrat sys_removexattrat
467 o32 open_tree_attr sys_open_tree_attr
+468 o32 lsm_config_self_policy sys_lsm_config_self_policy
+469 o32 lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl
index 94df3cb957e9..9db01dd55793 100644
--- a/arch/parisc/kernel/syscalls/syscall.tbl
+++ b/arch/parisc/kernel/syscalls/syscall.tbl
@@ -466,3 +466,5 @@
465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr
+468 common lsm_config_self_policy sys_lsm_config_self_policy
+469 common lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/powerpc/kernel/syscalls/syscall.tbl b/arch/powerpc/kernel/syscalls/syscall.tbl
index 9a084bdb8926..97714acb39ab 100644
--- a/arch/powerpc/kernel/syscalls/syscall.tbl
+++ b/arch/powerpc/kernel/syscalls/syscall.tbl
@@ -558,3 +558,5 @@
465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr
+468 common lsm_config_self_policy sys_lsm_config_self_policy
+469 common lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/s390/kernel/syscalls/syscall.tbl b/arch/s390/kernel/syscalls/syscall.tbl
index a4569b96ef06..d2b0f14fb516 100644
--- a/arch/s390/kernel/syscalls/syscall.tbl
+++ b/arch/s390/kernel/syscalls/syscall.tbl
@@ -470,3 +470,5 @@
465 common listxattrat sys_listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr sys_open_tree_attr
+468 common lsm_config_self_policy sys_lsm_config_self_policy sys_lsm_config_self_policy
+469 common lsm_config_system_policy sys_lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/sh/kernel/syscalls/syscall.tbl b/arch/sh/kernel/syscalls/syscall.tbl
index 52a7652fcff6..210d7118ce16 100644
--- a/arch/sh/kernel/syscalls/syscall.tbl
+++ b/arch/sh/kernel/syscalls/syscall.tbl
@@ -471,3 +471,5 @@
465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr
+468 common lsm_config_self_policy sys_lsm_config_self_policy
+469 common lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl
index 83e45eb6c095..494417d80680 100644
--- a/arch/sparc/kernel/syscalls/syscall.tbl
+++ b/arch/sparc/kernel/syscalls/syscall.tbl
@@ -513,3 +513,5 @@
465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr
+468 common lsm_config_self_policy sys_lsm_config_self_policy
+469 common lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index ac007ea00979..36c2c538e04f 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -473,3 +473,5 @@
465 i386 listxattrat sys_listxattrat
466 i386 removexattrat sys_removexattrat
467 i386 open_tree_attr sys_open_tree_attr
+468 i386 lsm_config_self_policy sys_lsm_config_self_policy
+469 i386 lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index cfb5ca41e30d..7eefbccfe531 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -391,6 +391,8 @@
465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr
+468 common lsm_config_self_policy sys_lsm_config_self_policy
+469 common lsm_config_system_policy sys_lsm_config_system_policy
#
# Due to a historical design error, certain syscalls are numbered differently
diff --git a/arch/xtensa/kernel/syscalls/syscall.tbl b/arch/xtensa/kernel/syscalls/syscall.tbl
index f657a77314f8..90d86a54a952 100644
--- a/arch/xtensa/kernel/syscalls/syscall.tbl
+++ b/arch/xtensa/kernel/syscalls/syscall.tbl
@@ -438,3 +438,5 @@
465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr
+468 common lsm_config_self_policy sys_lsm_config_self_policy
+469 common lsm_config_system_policy sys_lsm_config_system_policy
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index e5603cc91963..15b0f35c42fe 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -988,6 +988,11 @@ asmlinkage long sys_lsm_get_self_attr(unsigned int attr, struct lsm_ctx __user *
asmlinkage long sys_lsm_set_self_attr(unsigned int attr, struct lsm_ctx __user *ctx,
u32 size, u32 flags);
asmlinkage long sys_lsm_list_modules(u64 __user *ids, u32 __user *size, u32 flags);
+asmlinkage long sys_lsm_config_self_policy(u32 lsm_id, u32 op, void __user *buf,
+ u32 __user *size, u32 flags);
+asmlinkage long sys_lsm_config_system_policy(u32 lsm_id, u32 op, void __user *buf,
+ u32 __user *size, u32 flags);
+
/*
* Architecture-specific system calls
diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
index 2892a45023af..34278cc6a476 100644
--- a/include/uapi/asm-generic/unistd.h
+++ b/include/uapi/asm-generic/unistd.h
@@ -851,9 +851,13 @@ __SYSCALL(__NR_listxattrat, sys_listxattrat)
__SYSCALL(__NR_removexattrat, sys_removexattrat)
#define __NR_open_tree_attr 467
__SYSCALL(__NR_open_tree_attr, sys_open_tree_attr)
+#define __NR_lsm_config_self_policy 468
+__SYSCALL(__NR_lsm_config_self_policy, lsm_config_self_policy)
+#define __NR_lsm_config_system_policy 469
+__SYSCALL(__NR_lsm_config_system_policy, lsm_config_system_policy)
#undef __NR_syscalls
-#define __NR_syscalls 468
+#define __NR_syscalls 470
/*
* 32 bit systems traditionally used different
diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
index c00a86931f8c..3ecebcd3fbe0 100644
--- a/kernel/sys_ni.c
+++ b/kernel/sys_ni.c
@@ -172,6 +172,8 @@ COND_SYSCALL_COMPAT(fadvise64_64);
COND_SYSCALL(lsm_get_self_attr);
COND_SYSCALL(lsm_set_self_attr);
COND_SYSCALL(lsm_list_modules);
+COND_SYSCALL(lsm_config_self_policy);
+COND_SYSCALL(lsm_config_system_policy);
/* CONFIG_MMU only */
COND_SYSCALL(swapon);
diff --git a/security/lsm_syscalls.c b/security/lsm_syscalls.c
index 8440948a690c..a3cb6dab8102 100644
--- a/security/lsm_syscalls.c
+++ b/security/lsm_syscalls.c
@@ -118,3 +118,15 @@ SYSCALL_DEFINE3(lsm_list_modules, u64 __user *, ids, u32 __user *, size,
return lsm_active_cnt;
}
+
+SYSCALL_DEFINE5(lsm_config_self_policy, u32, lsm_id, u32, op, void __user *,
+ buf, u32 __user *, size, u32, flags)
+{
+ return 0;
+}
+
+SYSCALL_DEFINE5(lsm_config_system_policy, u32, lsm_id, u32, op, void __user *,
+ buf, u32 __user *, size, u32, flags)
+{
+ return 0;
+}
diff --git a/tools/include/uapi/asm-generic/unistd.h b/tools/include/uapi/asm-generic/unistd.h
index 2892a45023af..34278cc6a476 100644
--- a/tools/include/uapi/asm-generic/unistd.h
+++ b/tools/include/uapi/asm-generic/unistd.h
@@ -851,9 +851,13 @@ __SYSCALL(__NR_listxattrat, sys_listxattrat)
__SYSCALL(__NR_removexattrat, sys_removexattrat)
#define __NR_open_tree_attr 467
__SYSCALL(__NR_open_tree_attr, sys_open_tree_attr)
+#define __NR_lsm_config_self_policy 468
+__SYSCALL(__NR_lsm_config_self_policy, lsm_config_self_policy)
+#define __NR_lsm_config_system_policy 469
+__SYSCALL(__NR_lsm_config_system_policy, lsm_config_system_policy)
#undef __NR_syscalls
-#define __NR_syscalls 468
+#define __NR_syscalls 470
/*
* 32 bit systems traditionally used different
diff --git a/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl b/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl
index cfb5ca41e30d..7eefbccfe531 100644
--- a/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl
@@ -391,6 +391,8 @@
465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr
+468 common lsm_config_self_policy sys_lsm_config_self_policy
+469 common lsm_config_system_policy sys_lsm_config_system_policy
#
# Due to a historical design error, certain syscalls are numbered differently
--
2.48.1
^ permalink raw reply related
* [PATCH v3 2/3] lsm: introduce security_lsm_config_*_policy hooks
From: Maxime Bélair @ 2025-06-24 14:30 UTC (permalink / raw)
To: linux-security-module
Cc: john.johansen, paul, jmorris, serge, mic, kees,
stephen.smalley.work, casey, takedakn, penguin-kernel, song,
rdunlap, linux-api, apparmor, linux-kernel, Maxime Bélair
In-Reply-To: <20250624143211.436045-1-maxime.belair@canonical.com>
Define two new LSM hooks: security_lsm_config_self_policy and
security_lsm_config_system_policy and wire them into the corresponding
lsm_config_*_policy() syscalls so that LSMs can register a unified
interface for policy management. This initial, minimal implementation
only supports the LSM_POLICY_LOAD operation to limit changes.
Signed-off-by: Maxime Bélair <maxime.belair@canonical.com>
---
include/linux/lsm_hook_defs.h | 4 ++
include/linux/security.h | 18 +++++++++
include/uapi/linux/lsm.h | 8 ++++
security/Kconfig | 22 +++++++++++
security/lsm_syscalls.c | 17 ++++++++-
security/security.c | 69 +++++++++++++++++++++++++++++++++++
6 files changed, 136 insertions(+), 2 deletions(-)
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index bf3bbac4e02a..fca490444643 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -464,3 +464,7 @@ LSM_HOOK(int, 0, bdev_alloc_security, struct block_device *bdev)
LSM_HOOK(void, LSM_RET_VOID, bdev_free_security, struct block_device *bdev)
LSM_HOOK(int, 0, bdev_setintegrity, struct block_device *bdev,
enum lsm_integrity_type type, const void *value, size_t size)
+LSM_HOOK(int, -EINVAL, lsm_config_self_policy, u32 lsm_id, u32 op,
+ void __user *buf, size_t size, u32 flags)
+LSM_HOOK(int, -EINVAL, lsm_config_system_policy, u32 lsm_id, u32 op,
+ void __user *buf, size_t size, u32 flags)
diff --git a/include/linux/security.h b/include/linux/security.h
index cc9b54d95d22..8ad988b87be4 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -581,6 +581,11 @@ void security_bdev_free(struct block_device *bdev);
int security_bdev_setintegrity(struct block_device *bdev,
enum lsm_integrity_type type, const void *value,
size_t size);
+int security_lsm_config_self_policy(u32 lsm_id, u32 op, void __user *buf,
+ size_t size, u32 flags);
+int security_lsm_config_system_policy(u32 lsm_id, u32 op, void __user *buf,
+ size_t size, u32 flags);
+
#else /* CONFIG_SECURITY */
/**
@@ -1603,6 +1608,19 @@ static inline int security_bdev_setintegrity(struct block_device *bdev,
return 0;
}
+static int security_lsm_config_self_policy(u32 lsm_id, u32 op, void __user *buf,
+ size_t size, u32 flags)
+{
+
+ return -EOPNOTSUPP;
+}
+
+static int security_lsm_config_system_policy(u32 lsm_id, u32 op, void __user *buf,
+ size_t size, u32 flags)
+{
+
+ return -EOPNOTSUPP;
+}
#endif /* CONFIG_SECURITY */
#if defined(CONFIG_SECURITY) && defined(CONFIG_WATCH_QUEUE)
diff --git a/include/uapi/linux/lsm.h b/include/uapi/linux/lsm.h
index 938593dfd5da..844279f819ce 100644
--- a/include/uapi/linux/lsm.h
+++ b/include/uapi/linux/lsm.h
@@ -90,4 +90,12 @@ struct lsm_ctx {
*/
#define LSM_FLAG_SINGLE 0x0001
+/*
+ * LSM_POLICY_XXX definitions identify the different operations
+ * configure lsm policies
+ */
+
+#define LSM_POLICY_UNDEF 0
+#define LSM_POLICY_LOAD 100
+
#endif /* _UAPI_LINUX_LSM_H */
diff --git a/security/Kconfig b/security/Kconfig
index 4816fc74f81e..b194f7f7a7c2 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -220,6 +220,28 @@ config STATIC_USERMODEHELPER_PATH
If you wish for all usermode helper programs to be disabled,
specify an empty string here (i.e. "").
+config LSM_CONFIG_SELF_POLICY_MAX_BUFFER_SIZE
+ int "Maximum buffer size for lsm_config_self_policy"
+ range 16384 1073741824
+ depends on SECURITY
+ default 4194304
+ help
+ The maximum size of the buffer argument of lsm_config_self_policy.
+
+ The default value of 4194304 (4MiB) is reasonable and should be large
+ enough to fit policies in for most cases.
+
+config LSM_CONFIG_SYSTEM_POLICY_MAX_BUFFER_SIZE
+ int "Maximum buffer size for lsm_config_system_policy"
+ range 16384 1073741824
+ depends on SECURITY
+ default 4194304
+ help
+ The maximum size of the buffer argument of lsm_config_system_policy.
+
+ The default value of 4194304 (4MiB) is reasonable and should be large
+ enough to fit policies in for most cases
+
source "security/selinux/Kconfig"
source "security/smack/Kconfig"
source "security/tomoyo/Kconfig"
diff --git a/security/lsm_syscalls.c b/security/lsm_syscalls.c
index a3cb6dab8102..dd016ba6976c 100644
--- a/security/lsm_syscalls.c
+++ b/security/lsm_syscalls.c
@@ -122,11 +122,24 @@ SYSCALL_DEFINE3(lsm_list_modules, u64 __user *, ids, u32 __user *, size,
SYSCALL_DEFINE5(lsm_config_self_policy, u32, lsm_id, u32, op, void __user *,
buf, u32 __user *, size, u32, flags)
{
- return 0;
+ size_t usize;
+
+ if (get_user(usize, size))
+ return -EFAULT;
+
+ return security_lsm_config_self_policy(lsm_id, op, buf, usize, flags);
}
SYSCALL_DEFINE5(lsm_config_system_policy, u32, lsm_id, u32, op, void __user *,
buf, u32 __user *, size, u32, flags)
{
- return 0;
+ size_t usize;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (get_user(usize, size))
+ return -EFAULT;
+
+ return security_lsm_config_system_policy(lsm_id, op, buf, usize, flags);
}
diff --git a/security/security.c b/security/security.c
index fb57e8fddd91..15bf3dab7892 100644
--- a/security/security.c
+++ b/security/security.c
@@ -5883,6 +5883,75 @@ int security_bdev_setintegrity(struct block_device *bdev,
}
EXPORT_SYMBOL(security_bdev_setintegrity);
+/**
+ * security_lsm_config_self_policy() - Configure caller's LSM policies
+ * @lsm_id: id of the LSM to target
+ * @op: Operation to perform (one of the LSM_POLICY_XXX values)
+ * @buf: userspace pointer to policy data
+ * @size: size of @buf
+ * @flags: lsm policy configuration flags
+ *
+ * Configure the policies of a LSM for the current domain/user. This notably
+ * allows to update them even when the lsmfs is unavailable or restricted.
+ * Currently, only LSM_POLICY_LOAD is supported.
+ *
+ * Return: Returns 0 on success, error on failure.
+ */
+int security_lsm_config_self_policy(u32 lsm_id, u32 op, void __user *buf,
+ size_t size, u32 flags)
+{
+ int rc = LSM_RET_DEFAULT(lsm_config_self_policy);
+ struct lsm_static_call *scall;
+
+ if (size > (CONFIG_LSM_CONFIG_SELF_POLICY_MAX_BUFFER_SIZE))
+ return -E2BIG;
+
+ lsm_for_each_hook(scall, lsm_config_self_policy) {
+ if ((scall->hl->lsmid->id) == lsm_id) {
+ rc = scall->hl->hook.lsm_config_self_policy(lsm_id, op, buf, size, flags);
+ break;
+ }
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(security_lsm_config_self_policy);
+
+/**
+ * security_lsm_config_system_policy() - Configure system LSM policies
+ * @lsm_id: id of the lsm to target
+ * @op: Operation to perform (one of the LSM_POLICY_XXX values)
+ * @buf: userspace pointer to policy data
+ * @size: size of @buf
+ * @flags: lsm policy configuration flags
+ *
+ * Configure the policies of a LSM for the whole system. This notably allows
+ * to update them even when the lsmfs is unavailable or restricted. Currently,
+ * only LSM_POLICY_LOAD is supported.
+ *
+ * Return: Returns 0 on success, error on failure.
+ */
+int security_lsm_config_system_policy(u32 lsm_id, u32 op, void __user *buf,
+ size_t size, u32 flags)
+{
+ int rc = LSM_RET_DEFAULT(lsm_config_system_policy);
+ struct lsm_static_call *scall;
+
+ if (size > (CONFIG_LSM_CONFIG_SYSTEM_POLICY_MAX_BUFFER_SIZE))
+ return -E2BIG;
+
+ lsm_for_each_hook(scall, lsm_config_system_policy) {
+ if ((scall->hl->lsmid->id) == lsm_id) {
+ rc = scall->hl->hook.lsm_config_system_policy(lsm_id, op, buf, size, flags);
+ break;
+ }
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(security_lsm_config_system_policy);
+
+
#ifdef CONFIG_PERF_EVENTS
/**
* security_perf_event_open() - Check if a perf event open is allowed
--
2.48.1
^ permalink raw reply related
* [PATCH v3 3/3] AppArmor: add support for lsm_config_self_policy and lsm_config_system_policy
From: Maxime Bélair @ 2025-06-24 14:30 UTC (permalink / raw)
To: linux-security-module
Cc: john.johansen, paul, jmorris, serge, mic, kees,
stephen.smalley.work, casey, takedakn, penguin-kernel, song,
rdunlap, linux-api, apparmor, linux-kernel, Maxime Bélair
In-Reply-To: <20250624143211.436045-1-maxime.belair@canonical.com>
Enable users to manage AppArmor policies through the new hooks
lsm_config_self_policy and lsm_config_system_policy.
lsm_config_self_policy allows stacking existing policies in the kernel.
This ensures that it can only further restrict the caller and can never
be used to gain new privileges.
lsm_config_system_policy allows loading or replacing AppArmor policies in
any AppArmor namespace.
Signed-off-by: Maxime Bélair <maxime.belair@canonical.com>
---
security/apparmor/apparmorfs.c | 31 +++++++++++
security/apparmor/include/apparmorfs.h | 3 ++
security/apparmor/lsm.c | 71 ++++++++++++++++++++++++++
3 files changed, 105 insertions(+)
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 6039afae4bfc..6df43299b045 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -439,6 +439,37 @@ static ssize_t policy_update(u32 mask, const char __user *buf, size_t size,
return error;
}
+/**
+ * aa_profile_load_ns_name - load a profile into the current namespace identified by name
+ * @name: The name of the namesapce to load the policy in. "" for root_ns
+ * @name_size: size of @name. 0 For root ns
+ * @buf: buffer containing the user-provided policy
+ * @size: size of @buf
+ * @ppos: position pointer in the file
+ *
+ * Returns: 0 on success, negative value on error
+ */
+ssize_t aa_profile_load_ns_name(char *name, size_t name_size, const void __user *buf,
+ size_t size, loff_t *ppos)
+{
+ struct aa_ns *ns;
+
+ if (name_size == 0)
+ ns = aa_get_ns(root_ns);
+ else
+ ns = aa_lookupn_ns(root_ns, name, name_size);
+
+ if (!ns)
+ return -EINVAL;
+
+ int error = policy_update(AA_MAY_LOAD_POLICY | AA_MAY_REPLACE_POLICY,
+ buf, size, ppos, ns);
+
+ aa_put_ns(ns);
+
+ return error >= 0 ? 0 : error;
+}
+
/* .load file hook fn to load policy */
static ssize_t profile_load(struct file *f, const char __user *buf, size_t size,
loff_t *pos)
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h
index 1e94904f68d9..fd415afb7659 100644
--- a/security/apparmor/include/apparmorfs.h
+++ b/security/apparmor/include/apparmorfs.h
@@ -112,6 +112,9 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent);
void __aafs_ns_rmdir(struct aa_ns *ns);
int __aafs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name,
struct dentry *dent);
+ssize_t aa_profile_load_ns_name(char *name, size_t name_len, const void __user *buf,
+ size_t size, loff_t *ppos);
+
struct aa_loaddata;
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 9b6c2f157f83..7ca2eb8c0981 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -1275,6 +1275,73 @@ static int apparmor_socket_shutdown(struct socket *sock, int how)
return aa_sock_perm(OP_SHUTDOWN, AA_MAY_SHUTDOWN, sock);
}
+/**
+ * apparmor_lsm_config_self_policy - Stack a profile
+ * @lsm_id: AppArmor ID (LSM_ID_APPARMOR). Unused here
+ * @op: operation to perform. Currently, only LSM_POLICY_LOAD is supported
+ * @buf: buffer containing the user-provided name of the profile to stack
+ * @size: size of @buf
+ * @flags: reserved for future use; must be zero
+ *
+ * Returns: 0 on success, negative value on error
+ */
+static int apparmor_lsm_config_self_policy(u32 lsm_id, u32 op, void __user *buf,
+ size_t size, u32 flags)
+{
+ char *name = kvmalloc(size, GFP_KERNEL);
+ long name_size;
+ int ret;
+
+ if (!name)
+ return -ENOMEM;
+
+ if (op != LSM_POLICY_LOAD || flags)
+ return -EOPNOTSUPP;
+
+ name_size = strncpy_from_user(name, buf, size);
+ if (name_size < 0)
+ return name_size;
+
+ ret = aa_change_profile(name, AA_CHANGE_STACK);
+
+ kvfree(name);
+
+ return ret;
+}
+
+/**
+ * apparmor_lsm_config_system_policy - Load or replace a system policy
+ * @lsm_id: AppArmor ID (LSM_ID_APPARMOR). Unused here
+ * @op: operation to perform. Currently, only LSM_POLICY_LOAD is supported
+ * @buf: user-supplied buffer in the form "<ns>\0<policy>"
+ * <ns> is the namespace to load the policy into (empty string for root)
+ * <policy> is the policy to load
+ * @size: size of @buf
+ * @flags: reserved for future uses; must be zero
+ *
+ * Returns: 0 on success, negative value on error
+ */
+static int apparmor_lsm_config_system_policy(u32 lsm_id, u32 op, void __user *buf,
+ size_t size, u32 flags)
+{
+ loff_t pos = 0; // Partial writing is not currently supported
+ char name[256];
+ long name_size;
+
+ if (op != LSM_POLICY_LOAD || flags)
+ return -EOPNOTSUPP;
+
+ name_size = strncpy_from_user(name, buf, 256);
+ if (name_size < 0)
+ return name_size;
+ else if (name_size == 256)
+ return -E2BIG;
+
+ return aa_profile_load_ns_name(name, name_size, buf + name_size + 1,
+ size - name_size - 1, &pos);
+}
+
+
#ifdef CONFIG_NETWORK_SECMARK
/**
* apparmor_socket_sock_rcv_skb - check perms before associating skb to sk
@@ -1483,6 +1550,10 @@ static struct security_hook_list apparmor_hooks[] __ro_after_init = {
LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt),
LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt),
LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown),
+
+ LSM_HOOK_INIT(lsm_config_self_policy, apparmor_lsm_config_self_policy),
+ LSM_HOOK_INIT(lsm_config_system_policy,
+ apparmor_lsm_config_system_policy),
#ifdef CONFIG_NETWORK_SECMARK
LSM_HOOK_INIT(socket_sock_rcv_skb, apparmor_socket_sock_rcv_skb),
#endif
--
2.48.1
^ permalink raw reply related
* Re: [RFC 00/19] Kernel API Specification Framework
From: Cyril Hrubis @ 2025-06-24 15:27 UTC (permalink / raw)
To: Dmitry Vyukov
Cc: sashal, kees, elver, linux-api, linux-kernel, tools, workflows
In-Reply-To: <CACT4Y+Youc3M0z0U9arrTgyOC1+UKytav4zObhjUXn8-RLThMQ@mail.gmail.com>
Hi!
> > You may wonder if such kind of tests are useful at all, since quite a
> > few of these errors are checked for and generated from a common
> > functions. There are at least two cases I can think of. First of all it
> > makes sure that errors are stable when particular function/subsystem is
> > rewritten. And it can also make sure that errors are consistent across
> > different implementation of the same functionality e.g. filesystems. I
> > remember that some of the less used FUSE filesystems returned puzzling
> > errors in certain corner cases.
>
> I am not following how this is related to the validation part of the
> patch series. Can you elaborate?
This part is me trying to explain that generated conformance tests would
be useful for development as well.
> Generation of such conformance tests would need info about parameter
> types and their semantic meaning, not the validation part.
> The conformance tests should test that actual syscall checking of
> arguments, not the validation added by this framework.
Exactly.
I do not think that it makes sense to encode the argument ranges and
functions to generate a valid syscall parameters into the kernel. Rather
than that the information should encoded in the extended types, if we do
that well enough we can generate combination of different valid and
invalid parameters for the tests based on that.
--
Cyril Hrubis
chrubis@suse.cz
^ permalink raw reply
* [RFC v2 00/22] Kernel API specification framework
From: Sasha Levin @ 2025-06-24 18:07 UTC (permalink / raw)
To: linux-kernel; +Cc: linux-doc, linux-api, workflows, tools, Sasha Levin
Hey folks,
This is a second attempt at a "Kernel API Specification" framework, addressing
the feedback from the initial RFC and expanding the scope to include sysfs
attribute specifications.
Motivation
==========
The Linux kernel has one fundamental promise to userspace: we don't
break it. This promise is the foundation of Linux's success, allowing
applications written decades ago to still run on modern kernels. Yet
despite this being our most important commitment, we lack reliable
mechanisms to detect when we're about to break this promise.
Currently, we rely on:
- Developer vigilance and review
- User reports after release (often too late)
- Limited testing that can't cover all API usage patterns
- Documentation that's often incomplete or outdated
This gap between our commitment and our tooling is a fundamental problem.
We have sophisticated tools to catch memory leaks, race conditions, and
other bugs, but no systematic way to catch API breakage before it impacts
users.
As the kernel continues to grow, so do the interfaces exposed by it. This
applies to both the userspace API as well as the numerous internal APIs.
Over the years, we've accumulated a lot of documentation, but it's
sometimes lacking, spread out, and often out of date.
In the same way that we have runtime and static checkers to validate that
code is correct, we need a way to validate that the *use* of the various
APIs is correct and that changes don't break existing contracts.
This work aims to provide:
1. A machine-readable format to describe APIs.
2. Runtime validation of API contracts.
3. Generation of documentation and other artifacts.
4. Improved tooling for API exploration and debugging.
5. Most importantly: automated detection of API breakage.
With formal API specifications, we can:
- Detect when patches change API behavior in incompatible ways
- Validate that error codes, parameter constraints, and return values
remain consistent across kernel versions
- Generate automated tests that verify API contracts
- Provide userspace with machine-readable guarantees about kernel behavior
- Catch subtle breakage (like removing error codes, changing semantics,
or tightening constraints) that manual review might miss
The idea is to have an in-kernel API specification format that can be used
by tools such as:
- Static analysis tools (checkpatch, sparse, Coccinelle) to detect API
contract violations at compile time
- CI/CD systems to automatically flag potential userspace breakage
- Runtime verification (API contract validation) during testing
- Tracing and debugging (better BPF/ftrace integration)
- Documentation generation (automated, always up-to-date)
- Userspace helpers (interceptors, mocking frameworks, etc.)
- Fuzzers (can detect API contract violations, not just kernel crashes)
Where are we now?
=================
This series introduces a framework that allows developers to declare API
specifications directly in their subsystem code. These specifications are
then:
1. Exported via debugfs (making them runtime queryable)
2. Compiled into the kernel binary (accessible to tools)
3. Used for runtime validation (when enabled)
The `kapi` tool in tools/kapi/ can extract API specifications from:
- Source code (by parsing the KAPI macros)
- Running kernel (via debugfs)
- vmlinux binary (for offline analysis)
It produces output in multiple formats (plain text, JSON, RST) for easy
integration with existing workflows.
Changes since v1
================
- Added sysfs attribute validation support (patches 19-20)
- Added socket() syscall specification (patch 21)
- Enhanced signal handling with new actions (QUEUE, DISCARD, TRANSFORM)
- Expanded all API specifications with more detailed constraints
- Improved error handling documentation across all patches
- Added network/socket infrastructure to core framework
- Plumbed in syscall runtime validation
Sasha Levin (22):
kernel/api: introduce kernel API specification framework
eventpoll: add API specification for epoll_create1
eventpoll: add API specification for epoll_create
eventpoll: add API specification for epoll_ctl
eventpoll: add API specification for epoll_wait
eventpoll: add API specification for epoll_pwait
eventpoll: add API specification for epoll_pwait2
exec: add API specification for execve
exec: add API specification for execveat
mm/mlock: add API specification for mlock
mm/mlock: add API specification for mlock2
mm/mlock: add API specification for mlockall
mm/mlock: add API specification for munlock
mm/mlock: add API specification for munlockall
kernel/api: add debugfs interface for kernel API specifications
kernel/api: add IOCTL specification infrastructure
fwctl: add detailed IOCTL API specifications
binder: add detailed IOCTL API specifications
kernel/api: Add sysfs validation support to kernel API specification
framework
block: sysfs API specifications
net/socket: add API specification for socket()
tools/kapi: Add kernel API specification extraction tool
Documentation/admin-guide/kernel-api-spec.rst | 699 +++++++
MAINTAINERS | 9 +
arch/um/kernel/dyn.lds.S | 3 +
arch/um/kernel/uml.lds.S | 3 +
arch/x86/kernel/vmlinux.lds.S | 3 +
block/blk-integrity.c | 131 ++
block/blk-sysfs.c | 243 +++
block/genhd.c | 99 +
drivers/android/binder.c | 701 +++++++
drivers/fwctl/main.c | 285 ++-
fs/eventpoll.c | 1163 +++++++++++
fs/exec.c | 702 +++++++
include/asm-generic/vmlinux.lds.h | 20 +
include/linux/kernel_api_spec.h | 1841 +++++++++++++++++
include/linux/syscall_api_spec.h | 137 ++
include/linux/syscalls.h | 38 +
init/Kconfig | 2 +
kernel/Makefile | 1 +
kernel/api/Kconfig | 55 +
kernel/api/Makefile | 13 +
kernel/api/ioctl_validation.c | 355 ++++
kernel/api/kapi_debugfs.c | 340 +++
kernel/api/kernel_api_spec.c | 1531 ++++++++++++++
mm/mlock.c | 774 +++++++
net/socket.c | 489 +++++
tools/kapi/.gitignore | 4 +
tools/kapi/Cargo.toml | 19 +
tools/kapi/src/extractor/debugfs.rs | 415 ++++
tools/kapi/src/extractor/mod.rs | 411 ++++
tools/kapi/src/extractor/source_parser.rs | 1625 +++++++++++++++
.../src/extractor/vmlinux/binary_utils.rs | 283 +++
tools/kapi/src/extractor/vmlinux/mod.rs | 989 +++++++++
tools/kapi/src/formatter/json.rs | 420 ++++
tools/kapi/src/formatter/mod.rs | 130 ++
tools/kapi/src/formatter/plain.rs | 465 +++++
tools/kapi/src/formatter/rst.rs | 468 +++++
tools/kapi/src/formatter/shall.rs | 605 ++++++
tools/kapi/src/main.rs | 130 ++
38 files changed, 15598 insertions(+), 3 deletions(-)
create mode 100644 Documentation/admin-guide/kernel-api-spec.rst
create mode 100644 include/linux/kernel_api_spec.h
create mode 100644 include/linux/syscall_api_spec.h
create mode 100644 kernel/api/Kconfig
create mode 100644 kernel/api/Makefile
create mode 100644 kernel/api/ioctl_validation.c
create mode 100644 kernel/api/kapi_debugfs.c
create mode 100644 kernel/api/kernel_api_spec.c
create mode 100644 tools/kapi/.gitignore
create mode 100644 tools/kapi/Cargo.toml
create mode 100644 tools/kapi/src/extractor/debugfs.rs
create mode 100644 tools/kapi/src/extractor/mod.rs
create mode 100644 tools/kapi/src/extractor/source_parser.rs
create mode 100644 tools/kapi/src/extractor/vmlinux/binary_utils.rs
create mode 100644 tools/kapi/src/extractor/vmlinux/mod.rs
create mode 100644 tools/kapi/src/formatter/json.rs
create mode 100644 tools/kapi/src/formatter/mod.rs
create mode 100644 tools/kapi/src/formatter/plain.rs
create mode 100644 tools/kapi/src/formatter/rst.rs
create mode 100644 tools/kapi/src/formatter/shall.rs
create mode 100644 tools/kapi/src/main.rs
--
2.39.5
^ permalink raw reply
* [RFC v2 01/22] kernel/api: introduce kernel API specification framework
From: Sasha Levin @ 2025-06-24 18:07 UTC (permalink / raw)
To: linux-kernel; +Cc: linux-doc, linux-api, workflows, tools, Sasha Levin
In-Reply-To: <20250624180742.5795-1-sashal@kernel.org>
Add a comprehensive framework for formally documenting kernel APIs with
inline specifications. This framework provides:
- Structured API documentation with parameter specifications, return
values, error conditions, and execution context requirements
- Runtime validation capabilities for debugging (CONFIG_KAPI_RUNTIME_CHECKS)
- Export of specifications via debugfs for tooling integration
- Support for both internal kernel APIs and system calls
The framework stores specifications in a dedicated ELF section and
provides infrastructure for:
- Compile-time validation of specifications
- Runtime querying of API documentation
- Machine-readable export formats
- Integration with existing SYSCALL_DEFINE macros
This commit introduces the core infrastructure without modifying any
existing APIs. Subsequent patches will add specifications to individual
subsystems.
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
Documentation/admin-guide/kernel-api-spec.rst | 507 ++++++
MAINTAINERS | 9 +
arch/um/kernel/dyn.lds.S | 3 +
arch/um/kernel/uml.lds.S | 3 +
arch/x86/kernel/vmlinux.lds.S | 3 +
include/asm-generic/vmlinux.lds.h | 20 +
include/linux/kernel_api_spec.h | 1513 +++++++++++++++++
include/linux/syscall_api_spec.h | 137 ++
include/linux/syscalls.h | 38 +
init/Kconfig | 2 +
kernel/Makefile | 1 +
kernel/api/Kconfig | 35 +
kernel/api/Makefile | 7 +
kernel/api/kernel_api_spec.c | 1122 ++++++++++++
14 files changed, 3400 insertions(+)
create mode 100644 Documentation/admin-guide/kernel-api-spec.rst
create mode 100644 include/linux/kernel_api_spec.h
create mode 100644 include/linux/syscall_api_spec.h
create mode 100644 kernel/api/Kconfig
create mode 100644 kernel/api/Makefile
create mode 100644 kernel/api/kernel_api_spec.c
diff --git a/Documentation/admin-guide/kernel-api-spec.rst b/Documentation/admin-guide/kernel-api-spec.rst
new file mode 100644
index 0000000000000..3a63f6711e27b
--- /dev/null
+++ b/Documentation/admin-guide/kernel-api-spec.rst
@@ -0,0 +1,507 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================================
+Kernel API Specification Framework
+======================================
+
+:Author: Sasha Levin <sashal@kernel.org>
+:Date: June 2025
+
+.. contents:: Table of Contents
+ :depth: 3
+ :local:
+
+Introduction
+============
+
+The Kernel API Specification Framework (KAPI) provides a comprehensive system for
+formally documenting, validating, and introspecting kernel APIs. This framework
+addresses the long-standing challenge of maintaining accurate, machine-readable
+documentation for the thousands of internal kernel APIs and system calls.
+
+Purpose and Goals
+-----------------
+
+The framework aims to:
+
+1. **Improve API Documentation**: Provide structured, inline documentation that
+ lives alongside the code and is maintained as part of the development process.
+
+2. **Enable Runtime Validation**: Optionally validate API usage at runtime to catch
+ common programming errors during development and testing.
+
+3. **Support Tooling**: Export API specifications in machine-readable formats for
+ use by static analyzers, documentation generators, and development tools.
+
+4. **Enhance Debugging**: Provide detailed API information at runtime through debugfs
+ for debugging and introspection.
+
+5. **Formalize Contracts**: Explicitly document API contracts including parameter
+ constraints, execution contexts, locking requirements, and side effects.
+
+Architecture Overview
+=====================
+
+Components
+----------
+
+The framework consists of several key components:
+
+1. **Core Framework** (``kernel/api/kernel_api_spec.c``)
+
+ - API specification registration and storage
+ - Runtime validation engine
+ - Specification lookup and querying
+
+2. **DebugFS Interface** (``kernel/api/kapi_debugfs.c``)
+
+ - Runtime introspection via ``/sys/kernel/debug/kapi/``
+ - JSON and XML export formats
+ - Per-API detailed information
+
+3. **IOCTL Support** (``kernel/api/ioctl_validation.c``)
+
+ - Extended framework for IOCTL specifications
+ - Automatic validation wrappers
+ - Structure field validation
+
+4. **Specification Macros** (``include/linux/kernel_api_spec.h``)
+
+ - Declarative macros for API documentation
+ - Type-safe parameter specifications
+ - Context and constraint definitions
+
+Data Model
+----------
+
+The framework uses a hierarchical data model::
+
+ kernel_api_spec
+ ├── Basic Information
+ │ ├── name (API function name)
+ │ ├── version (specification version)
+ │ ├── description (human-readable description)
+ │ └── kernel_version (when API was introduced)
+ │
+ ├── Parameters (up to 16)
+ │ └── kapi_param_spec
+ │ ├── name
+ │ ├── type (int, pointer, string, etc.)
+ │ ├── direction (in, out, inout)
+ │ ├── constraints (range, mask, enum values)
+ │ └── validation rules
+ │
+ ├── Return Value
+ │ └── kapi_return_spec
+ │ ├── type
+ │ ├── success conditions
+ │ └── validation rules
+ │
+ ├── Error Conditions (up to 32)
+ │ └── kapi_error_spec
+ │ ├── error code
+ │ ├── condition description
+ │ └── recovery advice
+ │
+ ├── Execution Context
+ │ ├── allowed contexts (process, interrupt, etc.)
+ │ ├── locking requirements
+ │ └── preemption/interrupt state
+ │
+ └── Side Effects
+ ├── memory allocation
+ ├── state changes
+ └── signal handling
+
+Usage Guide
+===========
+
+Basic API Specification
+-----------------------
+
+To document a kernel API, use the specification macros in the implementation file:
+
+.. code-block:: c
+
+ #include <linux/kernel_api_spec.h>
+
+ KAPI_DEFINE_SPEC(kmalloc_spec, kmalloc, "3.0")
+ KAPI_DESCRIPTION("Allocate kernel memory")
+ KAPI_PARAM(0, size, KAPI_TYPE_SIZE_T, KAPI_DIR_IN,
+ "Number of bytes to allocate")
+ KAPI_PARAM_RANGE(0, 0, KMALLOC_MAX_SIZE)
+ KAPI_PARAM(1, flags, KAPI_TYPE_FLAGS, KAPI_DIR_IN,
+ "Allocation flags (GFP_*)")
+ KAPI_PARAM_MASK(1, __GFP_BITS_MASK)
+ KAPI_RETURN(KAPI_TYPE_POINTER, "Pointer to allocated memory or NULL")
+ KAPI_ERROR(ENOMEM, "Out of memory")
+ KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SOFTIRQ | KAPI_CTX_HARDIRQ)
+ KAPI_SIDE_EFFECT("Allocates memory from kernel heap")
+ KAPI_LOCK_NOT_REQUIRED("Any lock")
+ KAPI_END_SPEC
+
+ void *kmalloc(size_t size, gfp_t flags)
+ {
+ /* Implementation */
+ }
+
+System Call Specification
+-------------------------
+
+System calls use specialized macros:
+
+.. code-block:: c
+
+ KAPI_DEFINE_SYSCALL_SPEC(open_spec, open, "1.0")
+ KAPI_DESCRIPTION("Open a file")
+ KAPI_PARAM(0, pathname, KAPI_TYPE_USER_STRING, KAPI_DIR_IN,
+ "Path to file")
+ KAPI_PARAM_PATH(0, PATH_MAX)
+ KAPI_PARAM(1, flags, KAPI_TYPE_FLAGS, KAPI_DIR_IN,
+ "Open flags (O_*)")
+ KAPI_PARAM(2, mode, KAPI_TYPE_MODE_T, KAPI_DIR_IN,
+ "File permissions (if creating)")
+ KAPI_RETURN(KAPI_TYPE_INT, "File descriptor or -1")
+ KAPI_ERROR(EACCES, "Permission denied")
+ KAPI_ERROR(ENOENT, "File does not exist")
+ KAPI_ERROR(EMFILE, "Too many open files")
+ KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE)
+ KAPI_SIGNAL(EINTR, "Open can be interrupted by signal")
+ KAPI_END_SYSCALL_SPEC
+
+IOCTL Specification
+-------------------
+
+IOCTLs have extended support for structure validation:
+
+.. code-block:: c
+
+ KAPI_DEFINE_IOCTL_SPEC(vidioc_querycap_spec, VIDIOC_QUERYCAP,
+ "VIDIOC_QUERYCAP",
+ sizeof(struct v4l2_capability),
+ sizeof(struct v4l2_capability),
+ "video_fops")
+ KAPI_DESCRIPTION("Query device capabilities")
+ KAPI_IOCTL_FIELD(driver, KAPI_TYPE_CHAR_ARRAY, KAPI_DIR_OUT,
+ "Driver name", 16)
+ KAPI_IOCTL_FIELD(card, KAPI_TYPE_CHAR_ARRAY, KAPI_DIR_OUT,
+ "Device name", 32)
+ KAPI_IOCTL_FIELD(version, KAPI_TYPE_U32, KAPI_DIR_OUT,
+ "Driver version")
+ KAPI_IOCTL_FIELD(capabilities, KAPI_TYPE_FLAGS, KAPI_DIR_OUT,
+ "Device capabilities")
+ KAPI_END_IOCTL_SPEC
+
+Runtime Validation
+==================
+
+Enabling Validation
+-------------------
+
+Runtime validation is controlled by kernel configuration:
+
+1. Enable ``CONFIG_KAPI_SPEC`` to build the framework
+2. Enable ``CONFIG_KAPI_RUNTIME_CHECKS`` for runtime validation
+3. Optionally enable ``CONFIG_KAPI_SPEC_DEBUGFS`` for debugfs interface
+
+Validation Modes
+----------------
+
+The framework supports several validation modes:
+
+.. code-block:: c
+
+ /* Enable validation for specific API */
+ kapi_enable_validation("kmalloc");
+
+ /* Enable validation for all APIs */
+ kapi_enable_all_validation();
+
+ /* Set validation level */
+ kapi_set_validation_level(KAPI_VALIDATE_FULL);
+
+Validation Levels:
+
+- ``KAPI_VALIDATE_NONE``: No validation
+- ``KAPI_VALIDATE_BASIC``: Type and NULL checks only
+- ``KAPI_VALIDATE_NORMAL``: Basic + range and constraint checks
+- ``KAPI_VALIDATE_FULL``: All checks including custom validators
+
+Custom Validators
+-----------------
+
+APIs can register custom validation functions:
+
+.. code-block:: c
+
+ static bool validate_buffer_size(const struct kapi_param_spec *spec,
+ const void *value, void *context)
+ {
+ size_t size = *(size_t *)value;
+ struct my_context *ctx = context;
+
+ return size > 0 && size <= ctx->max_buffer_size;
+ }
+
+ KAPI_PARAM_CUSTOM_VALIDATOR(0, validate_buffer_size)
+
+DebugFS Interface
+=================
+
+The debugfs interface provides runtime access to API specifications:
+
+Directory Structure
+-------------------
+
+::
+
+ /sys/kernel/debug/kapi/
+ ├── apis/ # All registered APIs
+ │ ├── kmalloc/
+ │ │ ├── specification # Human-readable spec
+ │ │ ├── json # JSON format
+ │ │ └── xml # XML format
+ │ └── open/
+ │ └── ...
+ ├── summary # Overview of all APIs
+ ├── validation/ # Validation controls
+ │ ├── enabled # Global enable/disable
+ │ ├── level # Validation level
+ │ └── stats # Validation statistics
+ └── export/ # Bulk export options
+ ├── all.json # All specs in JSON
+ └── all.xml # All specs in XML
+
+Usage Examples
+--------------
+
+Query specific API::
+
+ $ cat /sys/kernel/debug/kapi/apis/kmalloc/specification
+ API: kmalloc
+ Version: 3.0
+ Description: Allocate kernel memory
+
+ Parameters:
+ [0] size (size_t, in): Number of bytes to allocate
+ Range: 0 - 4194304
+ [1] flags (flags, in): Allocation flags (GFP_*)
+ Mask: 0x1ffffff
+
+ Returns: pointer - Pointer to allocated memory or NULL
+
+ Errors:
+ ENOMEM: Out of memory
+
+ Context: process, softirq, hardirq
+
+ Side Effects:
+ - Allocates memory from kernel heap
+
+Export all specifications::
+
+ $ cat /sys/kernel/debug/kapi/export/all.json > kernel-apis.json
+
+Enable validation for specific API::
+
+ $ echo 1 > /sys/kernel/debug/kapi/apis/kmalloc/validate
+
+Performance Considerations
+==========================
+
+Memory Overhead
+---------------
+
+Each API specification consumes approximately 2-4KB of memory. With thousands
+of kernel APIs, this can add up to several megabytes. Consider:
+
+1. Building with ``CONFIG_KAPI_SPEC=n`` for production kernels
+2. Using ``__init`` annotations for APIs only used during boot
+3. Implementing lazy loading for rarely used specifications
+
+Runtime Overhead
+----------------
+
+When ``CONFIG_KAPI_RUNTIME_CHECKS`` is enabled:
+
+- Each validated API call adds 50-200ns overhead
+- Complex validations (custom validators) may add more
+- Use validation only in development/testing kernels
+
+Optimization Strategies
+-----------------------
+
+1. **Compile-time optimization**: When validation is disabled, all
+ validation code is optimized away by the compiler.
+
+2. **Selective validation**: Enable validation only for specific APIs
+ or subsystems under test.
+
+3. **Caching**: The framework caches validation results for repeated
+ calls with identical parameters.
+
+Documentation Generation
+------------------------
+
+The framework exports specifications via debugfs that can be used
+to generate documentation. Tools for automatic documentation generation
+from specifications are planned for future development.
+
+IDE Integration
+---------------
+
+Modern IDEs can use the JSON export for:
+
+- Parameter hints
+- Type checking
+- Context validation
+- Error code documentation
+
+Testing Framework
+-----------------
+
+The framework includes test helpers::
+
+ #ifdef CONFIG_KAPI_TESTING
+ /* Verify API behaves according to specification */
+ kapi_test_api("kmalloc", test_cases);
+ #endif
+
+Best Practices
+==============
+
+Writing Specifications
+----------------------
+
+1. **Be Comprehensive**: Document all parameters, errors, and side effects
+2. **Keep Updated**: Update specs when API behavior changes
+3. **Use Examples**: Include usage examples in descriptions
+4. **Validate Constraints**: Define realistic constraints for parameters
+5. **Document Context**: Clearly specify allowed execution contexts
+
+Maintenance
+-----------
+
+1. **Version Specifications**: Increment version when API changes
+2. **Deprecation**: Mark deprecated APIs and suggest replacements
+3. **Cross-reference**: Link related APIs in descriptions
+4. **Test Specifications**: Verify specs match implementation
+
+Common Patterns
+---------------
+
+**Optional Parameters**::
+
+ KAPI_PARAM(2, optional_arg, KAPI_TYPE_POINTER, KAPI_DIR_IN,
+ "Optional argument (may be NULL)")
+ KAPI_PARAM_OPTIONAL(2)
+
+**Variable Arguments**::
+
+ KAPI_PARAM(1, fmt, KAPI_TYPE_FORMAT_STRING, KAPI_DIR_IN,
+ "Printf-style format string")
+ KAPI_PARAM_VARIADIC(2, "Format arguments")
+
+**Callback Functions**::
+
+ KAPI_PARAM(1, callback, KAPI_TYPE_FUNCTION_PTR, KAPI_DIR_IN,
+ "Callback function")
+ KAPI_PARAM_CALLBACK(1, "int (*)(void *data)", "data")
+
+Troubleshooting
+===============
+
+Common Issues
+-------------
+
+**Specification Not Found**::
+
+ kernel: KAPI: Specification for 'my_api' not found
+
+ Solution: Ensure KAPI_DEFINE_SPEC is in the same translation unit
+ as the function implementation.
+
+**Validation Failures**::
+
+ kernel: KAPI: Validation failed for kmalloc parameter 'size':
+ value 5242880 exceeds maximum 4194304
+
+ Solution: Check parameter constraints or adjust specification if
+ the constraint is incorrect.
+
+**Build Errors**::
+
+ error: 'KAPI_TYPE_UNKNOWN' undeclared
+
+ Solution: Include <linux/kernel_api_spec.h> and ensure
+ CONFIG_KAPI_SPEC is enabled.
+
+Debug Options
+-------------
+
+Enable verbose debugging::
+
+ echo 8 > /proc/sys/kernel/printk
+ echo 1 > /sys/kernel/debug/kapi/debug/verbose
+
+Future Directions
+=================
+
+Planned Features
+----------------
+
+1. **Automatic Extraction**: Tool to extract specifications from existing
+ kernel-doc comments
+
+2. **Contract Verification**: Static analysis to verify implementation
+ matches specification
+
+3. **Performance Profiling**: Measure actual API performance against
+ documented expectations
+
+4. **Fuzzing Integration**: Use specifications to guide intelligent
+ fuzzing of kernel APIs
+
+5. **Version Compatibility**: Track API changes across kernel versions
+
+Research Areas
+--------------
+
+1. **Formal Verification**: Use specifications for mathematical proofs
+ of correctness
+
+2. **Runtime Monitoring**: Detect specification violations in production
+ with minimal overhead
+
+3. **API Evolution**: Analyze how kernel APIs change over time
+
+4. **Security Applications**: Use specifications for security policy
+ enforcement
+
+Contributing
+============
+
+Submitting Specifications
+-------------------------
+
+1. Add specifications to the same file as the API implementation
+2. Follow existing patterns and naming conventions
+3. Test with CONFIG_KAPI_RUNTIME_CHECKS enabled
+4. Verify debugfs output is correct
+5. Run scripts/checkpatch.pl on your changes
+
+Review Criteria
+---------------
+
+Specifications will be reviewed for:
+
+1. **Completeness**: All parameters and errors documented
+2. **Accuracy**: Specification matches implementation
+3. **Clarity**: Descriptions are clear and helpful
+4. **Consistency**: Follows framework conventions
+5. **Performance**: No unnecessary runtime overhead
+
+Contact
+-------
+
+- Maintainer: Sasha Levin <sashal@kernel.org>
diff --git a/MAINTAINERS b/MAINTAINERS
index c3f7fbd0d67af..759f6c0b9a4dd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13047,6 +13047,15 @@ W: https://linuxtv.org
T: git git://linuxtv.org/media.git
F: drivers/media/radio/radio-keene*
+KERNEL API SPECIFICATION FRAMEWORK (KAPI)
+M: Sasha Levin <sashal@kernel.org>
+L: linux-api@vger.kernel.org
+S: Maintained
+F: Documentation/admin-guide/kernel-api-spec.rst
+F: include/linux/kernel_api_spec.h
+F: kernel/api/
+F: scripts/extract-kapi-spec.sh
+
KERNEL AUTOMOUNTER
M: Ian Kent <raven@themaw.net>
L: autofs@vger.kernel.org
diff --git a/arch/um/kernel/dyn.lds.S b/arch/um/kernel/dyn.lds.S
index a36b7918a011a..283ab11788d8c 100644
--- a/arch/um/kernel/dyn.lds.S
+++ b/arch/um/kernel/dyn.lds.S
@@ -102,6 +102,9 @@ SECTIONS
init.data : { INIT_DATA }
__init_end = .;
+ /* Kernel API specifications in dedicated section */
+ KAPI_SPECS_SECTION()
+
/* Ensure the __preinit_array_start label is properly aligned. We
could instead move the label definition inside the section, but
the linker would then create the section even if it turns out to
diff --git a/arch/um/kernel/uml.lds.S b/arch/um/kernel/uml.lds.S
index a409d4b66114f..e3850d8293436 100644
--- a/arch/um/kernel/uml.lds.S
+++ b/arch/um/kernel/uml.lds.S
@@ -74,6 +74,9 @@ SECTIONS
init.data : { INIT_DATA }
__init_end = .;
+ /* Kernel API specifications in dedicated section */
+ KAPI_SPECS_SECTION()
+
.data :
{
INIT_TASK_DATA(KERNEL_STACK_SIZE)
diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S
index 4fa0be732af10..8cc508adc9d51 100644
--- a/arch/x86/kernel/vmlinux.lds.S
+++ b/arch/x86/kernel/vmlinux.lds.S
@@ -173,6 +173,9 @@ SECTIONS
RO_DATA(PAGE_SIZE)
X86_ALIGN_RODATA_END
+ /* Kernel API specifications in dedicated section */
+ KAPI_SPECS_SECTION()
+
/* Data */
.data : AT(ADDR(.data) - LOAD_OFFSET) {
/* Start of data section */
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index fa5f19b8d53a0..7b47736057e01 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -279,6 +279,26 @@ defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG)
#define TRACE_SYSCALLS()
#endif
+#ifdef CONFIG_KAPI_SPEC
+#define KAPI_SPECS() \
+ . = ALIGN(8); \
+ __start_kapi_specs = .; \
+ KEEP(*(.kapi_specs)) \
+ __stop_kapi_specs = .;
+
+/* For placing KAPI specs in a dedicated section */
+#define KAPI_SPECS_SECTION() \
+ .kapi_specs : AT(ADDR(.kapi_specs) - LOAD_OFFSET) { \
+ . = ALIGN(8); \
+ __start_kapi_specs = .; \
+ KEEP(*(.kapi_specs)) \
+ __stop_kapi_specs = .; \
+ }
+#else
+#define KAPI_SPECS()
+#define KAPI_SPECS_SECTION()
+#endif
+
#ifdef CONFIG_BPF_EVENTS
#define BPF_RAW_TP() STRUCT_ALIGN(); \
BOUNDED_SECTION_BY(__bpf_raw_tp_map, __bpf_raw_tp)
diff --git a/include/linux/kernel_api_spec.h b/include/linux/kernel_api_spec.h
new file mode 100644
index 0000000000000..d8439d411f41e
--- /dev/null
+++ b/include/linux/kernel_api_spec.h
@@ -0,0 +1,1513 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * kernel_api_spec.h - Kernel API Formal Specification Framework
+ *
+ * This framework provides structures and macros to formally specify kernel APIs
+ * in both human and machine-readable formats. It supports comprehensive documentation
+ * of function signatures, parameters, return values, error conditions, and constraints.
+ */
+
+#ifndef _LINUX_KERNEL_API_SPEC_H
+#define _LINUX_KERNEL_API_SPEC_H
+
+#include <linux/types.h>
+#include <linux/stringify.h>
+#include <linux/compiler.h>
+
+struct sigaction;
+
+#define KAPI_MAX_PARAMS 16
+#define KAPI_MAX_ERRORS 32
+#define KAPI_MAX_CONSTRAINTS 16
+#define KAPI_MAX_SIGNALS 32
+#define KAPI_MAX_NAME_LEN 128
+#define KAPI_MAX_DESC_LEN 512
+#define KAPI_MAX_CAPABILITIES 8
+#define KAPI_MAX_SOCKET_STATES 16
+#define KAPI_MAX_PROTOCOL_BEHAVIORS 8
+#define KAPI_MAX_NET_ERRORS 16
+#define KAPI_MAX_SOCKOPTS 16
+#define KAPI_MAX_ADDR_FAMILIES 8
+
+/**
+ * enum kapi_param_type - Parameter type classification
+ * @KAPI_TYPE_VOID: void type
+ * @KAPI_TYPE_INT: Integer types (int, long, etc.)
+ * @KAPI_TYPE_UINT: Unsigned integer types
+ * @KAPI_TYPE_PTR: Pointer types
+ * @KAPI_TYPE_STRUCT: Structure types
+ * @KAPI_TYPE_UNION: Union types
+ * @KAPI_TYPE_ENUM: Enumeration types
+ * @KAPI_TYPE_FUNC_PTR: Function pointer types
+ * @KAPI_TYPE_ARRAY: Array types
+ * @KAPI_TYPE_FD: File descriptor - validated in process context
+ * @KAPI_TYPE_USER_PTR: User space pointer - validated for access and size
+ * @KAPI_TYPE_PATH: Pathname - validated for access and path limits
+ * @KAPI_TYPE_CUSTOM: Custom/complex types
+ */
+enum kapi_param_type {
+ KAPI_TYPE_VOID = 0,
+ KAPI_TYPE_INT,
+ KAPI_TYPE_UINT,
+ KAPI_TYPE_PTR,
+ KAPI_TYPE_STRUCT,
+ KAPI_TYPE_UNION,
+ KAPI_TYPE_ENUM,
+ KAPI_TYPE_FUNC_PTR,
+ KAPI_TYPE_ARRAY,
+ KAPI_TYPE_FD, /* File descriptor - validated in process context */
+ KAPI_TYPE_USER_PTR, /* User space pointer - validated for access and size */
+ KAPI_TYPE_PATH, /* Pathname - validated for access and path limits */
+ KAPI_TYPE_CUSTOM,
+};
+
+/**
+ * enum kapi_param_flags - Parameter attribute flags
+ * @KAPI_PARAM_IN: Input parameter
+ * @KAPI_PARAM_OUT: Output parameter
+ * @KAPI_PARAM_INOUT: Input/output parameter
+ * @KAPI_PARAM_OPTIONAL: Optional parameter (can be NULL)
+ * @KAPI_PARAM_CONST: Const qualified parameter
+ * @KAPI_PARAM_VOLATILE: Volatile qualified parameter
+ * @KAPI_PARAM_USER: User space pointer
+ * @KAPI_PARAM_DMA: DMA-capable memory required
+ * @KAPI_PARAM_ALIGNED: Alignment requirements
+ */
+enum kapi_param_flags {
+ KAPI_PARAM_IN = (1 << 0),
+ KAPI_PARAM_OUT = (1 << 1),
+ KAPI_PARAM_INOUT = (1 << 2),
+ KAPI_PARAM_OPTIONAL = (1 << 3),
+ KAPI_PARAM_CONST = (1 << 4),
+ KAPI_PARAM_VOLATILE = (1 << 5),
+ KAPI_PARAM_USER = (1 << 6),
+ KAPI_PARAM_DMA = (1 << 7),
+ KAPI_PARAM_ALIGNED = (1 << 8),
+};
+
+/**
+ * enum kapi_context_flags - Function execution context flags
+ * @KAPI_CTX_PROCESS: Can be called from process context
+ * @KAPI_CTX_SOFTIRQ: Can be called from softirq context
+ * @KAPI_CTX_HARDIRQ: Can be called from hardirq context
+ * @KAPI_CTX_NMI: Can be called from NMI context
+ * @KAPI_CTX_ATOMIC: Must be called in atomic context
+ * @KAPI_CTX_SLEEPABLE: May sleep
+ * @KAPI_CTX_PREEMPT_DISABLED: Requires preemption disabled
+ * @KAPI_CTX_IRQ_DISABLED: Requires interrupts disabled
+ */
+enum kapi_context_flags {
+ KAPI_CTX_PROCESS = (1 << 0),
+ KAPI_CTX_SOFTIRQ = (1 << 1),
+ KAPI_CTX_HARDIRQ = (1 << 2),
+ KAPI_CTX_NMI = (1 << 3),
+ KAPI_CTX_ATOMIC = (1 << 4),
+ KAPI_CTX_SLEEPABLE = (1 << 5),
+ KAPI_CTX_PREEMPT_DISABLED = (1 << 6),
+ KAPI_CTX_IRQ_DISABLED = (1 << 7),
+};
+
+/**
+ * enum kapi_lock_type - Lock types used/required by the function
+ * @KAPI_LOCK_NONE: No locking requirements
+ * @KAPI_LOCK_MUTEX: Mutex lock
+ * @KAPI_LOCK_SPINLOCK: Spinlock
+ * @KAPI_LOCK_RWLOCK: Read-write lock
+ * @KAPI_LOCK_SEQLOCK: Sequence lock
+ * @KAPI_LOCK_RCU: RCU lock
+ * @KAPI_LOCK_SEMAPHORE: Semaphore
+ * @KAPI_LOCK_CUSTOM: Custom locking mechanism
+ */
+enum kapi_lock_type {
+ KAPI_LOCK_NONE = 0,
+ KAPI_LOCK_MUTEX,
+ KAPI_LOCK_SPINLOCK,
+ KAPI_LOCK_RWLOCK,
+ KAPI_LOCK_SEQLOCK,
+ KAPI_LOCK_RCU,
+ KAPI_LOCK_SEMAPHORE,
+ KAPI_LOCK_CUSTOM,
+};
+
+/**
+ * enum kapi_constraint_type - Types of parameter constraints
+ * @KAPI_CONSTRAINT_NONE: No constraint
+ * @KAPI_CONSTRAINT_RANGE: Numeric range constraint
+ * @KAPI_CONSTRAINT_MASK: Bitmask constraint
+ * @KAPI_CONSTRAINT_ENUM: Enumerated values constraint
+ * @KAPI_CONSTRAINT_CUSTOM: Custom validation function
+ */
+enum kapi_constraint_type {
+ KAPI_CONSTRAINT_NONE = 0,
+ KAPI_CONSTRAINT_RANGE,
+ KAPI_CONSTRAINT_MASK,
+ KAPI_CONSTRAINT_ENUM,
+ KAPI_CONSTRAINT_CUSTOM,
+};
+
+/**
+ * struct kapi_param_spec - Parameter specification
+ * @name: Parameter name
+ * @type_name: Type name as string
+ * @type: Parameter type classification
+ * @flags: Parameter attribute flags
+ * @size: Size in bytes (for arrays/buffers)
+ * @alignment: Required alignment
+ * @min_value: Minimum valid value (for numeric types)
+ * @max_value: Maximum valid value (for numeric types)
+ * @valid_mask: Valid bits mask (for flag parameters)
+ * @enum_values: Array of valid enumerated values
+ * @enum_count: Number of valid enumerated values
+ * @constraint_type: Type of constraint applied
+ * @validate: Custom validation function
+ * @description: Human-readable description
+ * @constraints: Additional constraints description
+ * @size_param_idx: Index of parameter that determines size (-1 if fixed size)
+ * @size_multiplier: Multiplier for size calculation (e.g., sizeof(struct))
+ */
+struct kapi_param_spec {
+ char name[KAPI_MAX_NAME_LEN];
+ char type_name[KAPI_MAX_NAME_LEN];
+ enum kapi_param_type type;
+ u32 flags;
+ size_t size;
+ size_t alignment;
+ s64 min_value;
+ s64 max_value;
+ u64 valid_mask;
+ const s64 *enum_values;
+ u32 enum_count;
+ enum kapi_constraint_type constraint_type;
+ bool (*validate)(s64 value);
+ char description[KAPI_MAX_DESC_LEN];
+ char constraints[KAPI_MAX_DESC_LEN];
+ int size_param_idx; /* Index of param that determines size, -1 if N/A */
+ size_t size_multiplier; /* Size per unit (e.g., sizeof(struct epoll_event)) */
+} __attribute__((packed));
+
+/**
+ * struct kapi_error_spec - Error condition specification
+ * @error_code: Error code value
+ * @name: Error code name (e.g., "EINVAL")
+ * @condition: Condition that triggers this error
+ * @description: Detailed error description
+ */
+struct kapi_error_spec {
+ int error_code;
+ char name[KAPI_MAX_NAME_LEN];
+ char condition[KAPI_MAX_DESC_LEN];
+ char description[KAPI_MAX_DESC_LEN];
+} __attribute__((packed));
+
+/**
+ * enum kapi_return_check_type - Return value check types
+ * @KAPI_RETURN_EXACT: Success is an exact value
+ * @KAPI_RETURN_RANGE: Success is within a range
+ * @KAPI_RETURN_ERROR_CHECK: Success is when NOT in error list
+ * @KAPI_RETURN_FD: Return value is a file descriptor (>= 0 is success)
+ * @KAPI_RETURN_CUSTOM: Custom validation function
+ */
+enum kapi_return_check_type {
+ KAPI_RETURN_EXACT,
+ KAPI_RETURN_RANGE,
+ KAPI_RETURN_ERROR_CHECK,
+ KAPI_RETURN_FD,
+ KAPI_RETURN_CUSTOM,
+};
+
+/**
+ * struct kapi_return_spec - Return value specification
+ * @type_name: Return type name
+ * @type: Return type classification
+ * @check_type: Type of success check to perform
+ * @success_value: Exact value indicating success (for EXACT)
+ * @success_min: Minimum success value (for RANGE)
+ * @success_max: Maximum success value (for RANGE)
+ * @error_values: Array of error values (for ERROR_CHECK)
+ * @error_count: Number of error values
+ * @is_success: Custom function to check success
+ * @description: Return value description
+ */
+struct kapi_return_spec {
+ char type_name[KAPI_MAX_NAME_LEN];
+ enum kapi_param_type type;
+ enum kapi_return_check_type check_type;
+ s64 success_value;
+ s64 success_min;
+ s64 success_max;
+ const s64 *error_values;
+ u32 error_count;
+ bool (*is_success)(s64 retval);
+ char description[KAPI_MAX_DESC_LEN];
+} __attribute__((packed));
+
+/**
+ * struct kapi_lock_spec - Lock requirement specification
+ * @lock_name: Name of the lock
+ * @lock_type: Type of lock
+ * @acquired: Whether function acquires this lock
+ * @released: Whether function releases this lock
+ * @held_on_entry: Whether lock must be held on entry
+ * @held_on_exit: Whether lock is held on exit
+ * @description: Additional lock requirements
+ */
+struct kapi_lock_spec {
+ char lock_name[KAPI_MAX_NAME_LEN];
+ enum kapi_lock_type lock_type;
+ bool acquired;
+ bool released;
+ bool held_on_entry;
+ bool held_on_exit;
+ char description[KAPI_MAX_DESC_LEN];
+} __attribute__((packed));
+
+/**
+ * struct kapi_constraint_spec - Additional constraint specification
+ * @name: Constraint name
+ * @description: Constraint description
+ * @expression: Formal expression (if applicable)
+ */
+struct kapi_constraint_spec {
+ char name[KAPI_MAX_NAME_LEN];
+ char description[KAPI_MAX_DESC_LEN];
+ char expression[KAPI_MAX_DESC_LEN];
+} __attribute__((packed));
+
+/**
+ * enum kapi_signal_direction - Signal flow direction
+ * @KAPI_SIGNAL_RECEIVE: Function may receive this signal
+ * @KAPI_SIGNAL_SEND: Function may send this signal
+ * @KAPI_SIGNAL_HANDLE: Function handles this signal specially
+ * @KAPI_SIGNAL_BLOCK: Function blocks this signal
+ * @KAPI_SIGNAL_IGNORE: Function ignores this signal
+ */
+enum kapi_signal_direction {
+ KAPI_SIGNAL_RECEIVE = (1 << 0),
+ KAPI_SIGNAL_SEND = (1 << 1),
+ KAPI_SIGNAL_HANDLE = (1 << 2),
+ KAPI_SIGNAL_BLOCK = (1 << 3),
+ KAPI_SIGNAL_IGNORE = (1 << 4),
+};
+
+/**
+ * enum kapi_signal_action - What the function does with the signal
+ * @KAPI_SIGNAL_ACTION_DEFAULT: Default signal action applies
+ * @KAPI_SIGNAL_ACTION_TERMINATE: Causes termination
+ * @KAPI_SIGNAL_ACTION_COREDUMP: Causes termination with core dump
+ * @KAPI_SIGNAL_ACTION_STOP: Stops the process
+ * @KAPI_SIGNAL_ACTION_CONTINUE: Continues a stopped process
+ * @KAPI_SIGNAL_ACTION_CUSTOM: Custom handling described in notes
+ * @KAPI_SIGNAL_ACTION_RETURN: Returns from syscall with EINTR
+ * @KAPI_SIGNAL_ACTION_RESTART: Restarts the syscall
+ * @KAPI_SIGNAL_ACTION_QUEUE: Queues the signal for later delivery
+ * @KAPI_SIGNAL_ACTION_DISCARD: Discards the signal
+ * @KAPI_SIGNAL_ACTION_TRANSFORM: Transforms to another signal
+ */
+enum kapi_signal_action {
+ KAPI_SIGNAL_ACTION_DEFAULT = 0,
+ KAPI_SIGNAL_ACTION_TERMINATE,
+ KAPI_SIGNAL_ACTION_COREDUMP,
+ KAPI_SIGNAL_ACTION_STOP,
+ KAPI_SIGNAL_ACTION_CONTINUE,
+ KAPI_SIGNAL_ACTION_CUSTOM,
+ KAPI_SIGNAL_ACTION_RETURN,
+ KAPI_SIGNAL_ACTION_RESTART,
+ KAPI_SIGNAL_ACTION_QUEUE,
+ KAPI_SIGNAL_ACTION_DISCARD,
+ KAPI_SIGNAL_ACTION_TRANSFORM,
+};
+
+/**
+ * struct kapi_signal_spec - Signal specification
+ * @signal_num: Signal number (e.g., SIGKILL, SIGTERM)
+ * @signal_name: Signal name as string
+ * @direction: Direction flags (OR of kapi_signal_direction)
+ * @action: What happens when signal is received
+ * @target: Description of target process/thread for sent signals
+ * @condition: Condition under which signal is sent/received/handled
+ * @description: Detailed description of signal handling
+ * @restartable: Whether syscall is restartable after this signal
+ * @sa_flags_required: Required signal action flags (SA_*)
+ * @sa_flags_forbidden: Forbidden signal action flags
+ * @error_on_signal: Error code returned when signal occurs (-EINTR, etc)
+ * @transform_to: Signal number to transform to (if action is TRANSFORM)
+ * @timing: When signal can occur ("entry", "during", "exit", "anytime")
+ * @priority: Signal handling priority (lower processed first)
+ * @interruptible: Whether this operation is interruptible by this signal
+ * @queue_behavior: How signal is queued ("realtime", "standard", "coalesce")
+ * @state_required: Required process state for signal to be delivered
+ * @state_forbidden: Forbidden process state for signal delivery
+ */
+struct kapi_signal_spec {
+ int signal_num;
+ char signal_name[32];
+ u32 direction;
+ enum kapi_signal_action action;
+ char target[KAPI_MAX_DESC_LEN];
+ char condition[KAPI_MAX_DESC_LEN];
+ char description[KAPI_MAX_DESC_LEN];
+ bool restartable;
+ u32 sa_flags_required;
+ u32 sa_flags_forbidden;
+ int error_on_signal;
+ int transform_to;
+ char timing[32];
+ u8 priority;
+ bool interruptible;
+ char queue_behavior[128];
+ u32 state_required;
+ u32 state_forbidden;
+} __attribute__((packed));
+
+/**
+ * struct kapi_signal_mask_spec - Signal mask specification
+ * @mask_name: Name of the signal mask
+ * @signals: Array of signal numbers in the mask
+ * @signal_count: Number of signals in the mask
+ * @description: Description of what this mask represents
+ */
+struct kapi_signal_mask_spec {
+ char mask_name[KAPI_MAX_NAME_LEN];
+ int signals[KAPI_MAX_SIGNALS];
+ u32 signal_count;
+ char description[KAPI_MAX_DESC_LEN];
+} __attribute__((packed));
+
+/**
+ * struct kapi_struct_field - Structure field specification
+ * @name: Field name
+ * @type: Field type classification
+ * @type_name: Type name as string
+ * @offset: Offset within structure
+ * @size: Size of field in bytes
+ * @flags: Field attribute flags
+ * @constraint_type: Type of constraint applied
+ * @min_value: Minimum valid value (for numeric types)
+ * @max_value: Maximum valid value (for numeric types)
+ * @valid_mask: Valid bits mask (for flag fields)
+ * @description: Field description
+ */
+struct kapi_struct_field {
+ char name[KAPI_MAX_NAME_LEN];
+ enum kapi_param_type type;
+ char type_name[KAPI_MAX_NAME_LEN];
+ size_t offset;
+ size_t size;
+ u32 flags;
+ enum kapi_constraint_type constraint_type;
+ s64 min_value;
+ s64 max_value;
+ u64 valid_mask;
+ char description[KAPI_MAX_DESC_LEN];
+} __attribute__((packed));
+
+/**
+ * struct kapi_struct_spec - Structure type specification
+ * @name: Structure name
+ * @size: Total size of structure
+ * @alignment: Required alignment
+ * @field_count: Number of fields
+ * @fields: Field specifications
+ * @description: Structure description
+ */
+struct kapi_struct_spec {
+ char name[KAPI_MAX_NAME_LEN];
+ size_t size;
+ size_t alignment;
+ u32 field_count;
+ struct kapi_struct_field fields[KAPI_MAX_PARAMS];
+ char description[KAPI_MAX_DESC_LEN];
+} __attribute__((packed));
+
+/**
+ * enum kapi_capability_action - What the capability allows
+ * @KAPI_CAP_BYPASS_CHECK: Bypasses a check entirely
+ * @KAPI_CAP_INCREASE_LIMIT: Increases or removes a limit
+ * @KAPI_CAP_OVERRIDE_RESTRICTION: Overrides a restriction
+ * @KAPI_CAP_GRANT_PERMISSION: Grants permission that would otherwise be denied
+ * @KAPI_CAP_MODIFY_BEHAVIOR: Changes the behavior of the operation
+ * @KAPI_CAP_ACCESS_RESOURCE: Allows access to restricted resources
+ * @KAPI_CAP_PERFORM_OPERATION: Allows performing a privileged operation
+ */
+enum kapi_capability_action {
+ KAPI_CAP_BYPASS_CHECK = 0,
+ KAPI_CAP_INCREASE_LIMIT,
+ KAPI_CAP_OVERRIDE_RESTRICTION,
+ KAPI_CAP_GRANT_PERMISSION,
+ KAPI_CAP_MODIFY_BEHAVIOR,
+ KAPI_CAP_ACCESS_RESOURCE,
+ KAPI_CAP_PERFORM_OPERATION,
+};
+
+/**
+ * struct kapi_capability_spec - Capability requirement specification
+ * @capability: The capability constant (e.g., CAP_IPC_LOCK)
+ * @cap_name: Capability name as string
+ * @action: What the capability allows (kapi_capability_action)
+ * @allows: Description of what the capability allows
+ * @without_cap: What happens without the capability
+ * @check_condition: Condition when capability is checked
+ * @priority: Check priority (lower checked first)
+ * @alternative: Alternative capabilities that can be used
+ * @alternative_count: Number of alternative capabilities
+ */
+struct kapi_capability_spec {
+ int capability;
+ char cap_name[KAPI_MAX_NAME_LEN];
+ enum kapi_capability_action action;
+ char allows[KAPI_MAX_DESC_LEN];
+ char without_cap[KAPI_MAX_DESC_LEN];
+ char check_condition[KAPI_MAX_DESC_LEN];
+ u8 priority;
+ int alternative[KAPI_MAX_CAPABILITIES];
+ u32 alternative_count;
+} __attribute__((packed));
+
+/**
+ * enum kapi_side_effect_type - Types of side effects
+ * @KAPI_EFFECT_NONE: No side effects
+ * @KAPI_EFFECT_ALLOC_MEMORY: Allocates memory
+ * @KAPI_EFFECT_FREE_MEMORY: Frees memory
+ * @KAPI_EFFECT_MODIFY_STATE: Modifies global/shared state
+ * @KAPI_EFFECT_SIGNAL_SEND: Sends signals
+ * @KAPI_EFFECT_FILE_POSITION: Modifies file position
+ * @KAPI_EFFECT_LOCK_ACQUIRE: Acquires locks
+ * @KAPI_EFFECT_LOCK_RELEASE: Releases locks
+ * @KAPI_EFFECT_RESOURCE_CREATE: Creates system resources (FDs, PIDs, etc)
+ * @KAPI_EFFECT_RESOURCE_DESTROY: Destroys system resources
+ * @KAPI_EFFECT_SCHEDULE: May cause scheduling/context switch
+ * @KAPI_EFFECT_HARDWARE: Interacts with hardware
+ * @KAPI_EFFECT_NETWORK: Network I/O operation
+ * @KAPI_EFFECT_FILESYSTEM: Filesystem modification
+ * @KAPI_EFFECT_PROCESS_STATE: Modifies process state
+ */
+enum kapi_side_effect_type {
+ KAPI_EFFECT_NONE = 0,
+ KAPI_EFFECT_ALLOC_MEMORY = (1 << 0),
+ KAPI_EFFECT_FREE_MEMORY = (1 << 1),
+ KAPI_EFFECT_MODIFY_STATE = (1 << 2),
+ KAPI_EFFECT_SIGNAL_SEND = (1 << 3),
+ KAPI_EFFECT_FILE_POSITION = (1 << 4),
+ KAPI_EFFECT_LOCK_ACQUIRE = (1 << 5),
+ KAPI_EFFECT_LOCK_RELEASE = (1 << 6),
+ KAPI_EFFECT_RESOURCE_CREATE = (1 << 7),
+ KAPI_EFFECT_RESOURCE_DESTROY = (1 << 8),
+ KAPI_EFFECT_SCHEDULE = (1 << 9),
+ KAPI_EFFECT_HARDWARE = (1 << 10),
+ KAPI_EFFECT_NETWORK = (1 << 11),
+ KAPI_EFFECT_FILESYSTEM = (1 << 12),
+ KAPI_EFFECT_PROCESS_STATE = (1 << 13),
+};
+
+/**
+ * struct kapi_side_effect - Side effect specification
+ * @type: Bitmask of effect types
+ * @target: What is affected (e.g., "process memory", "file descriptor table")
+ * @condition: Condition under which effect occurs
+ * @description: Detailed description of the effect
+ * @reversible: Whether the effect can be undone
+ */
+struct kapi_side_effect {
+ u32 type;
+ char target[KAPI_MAX_NAME_LEN];
+ char condition[KAPI_MAX_DESC_LEN];
+ char description[KAPI_MAX_DESC_LEN];
+ bool reversible;
+} __attribute__((packed));
+
+/**
+ * struct kapi_state_transition - State transition specification
+ * @from_state: Starting state description
+ * @to_state: Ending state description
+ * @condition: Condition for transition
+ * @object: Object whose state changes
+ * @description: Detailed description
+ */
+struct kapi_state_transition {
+ char from_state[KAPI_MAX_NAME_LEN];
+ char to_state[KAPI_MAX_NAME_LEN];
+ char condition[KAPI_MAX_DESC_LEN];
+ char object[KAPI_MAX_NAME_LEN];
+ char description[KAPI_MAX_DESC_LEN];
+} __attribute__((packed));
+
+#define KAPI_MAX_STRUCT_SPECS 8
+#define KAPI_MAX_SIDE_EFFECTS 16
+#define KAPI_MAX_STATE_TRANS 8
+
+#ifdef CONFIG_NET
+/**
+ * enum kapi_socket_state - Socket states for state machine
+ */
+enum kapi_socket_state {
+ KAPI_SOCK_STATE_UNSPEC = 0,
+ KAPI_SOCK_STATE_CLOSED,
+ KAPI_SOCK_STATE_OPEN,
+ KAPI_SOCK_STATE_BOUND,
+ KAPI_SOCK_STATE_LISTEN,
+ KAPI_SOCK_STATE_SYN_SENT,
+ KAPI_SOCK_STATE_SYN_RECV,
+ KAPI_SOCK_STATE_ESTABLISHED,
+ KAPI_SOCK_STATE_FIN_WAIT1,
+ KAPI_SOCK_STATE_FIN_WAIT2,
+ KAPI_SOCK_STATE_CLOSE_WAIT,
+ KAPI_SOCK_STATE_CLOSING,
+ KAPI_SOCK_STATE_LAST_ACK,
+ KAPI_SOCK_STATE_TIME_WAIT,
+ KAPI_SOCK_STATE_CONNECTED,
+ KAPI_SOCK_STATE_DISCONNECTED,
+};
+
+/**
+ * enum kapi_socket_protocol - Socket protocol types
+ */
+enum kapi_socket_protocol {
+ KAPI_PROTO_TCP = (1 << 0),
+ KAPI_PROTO_UDP = (1 << 1),
+ KAPI_PROTO_UNIX = (1 << 2),
+ KAPI_PROTO_RAW = (1 << 3),
+ KAPI_PROTO_PACKET = (1 << 4),
+ KAPI_PROTO_NETLINK = (1 << 5),
+ KAPI_PROTO_SCTP = (1 << 6),
+ KAPI_PROTO_DCCP = (1 << 7),
+ KAPI_PROTO_ALL = 0xFFFFFFFF,
+};
+
+/**
+ * enum kapi_buffer_behavior - Network buffer handling behaviors
+ */
+enum kapi_buffer_behavior {
+ KAPI_BUF_PEEK = (1 << 0),
+ KAPI_BUF_TRUNCATE = (1 << 1),
+ KAPI_BUF_SCATTER = (1 << 2),
+ KAPI_BUF_ZERO_COPY = (1 << 3),
+ KAPI_BUF_KERNEL_ALLOC = (1 << 4),
+ KAPI_BUF_DMA_CAPABLE = (1 << 5),
+ KAPI_BUF_FRAGMENT = (1 << 6),
+};
+
+/**
+ * enum kapi_async_behavior - Asynchronous operation behaviors
+ */
+enum kapi_async_behavior {
+ KAPI_ASYNC_BLOCK = 0,
+ KAPI_ASYNC_NONBLOCK = (1 << 0),
+ KAPI_ASYNC_POLL_READY = (1 << 1),
+ KAPI_ASYNC_SIGNAL_DRIVEN = (1 << 2),
+ KAPI_ASYNC_AIO = (1 << 3),
+ KAPI_ASYNC_IO_URING = (1 << 4),
+ KAPI_ASYNC_EPOLL = (1 << 5),
+};
+
+/**
+ * struct kapi_socket_state_spec - Socket state requirement/transition
+ */
+struct kapi_socket_state_spec {
+ enum kapi_socket_state required_states[KAPI_MAX_SOCKET_STATES];
+ u32 required_state_count;
+ enum kapi_socket_state forbidden_states[KAPI_MAX_SOCKET_STATES];
+ u32 forbidden_state_count;
+ enum kapi_socket_state resulting_state;
+ char state_condition[KAPI_MAX_DESC_LEN];
+ u32 applicable_protocols;
+} __attribute__((packed));
+
+/**
+ * struct kapi_protocol_behavior - Protocol-specific behavior
+ */
+struct kapi_protocol_behavior {
+ u32 applicable_protocols;
+ char behavior[KAPI_MAX_DESC_LEN];
+ s64 protocol_flags;
+ char flag_description[KAPI_MAX_DESC_LEN];
+} __attribute__((packed));
+
+/**
+ * struct kapi_buffer_spec - Network buffer specification
+ */
+struct kapi_buffer_spec {
+ u32 buffer_behaviors;
+ size_t min_buffer_size;
+ size_t max_buffer_size;
+ size_t optimal_buffer_size;
+ char fragmentation_rules[KAPI_MAX_DESC_LEN];
+ bool can_partial_transfer;
+ char partial_transfer_rules[KAPI_MAX_DESC_LEN];
+} __attribute__((packed));
+
+/**
+ * struct kapi_async_spec - Asynchronous behavior specification
+ */
+struct kapi_async_spec {
+ enum kapi_async_behavior supported_modes;
+ int nonblock_errno;
+ u32 poll_events_in;
+ u32 poll_events_out;
+ char completion_condition[KAPI_MAX_DESC_LEN];
+ bool supports_timeout;
+ char timeout_behavior[KAPI_MAX_DESC_LEN];
+} __attribute__((packed));
+
+/**
+ * struct kapi_addr_family_spec - Address family specification
+ */
+struct kapi_addr_family_spec {
+ int family;
+ char family_name[32];
+ size_t addr_struct_size;
+ size_t min_addr_len;
+ size_t max_addr_len;
+ char addr_format[KAPI_MAX_DESC_LEN];
+ bool supports_wildcard;
+ bool supports_multicast;
+ bool supports_broadcast;
+ char special_addresses[KAPI_MAX_DESC_LEN];
+ u32 port_range_min;
+ u32 port_range_max;
+} __attribute__((packed));
+#endif /* CONFIG_NET */
+
+/**
+ * struct kernel_api_spec - Complete kernel API specification
+ * @name: Function name
+ * @version: API version
+ * @description: Brief description
+ * @long_description: Detailed description
+ * @context_flags: Execution context flags
+ * @param_count: Number of parameters
+ * @params: Parameter specifications
+ * @return_spec: Return value specification
+ * @error_count: Number of possible errors
+ * @errors: Error specifications
+ * @lock_count: Number of lock specifications
+ * @locks: Lock requirement specifications
+ * @constraint_count: Number of additional constraints
+ * @constraints: Additional constraint specifications
+ * @examples: Usage examples
+ * @notes: Additional notes
+ * @since_version: Kernel version when introduced
+ * @deprecated: Whether API is deprecated
+ * @replacement: Replacement API if deprecated
+ * @signal_count: Number of signal specifications
+ * @signals: Signal handling specifications
+ * @signal_mask_count: Number of signal mask specifications
+ * @signal_masks: Signal mask specifications
+ * @struct_spec_count: Number of structure specifications
+ * @struct_specs: Structure type specifications
+ * @side_effect_count: Number of side effect specifications
+ * @side_effects: Side effect specifications
+ * @state_trans_count: Number of state transition specifications
+ * @state_transitions: State transition specifications
+ */
+struct kernel_api_spec {
+ char name[KAPI_MAX_NAME_LEN];
+ u32 version;
+ char description[KAPI_MAX_DESC_LEN];
+ char long_description[KAPI_MAX_DESC_LEN * 4];
+ u32 context_flags;
+
+ /* Parameters */
+ u32 param_count;
+ struct kapi_param_spec params[KAPI_MAX_PARAMS];
+
+ /* Return value */
+ struct kapi_return_spec return_spec;
+
+ /* Errors */
+ u32 error_count;
+ struct kapi_error_spec errors[KAPI_MAX_ERRORS];
+
+ /* Locking */
+ u32 lock_count;
+ struct kapi_lock_spec locks[KAPI_MAX_CONSTRAINTS];
+
+ /* Constraints */
+ u32 constraint_count;
+ struct kapi_constraint_spec constraints[KAPI_MAX_CONSTRAINTS];
+
+ /* Additional information */
+ char examples[KAPI_MAX_DESC_LEN * 2];
+ char notes[KAPI_MAX_DESC_LEN * 2];
+ char since_version[32];
+ bool deprecated;
+ char replacement[KAPI_MAX_NAME_LEN];
+
+ /* Signal specifications */
+ u32 signal_count;
+ struct kapi_signal_spec signals[KAPI_MAX_SIGNALS];
+
+ /* Signal mask specifications */
+ u32 signal_mask_count;
+ struct kapi_signal_mask_spec signal_masks[KAPI_MAX_SIGNALS];
+
+ /* Structure specifications */
+ u32 struct_spec_count;
+ struct kapi_struct_spec struct_specs[KAPI_MAX_STRUCT_SPECS];
+
+ /* Side effects */
+ u32 side_effect_count;
+ struct kapi_side_effect side_effects[KAPI_MAX_SIDE_EFFECTS];
+
+ /* State transitions */
+ u32 state_trans_count;
+ struct kapi_state_transition state_transitions[KAPI_MAX_STATE_TRANS];
+
+ /* Capability specifications */
+ u32 capability_count;
+ struct kapi_capability_spec capabilities[KAPI_MAX_CAPABILITIES];
+
+#ifdef CONFIG_NET
+ /* Networking-specific fields */
+ struct kapi_socket_state_spec socket_state;
+ struct kapi_protocol_behavior protocol_behaviors[KAPI_MAX_PROTOCOL_BEHAVIORS];
+ u32 protocol_behavior_count;
+ struct kapi_buffer_spec buffer_spec;
+ struct kapi_async_spec async_spec;
+ struct kapi_addr_family_spec addr_families[KAPI_MAX_ADDR_FAMILIES];
+ u32 addr_family_count;
+
+ /* Network operation characteristics */
+ bool is_connection_oriented;
+ bool is_message_oriented;
+ bool supports_oob_data;
+ bool supports_peek;
+ bool supports_select_poll;
+ bool is_reentrant;
+
+ /* Network semantic descriptions */
+ char connection_establishment[KAPI_MAX_DESC_LEN];
+ char connection_termination[KAPI_MAX_DESC_LEN];
+ char data_transfer_semantics[KAPI_MAX_DESC_LEN];
+#endif /* CONFIG_NET */
+} __attribute__((packed));
+
+/* Macros for defining API specifications */
+
+/**
+ * DEFINE_KERNEL_API_SPEC - Define a kernel API specification
+ * @func_name: Function name to specify
+ */
+#define DEFINE_KERNEL_API_SPEC(func_name) \
+ static struct kernel_api_spec __kapi_spec_##func_name \
+ __used __section(".kapi_specs") = { \
+ .name = __stringify(func_name), \
+ .version = 1,
+
+#define KAPI_END_SPEC };
+
+/**
+ * KAPI_DESCRIPTION - Set API description
+ * @desc: Description string
+ */
+#define KAPI_DESCRIPTION(desc) \
+ .description = desc,
+
+/**
+ * KAPI_LONG_DESC - Set detailed API description
+ * @desc: Detailed description string
+ */
+#define KAPI_LONG_DESC(desc) \
+ .long_description = desc,
+
+/**
+ * KAPI_CONTEXT - Set execution context flags
+ * @flags: Context flags (OR'ed KAPI_CTX_* values)
+ */
+#define KAPI_CONTEXT(flags) \
+ .context_flags = flags,
+
+/**
+ * KAPI_PARAM - Define a parameter specification
+ * @idx: Parameter index (0-based)
+ * @pname: Parameter name
+ * @ptype: Type name string
+ * @pdesc: Parameter description
+ */
+#define KAPI_PARAM(idx, pname, ptype, pdesc) \
+ .params[idx] = { \
+ .name = pname, \
+ .type_name = ptype, \
+ .description = pdesc, \
+ .size_param_idx = -1, /* Default: no dynamic sizing */
+
+#define KAPI_PARAM_TYPE(ptype) \
+ .type = ptype,
+
+#define KAPI_PARAM_FLAGS(pflags) \
+ .flags = pflags,
+
+#define KAPI_PARAM_SIZE(psize) \
+ .size = psize,
+
+#define KAPI_PARAM_RANGE(pmin, pmax) \
+ .min_value = pmin, \
+ .max_value = pmax,
+
+#define KAPI_PARAM_CONSTRAINT_TYPE(ctype) \
+ .constraint_type = ctype,
+
+#define KAPI_PARAM_CONSTRAINT(desc) \
+ .constraints = desc,
+
+#define KAPI_PARAM_VALID_MASK(mask) \
+ .valid_mask = mask,
+
+#define KAPI_PARAM_ENUM_VALUES(values) \
+ .enum_values = values, \
+ .enum_count = ARRAY_SIZE(values),
+
+#define KAPI_PARAM_END },
+
+/**
+ * KAPI_RETURN - Define return value specification
+ * @rtype: Return type name
+ * @rdesc: Return value description
+ */
+#define KAPI_RETURN(rtype, rdesc) \
+ .return_spec = { \
+ .type_name = rtype, \
+ .description = rdesc,
+
+#define KAPI_RETURN_SUCCESS(val) \
+ .success_value = val,
+
+#define KAPI_RETURN_TYPE(rtype) \
+ .type = rtype,
+
+#define KAPI_RETURN_CHECK_TYPE(ctype) \
+ .check_type = ctype,
+
+#define KAPI_RETURN_ERROR_VALUES(values) \
+ .error_values = values,
+
+#define KAPI_RETURN_ERROR_COUNT(count) \
+ .error_count = count,
+
+#define KAPI_RETURN_SUCCESS_RANGE(min, max) \
+ .success_min = min, \
+ .success_max = max,
+
+#define KAPI_RETURN_END },
+
+/**
+ * KAPI_ERROR - Define an error condition
+ * @idx: Error index
+ * @ecode: Error code value
+ * @ename: Error name
+ * @econd: Error condition
+ * @edesc: Error description
+ */
+#define KAPI_ERROR(idx, ecode, ename, econd, edesc) \
+ .errors[idx] = { \
+ .error_code = ecode, \
+ .name = ename, \
+ .condition = econd, \
+ .description = edesc, \
+ },
+
+/**
+ * KAPI_LOCK - Define a lock requirement
+ * @idx: Lock index
+ * @lname: Lock name
+ * @ltype: Lock type
+ */
+#define KAPI_LOCK(idx, lname, ltype) \
+ .locks[idx] = { \
+ .lock_name = lname, \
+ .lock_type = ltype,
+
+#define KAPI_LOCK_ACQUIRED \
+ .acquired = true,
+
+#define KAPI_LOCK_RELEASED \
+ .released = true,
+
+#define KAPI_LOCK_HELD_ENTRY \
+ .held_on_entry = true,
+
+#define KAPI_LOCK_HELD_EXIT \
+ .held_on_exit = true,
+
+#define KAPI_LOCK_DESC(ldesc) \
+ .description = ldesc,
+
+#define KAPI_LOCK_END },
+
+/**
+ * KAPI_CONSTRAINT - Define an additional constraint
+ * @idx: Constraint index
+ * @cname: Constraint name
+ * @cdesc: Constraint description
+ */
+#define KAPI_CONSTRAINT(idx, cname, cdesc) \
+ .constraints[idx] = { \
+ .name = cname, \
+ .description = cdesc,
+
+#define KAPI_CONSTRAINT_EXPR(expr) \
+ .expression = expr,
+
+#define KAPI_CONSTRAINT_END },
+
+/**
+ * KAPI_EXAMPLES - Set API usage examples
+ * @examples: Examples string
+ */
+#define KAPI_EXAMPLES(ex) \
+ .examples = ex,
+
+/**
+ * KAPI_NOTES - Set API notes
+ * @notes: Notes string
+ */
+#define KAPI_NOTES(n) \
+ .notes = n,
+
+/**
+ * KAPI_SIGNAL - Define a signal specification
+ * @idx: Signal index
+ * @signum: Signal number (e.g., SIGKILL)
+ * @signame: Signal name string
+ * @dir: Direction flags
+ * @act: Action taken
+ */
+#define KAPI_SIGNAL(idx, signum, signame, dir, act) \
+ .signals[idx] = { \
+ .signal_num = signum, \
+ .signal_name = signame, \
+ .direction = dir, \
+ .action = act,
+
+#define KAPI_SIGNAL_TARGET(tgt) \
+ .target = tgt,
+
+#define KAPI_SIGNAL_CONDITION(cond) \
+ .condition = cond,
+
+#define KAPI_SIGNAL_DESC(desc) \
+ .description = desc,
+
+#define KAPI_SIGNAL_RESTARTABLE \
+ .restartable = true,
+
+#define KAPI_SIGNAL_SA_FLAGS_REQ(flags) \
+ .sa_flags_required = flags,
+
+#define KAPI_SIGNAL_SA_FLAGS_FORBID(flags) \
+ .sa_flags_forbidden = flags,
+
+#define KAPI_SIGNAL_ERROR(err) \
+ .error_on_signal = err,
+
+#define KAPI_SIGNAL_TRANSFORM(sig) \
+ .transform_to = sig,
+
+#define KAPI_SIGNAL_TIMING(when) \
+ .timing = when,
+
+#define KAPI_SIGNAL_PRIORITY(prio) \
+ .priority = prio,
+
+#define KAPI_SIGNAL_INTERRUPTIBLE \
+ .interruptible = true,
+
+#define KAPI_SIGNAL_QUEUE(behavior) \
+ .queue_behavior = behavior,
+
+#define KAPI_SIGNAL_STATE_REQ(state) \
+ .state_required = state,
+
+#define KAPI_SIGNAL_STATE_FORBID(state) \
+ .state_forbidden = state,
+
+#define KAPI_SIGNAL_END },
+
+#define KAPI_SIGNAL_COUNT(n) \
+ .signal_count = n,
+
+/**
+ * KAPI_SIGNAL_MASK - Define a signal mask specification
+ * @idx: Mask index
+ * @name: Mask name
+ * @desc: Mask description
+ */
+#define KAPI_SIGNAL_MASK(idx, name, desc) \
+ .signal_masks[idx] = { \
+ .mask_name = name, \
+ .description = desc,
+
+#define KAPI_SIGNAL_MASK_ADD(signum) \
+ .signals[.signal_count++] = signum,
+
+#define KAPI_SIGNAL_MASK_END },
+
+/**
+ * KAPI_STRUCT_SPEC - Define a structure specification
+ * @idx: Structure spec index
+ * @sname: Structure name
+ * @sdesc: Structure description
+ */
+#define KAPI_STRUCT_SPEC(idx, sname, sdesc) \
+ .struct_specs[idx] = { \
+ .name = #sname, \
+ .description = sdesc,
+
+#define KAPI_STRUCT_SIZE(ssize, salign) \
+ .size = ssize, \
+ .alignment = salign,
+
+#define KAPI_STRUCT_FIELD_COUNT(n) \
+ .field_count = n,
+
+/**
+ * KAPI_STRUCT_FIELD - Define a structure field
+ * @fidx: Field index
+ * @fname: Field name
+ * @ftype: Field type (KAPI_TYPE_*)
+ * @ftype_name: Type name as string
+ * @fdesc: Field description
+ */
+#define KAPI_STRUCT_FIELD(fidx, fname, ftype, ftype_name, fdesc) \
+ .fields[fidx] = { \
+ .name = fname, \
+ .type = ftype, \
+ .type_name = ftype_name, \
+ .description = fdesc,
+
+#define KAPI_FIELD_OFFSET(foffset) \
+ .offset = foffset,
+
+#define KAPI_FIELD_SIZE(fsize) \
+ .size = fsize,
+
+#define KAPI_FIELD_FLAGS(fflags) \
+ .flags = fflags,
+
+#define KAPI_FIELD_CONSTRAINT_RANGE(min, max) \
+ .constraint_type = KAPI_CONSTRAINT_RANGE, \
+ .min_value = min, \
+ .max_value = max,
+
+#define KAPI_FIELD_CONSTRAINT_MASK(mask) \
+ .constraint_type = KAPI_CONSTRAINT_MASK, \
+ .valid_mask = mask,
+
+#define KAPI_FIELD_CONSTRAINT_ENUM(values, count) \
+ .constraint_type = KAPI_CONSTRAINT_ENUM, \
+ .enum_values = values, \
+ .enum_count = count,
+
+#define KAPI_STRUCT_FIELD_END },
+
+#define KAPI_STRUCT_SPEC_END },
+
+/* Counter for structure specifications */
+#define KAPI_STRUCT_SPEC_COUNT(n) \
+ .struct_spec_count = n,
+
+/* Additional lock-related macros */
+#define KAPI_LOCK_COUNT(n) \
+ .lock_count = n,
+
+/**
+ * KAPI_SIDE_EFFECT - Define a side effect
+ * @idx: Side effect index
+ * @etype: Effect type bitmask (OR'ed KAPI_EFFECT_* values)
+ * @etarget: What is affected
+ * @edesc: Effect description
+ */
+#define KAPI_SIDE_EFFECT(idx, etype, etarget, edesc) \
+ .side_effects[idx] = { \
+ .type = etype, \
+ .target = etarget, \
+ .description = edesc, \
+ .reversible = false, /* Default to non-reversible */
+
+#define KAPI_EFFECT_CONDITION(cond) \
+ .condition = cond,
+
+#define KAPI_EFFECT_REVERSIBLE \
+ .reversible = true,
+
+#define KAPI_SIDE_EFFECT_END },
+
+/**
+ * KAPI_STATE_TRANS - Define a state transition
+ * @idx: State transition index
+ * @obj: Object whose state changes
+ * @from: From state
+ * @to: To state
+ * @desc: Transition description
+ */
+#define KAPI_STATE_TRANS(idx, obj, from, to, desc) \
+ .state_transitions[idx] = { \
+ .object = obj, \
+ .from_state = from, \
+ .to_state = to, \
+ .description = desc,
+
+#define KAPI_STATE_TRANS_COND(cond) \
+ .condition = cond,
+
+#define KAPI_STATE_TRANS_END },
+
+/* Counters for side effects and state transitions */
+#define KAPI_SIDE_EFFECT_COUNT(n) \
+ .side_effect_count = n,
+
+#define KAPI_STATE_TRANS_COUNT(n) \
+ .state_trans_count = n,
+
+/* Helper macros for common side effect patterns */
+#define KAPI_EFFECTS_MEMORY (KAPI_EFFECT_ALLOC_MEMORY | KAPI_EFFECT_FREE_MEMORY)
+#define KAPI_EFFECTS_LOCKING (KAPI_EFFECT_LOCK_ACQUIRE | KAPI_EFFECT_LOCK_RELEASE)
+#define KAPI_EFFECTS_RESOURCES (KAPI_EFFECT_RESOURCE_CREATE | KAPI_EFFECT_RESOURCE_DESTROY)
+#define KAPI_EFFECTS_IO (KAPI_EFFECT_NETWORK | KAPI_EFFECT_FILESYSTEM)
+
+/* Helper macros for common patterns */
+
+#define KAPI_PARAM_IN (KAPI_PARAM_IN)
+#define KAPI_PARAM_OUT (KAPI_PARAM_OUT)
+#define KAPI_PARAM_INOUT (KAPI_PARAM_IN | KAPI_PARAM_OUT)
+#define KAPI_PARAM_OPTIONAL (KAPI_PARAM_OPTIONAL)
+#define KAPI_PARAM_USER_PTR (KAPI_PARAM_USER | KAPI_PARAM_PTR)
+
+/* Common signal timing constants */
+#define KAPI_SIGNAL_TIME_ENTRY "entry"
+#define KAPI_SIGNAL_TIME_DURING "during"
+#define KAPI_SIGNAL_TIME_EXIT "exit"
+#define KAPI_SIGNAL_TIME_ANYTIME "anytime"
+#define KAPI_SIGNAL_TIME_BLOCKING "while_blocked"
+#define KAPI_SIGNAL_TIME_SLEEPING "while_sleeping"
+
+/* Common signal queue behaviors */
+#define KAPI_SIGNAL_QUEUE_STANDARD "standard"
+#define KAPI_SIGNAL_QUEUE_REALTIME "realtime"
+#define KAPI_SIGNAL_QUEUE_COALESCE "coalesce"
+#define KAPI_SIGNAL_QUEUE_REPLACE "replace"
+#define KAPI_SIGNAL_QUEUE_DISCARD "discard"
+
+/* Process state flags for signal delivery */
+#define KAPI_SIGNAL_STATE_RUNNING (1 << 0)
+#define KAPI_SIGNAL_STATE_SLEEPING (1 << 1)
+#define KAPI_SIGNAL_STATE_STOPPED (1 << 2)
+#define KAPI_SIGNAL_STATE_TRACED (1 << 3)
+#define KAPI_SIGNAL_STATE_ZOMBIE (1 << 4)
+#define KAPI_SIGNAL_STATE_DEAD (1 << 5)
+
+/* Capability specification macros */
+
+/**
+ * KAPI_CAPABILITY - Define a capability requirement
+ * @idx: Capability index
+ * @cap: Capability constant (e.g., CAP_IPC_LOCK)
+ * @name: Capability name string
+ * @act: Action type (kapi_capability_action)
+ */
+#define KAPI_CAPABILITY(idx, cap, name, act) \
+ .capabilities[idx] = { \
+ .capability = cap, \
+ .cap_name = name, \
+ .action = act,
+
+#define KAPI_CAP_ALLOWS(desc) \
+ .allows = desc,
+
+#define KAPI_CAP_WITHOUT(desc) \
+ .without_cap = desc,
+
+#define KAPI_CAP_CONDITION(cond) \
+ .check_condition = cond,
+
+#define KAPI_CAP_PRIORITY(prio) \
+ .priority = prio,
+
+#define KAPI_CAP_ALTERNATIVE(caps, count) \
+ .alternative = caps, \
+ .alternative_count = count,
+
+#define KAPI_CAPABILITY_END },
+
+/* Counter for capability specifications */
+#define KAPI_CAPABILITY_COUNT(n) \
+ .capability_count = n,
+
+/* Common signal patterns for syscalls */
+#define KAPI_SIGNAL_INTERRUPTIBLE_SLEEP \
+ KAPI_SIGNAL(0, SIGINT, "SIGINT", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_RETURN) \
+ KAPI_SIGNAL_TIMING(KAPI_SIGNAL_TIME_SLEEPING) \
+ KAPI_SIGNAL_ERROR(-EINTR) \
+ KAPI_SIGNAL_RESTARTABLE \
+ KAPI_SIGNAL_DESC("Interrupts sleep, returns -EINTR") \
+ KAPI_SIGNAL_END, \
+ KAPI_SIGNAL(1, SIGTERM, "SIGTERM", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_RETURN) \
+ KAPI_SIGNAL_TIMING(KAPI_SIGNAL_TIME_SLEEPING) \
+ KAPI_SIGNAL_ERROR(-EINTR) \
+ KAPI_SIGNAL_RESTARTABLE \
+ KAPI_SIGNAL_DESC("Interrupts sleep, returns -EINTR") \
+ KAPI_SIGNAL_END
+
+#define KAPI_SIGNAL_FATAL_DEFAULT \
+ KAPI_SIGNAL(2, SIGKILL, "SIGKILL", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_TERMINATE) \
+ KAPI_SIGNAL_TIMING(KAPI_SIGNAL_TIME_ANYTIME) \
+ KAPI_SIGNAL_PRIORITY(0) \
+ KAPI_SIGNAL_DESC("Process terminated immediately") \
+ KAPI_SIGNAL_END
+
+#define KAPI_SIGNAL_STOP_CONT \
+ KAPI_SIGNAL(3, SIGSTOP, "SIGSTOP", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_STOP) \
+ KAPI_SIGNAL_TIMING(KAPI_SIGNAL_TIME_ANYTIME) \
+ KAPI_SIGNAL_DESC("Process stopped") \
+ KAPI_SIGNAL_END, \
+ KAPI_SIGNAL(4, SIGCONT, "SIGCONT", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_CONTINUE) \
+ KAPI_SIGNAL_TIMING(KAPI_SIGNAL_TIME_ANYTIME) \
+ KAPI_SIGNAL_DESC("Process continued") \
+ KAPI_SIGNAL_END
+
+/* Validation and runtime checking */
+
+#ifdef CONFIG_KAPI_RUNTIME_CHECKS
+bool kapi_validate_params(const struct kernel_api_spec *spec, ...);
+bool kapi_validate_param(const struct kapi_param_spec *param_spec, s64 value);
+bool kapi_validate_param_with_context(const struct kapi_param_spec *param_spec,
+ s64 value, const s64 *all_params, int param_count);
+int kapi_validate_syscall_param(const struct kernel_api_spec *spec,
+ int param_idx, s64 value);
+int kapi_validate_syscall_params(const struct kernel_api_spec *spec,
+ const s64 *params, int param_count);
+bool kapi_check_return_success(const struct kapi_return_spec *return_spec, s64 retval);
+bool kapi_validate_return_value(const struct kernel_api_spec *spec, s64 retval);
+int kapi_validate_syscall_return(const struct kernel_api_spec *spec, s64 retval);
+void kapi_check_context(const struct kernel_api_spec *spec);
+void kapi_check_locks(const struct kernel_api_spec *spec);
+bool kapi_check_signal_allowed(const struct kernel_api_spec *spec, int signum);
+bool kapi_validate_signal_action(const struct kernel_api_spec *spec, int signum,
+ struct sigaction *act);
+int kapi_get_signal_error(const struct kernel_api_spec *spec, int signum);
+bool kapi_is_signal_restartable(const struct kernel_api_spec *spec, int signum);
+#else
+static inline bool kapi_validate_params(const struct kernel_api_spec *spec, ...)
+{
+ return true;
+}
+static inline bool kapi_validate_param(const struct kapi_param_spec *param_spec, s64 value)
+{
+ return true;
+}
+static inline bool kapi_validate_param_with_context(const struct kapi_param_spec *param_spec,
+ s64 value, const s64 *all_params, int param_count)
+{
+ return true;
+}
+static inline int kapi_validate_syscall_param(const struct kernel_api_spec *spec,
+ int param_idx, s64 value)
+{
+ return 0;
+}
+static inline int kapi_validate_syscall_params(const struct kernel_api_spec *spec,
+ const s64 *params, int param_count)
+{
+ return 0;
+}
+static inline bool kapi_check_return_success(const struct kapi_return_spec *return_spec, s64 retval)
+{
+ return true;
+}
+static inline bool kapi_validate_return_value(const struct kernel_api_spec *spec, s64 retval)
+{
+ return true;
+}
+static inline int kapi_validate_syscall_return(const struct kernel_api_spec *spec, s64 retval)
+{
+ return 0;
+}
+static inline void kapi_check_context(const struct kernel_api_spec *spec) {}
+static inline void kapi_check_locks(const struct kernel_api_spec *spec) {}
+static inline bool kapi_check_signal_allowed(const struct kernel_api_spec *spec, int signum)
+{
+ return true;
+}
+static inline bool kapi_validate_signal_action(const struct kernel_api_spec *spec, int signum,
+ struct sigaction *act)
+{
+ return true;
+}
+static inline int kapi_get_signal_error(const struct kernel_api_spec *spec, int signum)
+{
+ return -EINTR;
+}
+static inline bool kapi_is_signal_restartable(const struct kernel_api_spec *spec, int signum)
+{
+ return false;
+}
+#endif
+
+/* Export/query functions */
+const struct kernel_api_spec *kapi_get_spec(const char *name);
+int kapi_export_json(const struct kernel_api_spec *spec, char *buf, size_t size);
+void kapi_print_spec(const struct kernel_api_spec *spec);
+
+/* Registration for dynamic APIs */
+int kapi_register_spec(struct kernel_api_spec *spec);
+void kapi_unregister_spec(const char *name);
+
+/* Helper to get parameter constraint info */
+static inline bool kapi_get_param_constraint(const char *api_name, int param_idx,
+ enum kapi_constraint_type *type,
+ u64 *valid_mask, s64 *min_val, s64 *max_val)
+{
+ const struct kernel_api_spec *spec = kapi_get_spec(api_name);
+
+ if (!spec || param_idx >= spec->param_count)
+ return false;
+
+ if (type)
+ *type = spec->params[param_idx].constraint_type;
+ if (valid_mask)
+ *valid_mask = spec->params[param_idx].valid_mask;
+ if (min_val)
+ *min_val = spec->params[param_idx].min_value;
+ if (max_val)
+ *max_val = spec->params[param_idx].max_value;
+
+ return true;
+}
+
+#ifdef CONFIG_NET
+/* Networking-specific macros */
+
+/* Socket state requirement macros */
+#define KAPI_SOCKET_STATE_REQ(...) \
+ .socket_state = { \
+ .required_states = { __VA_ARGS__ }, \
+ .required_state_count = sizeof((enum kapi_socket_state[]){__VA_ARGS__})/sizeof(enum kapi_socket_state),
+
+#define KAPI_SOCKET_STATE_FORBID(...) \
+ .forbidden_states = { __VA_ARGS__ }, \
+ .forbidden_state_count = sizeof((enum kapi_socket_state[]){__VA_ARGS__})/sizeof(enum kapi_socket_state),
+
+#define KAPI_SOCKET_STATE_RESULT(state) \
+ .resulting_state = state,
+
+#define KAPI_SOCKET_STATE_COND(cond) \
+ .state_condition = cond,
+
+#define KAPI_SOCKET_STATE_PROTOS(protos) \
+ .applicable_protocols = protos,
+
+#define KAPI_SOCKET_STATE_END },
+
+/* Protocol behavior macros */
+#define KAPI_PROTOCOL_BEHAVIOR(idx, protos, desc) \
+ .protocol_behaviors[idx] = { \
+ .applicable_protocols = protos, \
+ .behavior = desc,
+
+#define KAPI_PROTOCOL_FLAGS(flags, desc) \
+ .protocol_flags = flags, \
+ .flag_description = desc,
+
+#define KAPI_PROTOCOL_BEHAVIOR_END },
+
+/* Async behavior macros */
+#define KAPI_ASYNC_SPEC(modes, errno) \
+ .async_spec = { \
+ .supported_modes = modes, \
+ .nonblock_errno = errno,
+
+#define KAPI_ASYNC_POLL(in, out) \
+ .poll_events_in = in, \
+ .poll_events_out = out,
+
+#define KAPI_ASYNC_COMPLETION(cond) \
+ .completion_condition = cond,
+
+#define KAPI_ASYNC_TIMEOUT(supported, desc) \
+ .supports_timeout = supported, \
+ .timeout_behavior = desc,
+
+#define KAPI_ASYNC_END },
+
+/* Buffer behavior macros */
+#define KAPI_BUFFER_SPEC(behaviors) \
+ .buffer_spec = { \
+ .buffer_behaviors = behaviors,
+
+#define KAPI_BUFFER_SIZE(min, max, optimal) \
+ .min_buffer_size = min, \
+ .max_buffer_size = max, \
+ .optimal_buffer_size = optimal,
+
+#define KAPI_BUFFER_PARTIAL(allowed, rules) \
+ .can_partial_transfer = allowed, \
+ .partial_transfer_rules = rules,
+
+#define KAPI_BUFFER_FRAGMENT(rules) \
+ .fragmentation_rules = rules,
+
+#define KAPI_BUFFER_END },
+
+/* Address family macros */
+#define KAPI_ADDR_FAMILY(idx, fam, name, struct_sz, min_len, max_len) \
+ .addr_families[idx] = { \
+ .family = fam, \
+ .family_name = name, \
+ .addr_struct_size = struct_sz, \
+ .min_addr_len = min_len, \
+ .max_addr_len = max_len,
+
+#define KAPI_ADDR_FORMAT(fmt) \
+ .addr_format = fmt,
+
+#define KAPI_ADDR_FEATURES(wildcard, multicast, broadcast) \
+ .supports_wildcard = wildcard, \
+ .supports_multicast = multicast, \
+ .supports_broadcast = broadcast,
+
+#define KAPI_ADDR_SPECIAL(addrs) \
+ .special_addresses = addrs,
+
+#define KAPI_ADDR_PORTS(min, max) \
+ .port_range_min = min, \
+ .port_range_max = max,
+
+#define KAPI_ADDR_FAMILY_END },
+
+#define KAPI_ADDR_FAMILY_COUNT(n) \
+ .addr_family_count = n,
+
+#define KAPI_PROTOCOL_BEHAVIOR_COUNT(n) \
+ .protocol_behavior_count = n,
+
+#define KAPI_CONSTRAINT_COUNT(n) \
+ .constraint_count = n,
+
+/* Network operation characteristics macros */
+#define KAPI_NET_CONNECTION_ORIENTED \
+ .is_connection_oriented = true,
+
+#define KAPI_NET_MESSAGE_ORIENTED \
+ .is_message_oriented = true,
+
+#define KAPI_NET_SUPPORTS_OOB \
+ .supports_oob_data = true,
+
+#define KAPI_NET_SUPPORTS_PEEK \
+ .supports_peek = true,
+
+#define KAPI_NET_REENTRANT \
+ .is_reentrant = true,
+
+/* Semantic description macros */
+#define KAPI_NET_CONN_ESTABLISH(desc) \
+ .connection_establishment = desc,
+
+#define KAPI_NET_CONN_TERMINATE(desc) \
+ .connection_termination = desc,
+
+#define KAPI_NET_DATA_TRANSFER(desc) \
+ .data_transfer_semantics = desc,
+
+#endif /* CONFIG_NET */
+
+#endif /* _LINUX_KERNEL_API_SPEC_H */
diff --git a/include/linux/syscall_api_spec.h b/include/linux/syscall_api_spec.h
new file mode 100644
index 0000000000000..0a813ad89ea15
--- /dev/null
+++ b/include/linux/syscall_api_spec.h
@@ -0,0 +1,137 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * syscall_api_spec.h - System Call API Specification Integration
+ *
+ * This header extends the SYSCALL_DEFINEX macros to support inline API specifications,
+ * allowing syscall documentation to be written alongside the implementation in a
+ * human-readable and machine-parseable format.
+ */
+
+#ifndef _LINUX_SYSCALL_API_SPEC_H
+#define _LINUX_SYSCALL_API_SPEC_H
+
+#include <linux/kernel_api_spec.h>
+
+
+
+/* Automatic syscall validation infrastructure */
+/*
+ * The validation is now integrated directly into the SYSCALL_DEFINEx macros
+ * in syscalls.h when CONFIG_KAPI_RUNTIME_CHECKS is enabled.
+ *
+ * The validation happens in the __do_kapi_sys##name wrapper function which:
+ * 1. Validates all parameters before calling the actual syscall
+ * 2. Calls the real syscall implementation
+ * 3. Validates the return value
+ * 4. Returns the result
+ */
+
+
+/*
+ * Helper macros for common syscall patterns
+ */
+
+/* For syscalls that can sleep */
+#define KAPI_SYSCALL_SLEEPABLE \
+ KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE)
+
+/* For syscalls that must be atomic */
+#define KAPI_SYSCALL_ATOMIC \
+ KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_ATOMIC)
+
+/* Common parameter specifications */
+#define KAPI_PARAM_FD(idx, desc) \
+ KAPI_PARAM(idx, "fd", "int", desc) \
+ KAPI_PARAM_FLAGS(KAPI_PARAM_IN) \
+ .type = KAPI_TYPE_FD, \
+ .constraint_type = KAPI_CONSTRAINT_NONE, \
+ KAPI_PARAM_END
+
+#define KAPI_PARAM_USER_BUF(idx, name, desc) \
+ KAPI_PARAM(idx, name, "void __user *", desc) \
+ KAPI_PARAM_FLAGS(KAPI_PARAM_USER_PTR | KAPI_PARAM_IN) \
+ KAPI_PARAM_END
+
+#define KAPI_PARAM_USER_STRUCT(idx, name, struct_type, desc) \
+ KAPI_PARAM(idx, name, #struct_type " __user *", desc) \
+ KAPI_PARAM_FLAGS(KAPI_PARAM_USER | KAPI_PARAM_IN) \
+ .type = KAPI_TYPE_USER_PTR, \
+ .size = sizeof(struct_type), \
+ .constraint_type = KAPI_CONSTRAINT_NONE, \
+ KAPI_PARAM_END
+
+#define KAPI_PARAM_SIZE_T(idx, name, desc) \
+ KAPI_PARAM(idx, name, "size_t", desc) \
+ KAPI_PARAM_FLAGS(KAPI_PARAM_IN) \
+ KAPI_PARAM_RANGE(0, SIZE_MAX) \
+ KAPI_PARAM_END
+
+/* Common error specifications */
+#define KAPI_ERROR_EBADF(idx) \
+ KAPI_ERROR(idx, -EBADF, "EBADF", "Invalid file descriptor", \
+ "The file descriptor is not valid or has been closed")
+
+#define KAPI_ERROR_EINVAL(idx, condition) \
+ KAPI_ERROR(idx, -EINVAL, "EINVAL", condition, \
+ "Invalid argument provided")
+
+#define KAPI_ERROR_ENOMEM(idx) \
+ KAPI_ERROR(idx, -ENOMEM, "ENOMEM", "Insufficient memory", \
+ "Cannot allocate memory for the operation")
+
+#define KAPI_ERROR_EPERM(idx) \
+ KAPI_ERROR(idx, -EPERM, "EPERM", "Operation not permitted", \
+ "The calling process does not have the required permissions")
+
+#define KAPI_ERROR_EFAULT(idx) \
+ KAPI_ERROR(idx, -EFAULT, "EFAULT", "Bad address", \
+ "Invalid user space address provided")
+
+/* Standard return value specifications */
+#define KAPI_RETURN_SUCCESS_ZERO \
+ KAPI_RETURN("long", "0 on success, negative error code on failure") \
+ KAPI_RETURN_SUCCESS(0, "== 0") \
+ KAPI_RETURN_END
+
+#define KAPI_RETURN_FD_SPEC \
+ KAPI_RETURN("long", "File descriptor on success, negative error code on failure") \
+ .check_type = KAPI_RETURN_FD, \
+ KAPI_RETURN_END
+
+#define KAPI_RETURN_COUNT \
+ KAPI_RETURN("long", "Number of bytes processed on success, negative error code on failure") \
+ KAPI_RETURN_SUCCESS(0, ">= 0") \
+ KAPI_RETURN_END
+
+/**
+ * KAPI_ERROR_COUNT - Set the error count
+ * @count: Number of errors defined
+ */
+#define KAPI_ERROR_COUNT(count) \
+ .error_count = count,
+
+/**
+ * KAPI_PARAM_COUNT - Set the parameter count
+ * @count: Number of parameters defined
+ */
+#define KAPI_PARAM_COUNT(count) \
+ .param_count = count,
+
+/**
+ * KAPI_SINCE_VERSION - Set the since version
+ * @version: Version string when the API was introduced
+ */
+#define KAPI_SINCE_VERSION(version) \
+ .since_version = version,
+
+
+/**
+ * KAPI_SIGNAL_MASK_COUNT - Set the signal mask count
+ * @count: Number of signal masks defined
+ */
+#define KAPI_SIGNAL_MASK_COUNT(count) \
+ .signal_mask_count = count,
+
+
+
+#endif /* _LINUX_SYSCALL_API_SPEC_H */
\ No newline at end of file
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index e5603cc91963d..62a8edc14ec87 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -87,6 +87,7 @@ struct xattr_args;
#include <linux/bug.h>
#include <linux/sem.h>
#include <asm/siginfo.h>
+#include <linux/syscall_api_spec.h>
#include <linux/unistd.h>
#include <linux/quota.h>
#include <linux/key.h>
@@ -132,6 +133,7 @@ struct xattr_args;
#define __SC_TYPE(t, a) t
#define __SC_ARGS(t, a) a
#define __SC_TEST(t, a) (void)BUILD_BUG_ON_ZERO(!__TYPE_IS_LL(t) && sizeof(t) > sizeof(long))
+#define __SC_CAST_TO_S64(t, a) (s64)(a)
#ifdef CONFIG_FTRACE_SYSCALLS
#define __SC_STR_ADECL(t, a) #a
@@ -242,6 +244,41 @@ static inline int is_syscall_trace_event(struct trace_event_call *tp_event)
* done within __do_sys_*().
*/
#ifndef __SYSCALL_DEFINEx
+#ifdef CONFIG_KAPI_RUNTIME_CHECKS
+#define __SYSCALL_DEFINEx(x, name, ...) \
+ __diag_push(); \
+ __diag_ignore(GCC, 8, "-Wattribute-alias", \
+ "Type aliasing is used to sanitize syscall arguments");\
+ asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \
+ __attribute__((alias(__stringify(__se_sys##name)))); \
+ ALLOW_ERROR_INJECTION(sys##name, ERRNO); \
+ static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\
+ static inline long __do_kapi_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \
+ asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \
+ asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \
+ { \
+ long ret = __do_kapi_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\
+ __MAP(x,__SC_TEST,__VA_ARGS__); \
+ __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \
+ return ret; \
+ } \
+ __diag_pop(); \
+ static inline long __do_kapi_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))\
+ { \
+ const struct kernel_api_spec *__spec = kapi_get_spec("sys_" #name); \
+ if (__spec) { \
+ s64 __params[x] = { __MAP(x,__SC_CAST_TO_S64,__VA_ARGS__) }; \
+ int __ret = kapi_validate_syscall_params(__spec, __params, x); \
+ if (__ret) return __ret; \
+ } \
+ long ret = __do_sys##name(__MAP(x,__SC_ARGS,__VA_ARGS__)); \
+ if (__spec) { \
+ kapi_validate_syscall_return(__spec, (s64)ret); \
+ } \
+ return ret; \
+ } \
+ static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))
+#else /* !CONFIG_KAPI_RUNTIME_CHECKS */
#define __SYSCALL_DEFINEx(x, name, ...) \
__diag_push(); \
__diag_ignore(GCC, 8, "-Wattribute-alias", \
@@ -260,6 +297,7 @@ static inline int is_syscall_trace_event(struct trace_event_call *tp_event)
} \
__diag_pop(); \
static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))
+#endif /* CONFIG_KAPI_RUNTIME_CHECKS */
#endif /* __SYSCALL_DEFINEx */
/* For split 64-bit arguments on 32-bit architectures */
diff --git a/init/Kconfig b/init/Kconfig
index af4c2f0854554..7a15248933895 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -2079,6 +2079,8 @@ config TRACEPOINTS
source "kernel/Kconfig.kexec"
+source "kernel/api/Kconfig"
+
endmenu # General setup
source "arch/Kconfig"
diff --git a/kernel/Makefile b/kernel/Makefile
index 32e80dd626af0..ba94ee4bb2292 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -56,6 +56,7 @@ obj-y += livepatch/
obj-y += dma/
obj-y += entry/
obj-$(CONFIG_MODULES) += module/
+obj-$(CONFIG_KAPI_SPEC) += api/
obj-$(CONFIG_KCMP) += kcmp.o
obj-$(CONFIG_FREEZER) += freezer.o
diff --git a/kernel/api/Kconfig b/kernel/api/Kconfig
new file mode 100644
index 0000000000000..fde25ec70e134
--- /dev/null
+++ b/kernel/api/Kconfig
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Kernel API Specification Framework Configuration
+#
+
+config KAPI_SPEC
+ bool "Kernel API Specification Framework"
+ help
+ This option enables the kernel API specification framework,
+ which provides formal documentation of kernel APIs in both
+ human and machine-readable formats.
+
+ The framework allows developers to document APIs inline with
+ their implementation, including parameter specifications,
+ return values, error conditions, locking requirements, and
+ execution context constraints.
+
+ When enabled, API specifications can be queried at runtime
+ and exported in various formats (JSON, XML) through debugfs.
+
+ If unsure, say N.
+
+config KAPI_RUNTIME_CHECKS
+ bool "Runtime API specification checks"
+ depends on KAPI_SPEC
+ depends on DEBUG_KERNEL
+ help
+ Enable runtime validation of API usage against specifications.
+ This includes checking execution context requirements, parameter
+ validation, and lock state verification.
+
+ This adds overhead and should only be used for debugging and
+ development. The checks use WARN_ONCE to report violations.
+
+ If unsure, say N.
diff --git a/kernel/api/Makefile b/kernel/api/Makefile
new file mode 100644
index 0000000000000..4120ded7e5cf1
--- /dev/null
+++ b/kernel/api/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Kernel API Specification Framework
+#
+
+# Core API specification framework
+obj-$(CONFIG_KAPI_SPEC) += kernel_api_spec.o
\ No newline at end of file
diff --git a/kernel/api/kernel_api_spec.c b/kernel/api/kernel_api_spec.c
new file mode 100644
index 0000000000000..8827e9f96c111
--- /dev/null
+++ b/kernel/api/kernel_api_spec.c
@@ -0,0 +1,1122 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * kernel_api_spec.c - Kernel API Specification Framework Implementation
+ *
+ * Provides runtime support for kernel API specifications including validation,
+ * export to various formats, and querying capabilities.
+ */
+
+#include <linux/kernel.h>
+#include <linux/kernel_api_spec.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/export.h>
+#include <linux/preempt.h>
+#include <linux/hardirq.h>
+#include <linux/file.h>
+#include <linux/fdtable.h>
+#include <linux/uaccess.h>
+#include <linux/limits.h>
+#include <linux/fcntl.h>
+
+/* Section where API specifications are stored */
+extern struct kernel_api_spec __start_kapi_specs[];
+extern struct kernel_api_spec __stop_kapi_specs[];
+
+/* Dynamic API registration */
+static LIST_HEAD(dynamic_api_specs);
+static DEFINE_MUTEX(api_spec_mutex);
+
+struct dynamic_api_spec {
+ struct list_head list;
+ struct kernel_api_spec *spec;
+};
+
+/**
+ * kapi_get_spec - Get API specification by name
+ * @name: Function name to look up
+ *
+ * Return: Pointer to API specification or NULL if not found
+ */
+const struct kernel_api_spec *kapi_get_spec(const char *name)
+{
+ struct kernel_api_spec *spec;
+ struct dynamic_api_spec *dyn_spec;
+
+ /* Search static specifications */
+ for (spec = __start_kapi_specs; spec < __stop_kapi_specs; spec++) {
+ if (strcmp(spec->name, name) == 0)
+ return spec;
+ }
+
+ /* Search dynamic specifications */
+ mutex_lock(&api_spec_mutex);
+ list_for_each_entry(dyn_spec, &dynamic_api_specs, list) {
+ if (strcmp(dyn_spec->spec->name, name) == 0) {
+ mutex_unlock(&api_spec_mutex);
+ return dyn_spec->spec;
+ }
+ }
+ mutex_unlock(&api_spec_mutex);
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(kapi_get_spec);
+
+/**
+ * kapi_register_spec - Register a dynamic API specification
+ * @spec: API specification to register
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int kapi_register_spec(struct kernel_api_spec *spec)
+{
+ struct dynamic_api_spec *dyn_spec;
+
+ if (!spec || !spec->name[0])
+ return -EINVAL;
+
+ /* Check if already exists */
+ if (kapi_get_spec(spec->name))
+ return -EEXIST;
+
+ dyn_spec = kzalloc(sizeof(*dyn_spec), GFP_KERNEL);
+ if (!dyn_spec)
+ return -ENOMEM;
+
+ dyn_spec->spec = spec;
+
+ mutex_lock(&api_spec_mutex);
+ list_add_tail(&dyn_spec->list, &dynamic_api_specs);
+ mutex_unlock(&api_spec_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(kapi_register_spec);
+
+/**
+ * kapi_unregister_spec - Unregister a dynamic API specification
+ * @name: Name of API to unregister
+ */
+void kapi_unregister_spec(const char *name)
+{
+ struct dynamic_api_spec *dyn_spec, *tmp;
+
+ mutex_lock(&api_spec_mutex);
+ list_for_each_entry_safe(dyn_spec, tmp, &dynamic_api_specs, list) {
+ if (strcmp(dyn_spec->spec->name, name) == 0) {
+ list_del(&dyn_spec->list);
+ kfree(dyn_spec);
+ break;
+ }
+ }
+ mutex_unlock(&api_spec_mutex);
+}
+EXPORT_SYMBOL_GPL(kapi_unregister_spec);
+
+/**
+ * param_type_to_string - Convert parameter type to string
+ * @type: Parameter type
+ *
+ * Return: String representation of type
+ */
+static const char *param_type_to_string(enum kapi_param_type type)
+{
+ static const char * const type_names[] = {
+ [KAPI_TYPE_VOID] = "void",
+ [KAPI_TYPE_INT] = "int",
+ [KAPI_TYPE_UINT] = "uint",
+ [KAPI_TYPE_PTR] = "pointer",
+ [KAPI_TYPE_STRUCT] = "struct",
+ [KAPI_TYPE_UNION] = "union",
+ [KAPI_TYPE_ENUM] = "enum",
+ [KAPI_TYPE_FUNC_PTR] = "function_pointer",
+ [KAPI_TYPE_ARRAY] = "array",
+ [KAPI_TYPE_FD] = "file_descriptor",
+ [KAPI_TYPE_USER_PTR] = "user_pointer",
+ [KAPI_TYPE_PATH] = "pathname",
+ [KAPI_TYPE_CUSTOM] = "custom",
+ };
+
+ if (type >= ARRAY_SIZE(type_names))
+ return "unknown";
+
+ return type_names[type];
+}
+
+/**
+ * lock_type_to_string - Convert lock type to string
+ * @type: Lock type
+ *
+ * Return: String representation of lock type
+ */
+static const char *lock_type_to_string(enum kapi_lock_type type)
+{
+ static const char * const lock_names[] = {
+ [KAPI_LOCK_NONE] = "none",
+ [KAPI_LOCK_MUTEX] = "mutex",
+ [KAPI_LOCK_SPINLOCK] = "spinlock",
+ [KAPI_LOCK_RWLOCK] = "rwlock",
+ [KAPI_LOCK_SEQLOCK] = "seqlock",
+ [KAPI_LOCK_RCU] = "rcu",
+ [KAPI_LOCK_SEMAPHORE] = "semaphore",
+ [KAPI_LOCK_CUSTOM] = "custom",
+ };
+
+ if (type >= ARRAY_SIZE(lock_names))
+ return "unknown";
+
+ return lock_names[type];
+}
+
+/**
+ * return_check_type_to_string - Convert return check type to string
+ * @type: Return check type
+ *
+ * Return: String representation of return check type
+ */
+static const char *return_check_type_to_string(enum kapi_return_check_type type)
+{
+ static const char * const check_names[] = {
+ [KAPI_RETURN_EXACT] = "exact",
+ [KAPI_RETURN_RANGE] = "range",
+ [KAPI_RETURN_ERROR_CHECK] = "error_check",
+ [KAPI_RETURN_FD] = "file_descriptor",
+ [KAPI_RETURN_CUSTOM] = "custom",
+ };
+
+ if (type >= ARRAY_SIZE(check_names))
+ return "unknown";
+
+ return check_names[type];
+}
+
+/**
+ * capability_action_to_string - Convert capability action to string
+ * @action: Capability action
+ *
+ * Return: String representation of capability action
+ */
+static const char *capability_action_to_string(enum kapi_capability_action action)
+{
+ static const char * const action_names[] = {
+ [KAPI_CAP_BYPASS_CHECK] = "bypass_check",
+ [KAPI_CAP_INCREASE_LIMIT] = "increase_limit",
+ [KAPI_CAP_OVERRIDE_RESTRICTION] = "override_restriction",
+ [KAPI_CAP_GRANT_PERMISSION] = "grant_permission",
+ [KAPI_CAP_MODIFY_BEHAVIOR] = "modify_behavior",
+ [KAPI_CAP_ACCESS_RESOURCE] = "access_resource",
+ [KAPI_CAP_PERFORM_OPERATION] = "perform_operation",
+ };
+
+ if (action >= ARRAY_SIZE(action_names))
+ return "unknown";
+
+ return action_names[action];
+}
+
+/**
+ * kapi_export_json - Export API specification to JSON format
+ * @spec: API specification to export
+ * @buf: Buffer to write JSON to
+ * @size: Size of buffer
+ *
+ * Return: Number of bytes written or negative error
+ */
+int kapi_export_json(const struct kernel_api_spec *spec, char *buf, size_t size)
+{
+ int ret = 0;
+ int i;
+
+ if (!spec || !buf || size == 0)
+ return -EINVAL;
+
+ ret = scnprintf(buf, size,
+ "{\n"
+ " \"name\": \"%s\",\n"
+ " \"version\": %u,\n"
+ " \"description\": \"%s\",\n"
+ " \"long_description\": \"%s\",\n"
+ " \"context_flags\": \"0x%x\",\n",
+ spec->name,
+ spec->version,
+ spec->description,
+ spec->long_description,
+ spec->context_flags);
+
+ /* Parameters */
+ ret += scnprintf(buf + ret, size - ret,
+ " \"parameters\": [\n");
+
+ for (i = 0; i < spec->param_count && i < KAPI_MAX_PARAMS; i++) {
+ const struct kapi_param_spec *param = &spec->params[i];
+
+ ret += scnprintf(buf + ret, size - ret,
+ " {\n"
+ " \"name\": \"%s\",\n"
+ " \"type\": \"%s\",\n"
+ " \"type_class\": \"%s\",\n"
+ " \"flags\": \"0x%x\",\n"
+ " \"description\": \"%s\"\n"
+ " }%s\n",
+ param->name,
+ param->type_name,
+ param_type_to_string(param->type),
+ param->flags,
+ param->description,
+ (i < spec->param_count - 1) ? "," : "");
+ }
+
+ ret += scnprintf(buf + ret, size - ret, " ],\n");
+
+ /* Return value */
+ ret += scnprintf(buf + ret, size - ret,
+ " \"return\": {\n"
+ " \"type\": \"%s\",\n"
+ " \"type_class\": \"%s\",\n"
+ " \"check_type\": \"%s\",\n",
+ spec->return_spec.type_name,
+ param_type_to_string(spec->return_spec.type),
+ return_check_type_to_string(spec->return_spec.check_type));
+
+ switch (spec->return_spec.check_type) {
+ case KAPI_RETURN_EXACT:
+ ret += scnprintf(buf + ret, size - ret,
+ " \"success_value\": %lld,\n",
+ spec->return_spec.success_value);
+ break;
+ case KAPI_RETURN_RANGE:
+ ret += scnprintf(buf + ret, size - ret,
+ " \"success_min\": %lld,\n"
+ " \"success_max\": %lld,\n",
+ spec->return_spec.success_min,
+ spec->return_spec.success_max);
+ break;
+ case KAPI_RETURN_ERROR_CHECK:
+ ret += scnprintf(buf + ret, size - ret,
+ " \"error_count\": %u,\n",
+ spec->return_spec.error_count);
+ break;
+ default:
+ break;
+ }
+
+ ret += scnprintf(buf + ret, size - ret,
+ " \"description\": \"%s\"\n"
+ " },\n",
+ spec->return_spec.description);
+
+ /* Errors */
+ ret += scnprintf(buf + ret, size - ret,
+ " \"errors\": [\n");
+
+ for (i = 0; i < spec->error_count && i < KAPI_MAX_ERRORS; i++) {
+ const struct kapi_error_spec *error = &spec->errors[i];
+
+ ret += scnprintf(buf + ret, size - ret,
+ " {\n"
+ " \"code\": %d,\n"
+ " \"name\": \"%s\",\n"
+ " \"condition\": \"%s\",\n"
+ " \"description\": \"%s\"\n"
+ " }%s\n",
+ error->error_code,
+ error->name,
+ error->condition,
+ error->description,
+ (i < spec->error_count - 1) ? "," : "");
+ }
+
+ ret += scnprintf(buf + ret, size - ret, " ],\n");
+
+ /* Locks */
+ ret += scnprintf(buf + ret, size - ret,
+ " \"locks\": [\n");
+
+ for (i = 0; i < spec->lock_count && i < KAPI_MAX_CONSTRAINTS; i++) {
+ const struct kapi_lock_spec *lock = &spec->locks[i];
+
+ ret += scnprintf(buf + ret, size - ret,
+ " {\n"
+ " \"name\": \"%s\",\n"
+ " \"type\": \"%s\",\n"
+ " \"acquired\": %s,\n"
+ " \"released\": %s,\n"
+ " \"held_on_entry\": %s,\n"
+ " \"held_on_exit\": %s,\n"
+ " \"description\": \"%s\"\n"
+ " }%s\n",
+ lock->lock_name,
+ lock_type_to_string(lock->lock_type),
+ lock->acquired ? "true" : "false",
+ lock->released ? "true" : "false",
+ lock->held_on_entry ? "true" : "false",
+ lock->held_on_exit ? "true" : "false",
+ lock->description,
+ (i < spec->lock_count - 1) ? "," : "");
+ }
+
+ ret += scnprintf(buf + ret, size - ret, " ],\n");
+
+ /* Capabilities */
+ ret += scnprintf(buf + ret, size - ret,
+ " \"capabilities\": [\n");
+
+ for (i = 0; i < spec->capability_count && i < KAPI_MAX_CAPABILITIES; i++) {
+ const struct kapi_capability_spec *cap = &spec->capabilities[i];
+
+ ret += scnprintf(buf + ret, size - ret,
+ " {\n"
+ " \"capability\": %d,\n"
+ " \"name\": \"%s\",\n"
+ " \"action\": \"%s\",\n"
+ " \"allows\": \"%s\",\n"
+ " \"without_cap\": \"%s\",\n"
+ " \"check_condition\": \"%s\",\n"
+ " \"priority\": %u",
+ cap->capability,
+ cap->cap_name,
+ capability_action_to_string(cap->action),
+ cap->allows,
+ cap->without_cap,
+ cap->check_condition,
+ cap->priority);
+
+ if (cap->alternative_count > 0) {
+ int j;
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n \"alternatives\": [");
+ for (j = 0; j < cap->alternative_count; j++) {
+ ret += scnprintf(buf + ret, size - ret,
+ "%d%s", cap->alternative[j],
+ (j < cap->alternative_count - 1) ? ", " : "");
+ }
+ ret += scnprintf(buf + ret, size - ret, "]");
+ }
+
+ ret += scnprintf(buf + ret, size - ret,
+ "\n }%s\n",
+ (i < spec->capability_count - 1) ? "," : "");
+ }
+
+ ret += scnprintf(buf + ret, size - ret, " ],\n");
+
+ /* Additional info */
+ ret += scnprintf(buf + ret, size - ret,
+ " \"since_version\": \"%s\",\n"
+ " \"deprecated\": %s,\n"
+ " \"replacement\": \"%s\",\n"
+ " \"examples\": \"%s\",\n"
+ " \"notes\": \"%s\"\n"
+ "}\n",
+ spec->since_version,
+ spec->deprecated ? "true" : "false",
+ spec->replacement,
+ spec->examples,
+ spec->notes);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(kapi_export_json);
+
+
+/**
+ * kapi_print_spec - Print API specification to kernel log
+ * @spec: API specification to print
+ */
+void kapi_print_spec(const struct kernel_api_spec *spec)
+{
+ int i;
+
+ if (!spec)
+ return;
+
+ pr_info("=== Kernel API Specification ===\n");
+ pr_info("Name: %s\n", spec->name);
+ pr_info("Version: %u\n", spec->version);
+ pr_info("Description: %s\n", spec->description);
+
+ if (spec->long_description[0])
+ pr_info("Long Description: %s\n", spec->long_description);
+
+ pr_info("Context Flags: 0x%x\n", spec->context_flags);
+
+ /* Parameters */
+ if (spec->param_count > 0) {
+ pr_info("Parameters:\n");
+ for (i = 0; i < spec->param_count && i < KAPI_MAX_PARAMS; i++) {
+ const struct kapi_param_spec *param = &spec->params[i];
+ pr_info(" [%d] %s: %s (flags: 0x%x)\n",
+ i, param->name, param->type_name, param->flags);
+ if (param->description[0])
+ pr_info(" Description: %s\n", param->description);
+ }
+ }
+
+ /* Return value */
+ pr_info("Return: %s\n", spec->return_spec.type_name);
+ if (spec->return_spec.description[0])
+ pr_info(" Description: %s\n", spec->return_spec.description);
+
+ /* Errors */
+ if (spec->error_count > 0) {
+ pr_info("Possible Errors:\n");
+ for (i = 0; i < spec->error_count && i < KAPI_MAX_ERRORS; i++) {
+ const struct kapi_error_spec *error = &spec->errors[i];
+ pr_info(" %s (%d): %s\n",
+ error->name, error->error_code, error->condition);
+ }
+ }
+
+ /* Capabilities */
+ if (spec->capability_count > 0) {
+ pr_info("Capabilities:\n");
+ for (i = 0; i < spec->capability_count && i < KAPI_MAX_CAPABILITIES; i++) {
+ const struct kapi_capability_spec *cap = &spec->capabilities[i];
+ pr_info(" %s (%d):\n", cap->cap_name, cap->capability);
+ pr_info(" Action: %s\n", capability_action_to_string(cap->action));
+ pr_info(" Allows: %s\n", cap->allows);
+ pr_info(" Without: %s\n", cap->without_cap);
+ if (cap->check_condition[0])
+ pr_info(" Condition: %s\n", cap->check_condition);
+ }
+ }
+
+ pr_info("================================\n");
+}
+EXPORT_SYMBOL_GPL(kapi_print_spec);
+
+#ifdef CONFIG_KAPI_RUNTIME_CHECKS
+
+/**
+ * kapi_validate_fd - Validate that a file descriptor is valid in current context
+ * @fd: File descriptor to validate
+ *
+ * Return: true if fd is valid in current process context, false otherwise
+ */
+static bool kapi_validate_fd(int fd)
+{
+ struct fd f;
+
+ /* Special case: AT_FDCWD is always valid */
+ if (fd == AT_FDCWD)
+ return true;
+
+ /* Check basic range */
+ if (fd < 0)
+ return false;
+
+ /* Check if fd is valid in current process context */
+ f = fdget(fd);
+ if (fd_empty(f)) {
+ return false;
+ }
+
+ /* fd is valid, release reference */
+ fdput(f);
+ return true;
+}
+
+/**
+ * kapi_validate_user_ptr - Validate that a user pointer is accessible
+ * @ptr: User pointer to validate
+ * @size: Size in bytes to validate
+ * @write: Whether write access is required
+ *
+ * Return: true if user memory is accessible, false otherwise
+ */
+static bool kapi_validate_user_ptr(const void __user *ptr, size_t size, bool write)
+{
+ /* NULL is valid if parameter is marked optional */
+ if (!ptr)
+ return false;
+
+ /* Check if the user memory region is accessible */
+ if (write) {
+ return access_ok(ptr, size);
+ } else {
+ return access_ok(ptr, size);
+ }
+}
+
+/**
+ * kapi_validate_user_ptr_with_params - Validate user pointer with dynamic size
+ * @param_spec: Parameter specification
+ * @ptr: User pointer to validate
+ * @all_params: Array of all parameter values
+ * @param_count: Number of parameters
+ *
+ * Return: true if user memory is accessible, false otherwise
+ */
+static bool kapi_validate_user_ptr_with_params(const struct kapi_param_spec *param_spec,
+ const void __user *ptr,
+ const s64 *all_params,
+ int param_count)
+{
+ size_t actual_size;
+ bool write;
+
+ /* NULL is allowed for optional parameters */
+ if (!ptr && (param_spec->flags & KAPI_PARAM_OPTIONAL))
+ return true;
+
+ /* Calculate actual size based on related parameter */
+ if (param_spec->size_param_idx >= 0 &&
+ param_spec->size_param_idx < param_count) {
+ s64 count = all_params[param_spec->size_param_idx];
+
+ /* Validate count is positive */
+ if (count <= 0) {
+ pr_warn("Parameter %s: size determinant is non-positive (%lld)\n",
+ param_spec->name, count);
+ return false;
+ }
+
+ /* Check for multiplication overflow */
+ if (param_spec->size_multiplier > 0 &&
+ count > SIZE_MAX / param_spec->size_multiplier) {
+ pr_warn("Parameter %s: size calculation overflow\n",
+ param_spec->name);
+ return false;
+ }
+
+ actual_size = count * param_spec->size_multiplier;
+ } else {
+ /* Use fixed size */
+ actual_size = param_spec->size;
+ }
+
+ write = (param_spec->flags & KAPI_PARAM_OUT) ||
+ (param_spec->flags & KAPI_PARAM_INOUT);
+
+ return kapi_validate_user_ptr(ptr, actual_size, write);
+}
+
+/**
+ * kapi_validate_path - Validate that a pathname is accessible and within limits
+ * @path: User pointer to pathname
+ * @param_spec: Parameter specification
+ *
+ * Return: true if path is valid, false otherwise
+ */
+static bool kapi_validate_path(const char __user *path,
+ const struct kapi_param_spec *param_spec)
+{
+ size_t len;
+
+ /* NULL is allowed for optional parameters */
+ if (!path && (param_spec->flags & KAPI_PARAM_OPTIONAL))
+ return true;
+
+ if (!path) {
+ pr_warn("Parameter %s: NULL path not allowed\n", param_spec->name);
+ return false;
+ }
+
+ /* Check if the path is accessible */
+ if (!access_ok(path, 1)) {
+ pr_warn("Parameter %s: path pointer %p not accessible\n",
+ param_spec->name, path);
+ return false;
+ }
+
+ /* Use strnlen_user to get the length and validate accessibility */
+ len = strnlen_user(path, PATH_MAX + 1);
+ if (len == 0) {
+ pr_warn("Parameter %s: invalid path pointer %p\n",
+ param_spec->name, path);
+ return false;
+ }
+
+ /* Check path length limit */
+ if (len > PATH_MAX) {
+ pr_warn("Parameter %s: path too long (exceeds PATH_MAX)\n",
+ param_spec->name);
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * kapi_validate_param - Validate a parameter against its specification
+ * @param_spec: Parameter specification
+ * @value: Parameter value to validate
+ *
+ * Return: true if valid, false otherwise
+ */
+bool kapi_validate_param(const struct kapi_param_spec *param_spec, s64 value)
+{
+ int i;
+
+ /* Special handling for file descriptor type */
+ if (param_spec->type == KAPI_TYPE_FD) {
+ if (!kapi_validate_fd((int)value)) {
+ pr_warn("Parameter %s: invalid file descriptor %lld\n",
+ param_spec->name, value);
+ return false;
+ }
+ /* Continue with additional constraint checks if needed */
+ }
+
+ /* Special handling for user pointer type */
+ if (param_spec->type == KAPI_TYPE_USER_PTR) {
+ const void __user *ptr = (const void __user *)value;
+ bool write = (param_spec->flags & KAPI_PARAM_OUT) ||
+ (param_spec->flags & KAPI_PARAM_INOUT);
+
+ /* NULL is allowed for optional parameters */
+ if (!ptr && (param_spec->flags & KAPI_PARAM_OPTIONAL))
+ return true;
+
+ if (!kapi_validate_user_ptr(ptr, param_spec->size, write)) {
+ pr_warn("Parameter %s: invalid user pointer %p (size: %zu, %s)\n",
+ param_spec->name, ptr, param_spec->size,
+ write ? "write" : "read");
+ return false;
+ }
+ /* Continue with additional constraint checks if needed */
+ }
+
+ /* Special handling for path type */
+ if (param_spec->type == KAPI_TYPE_PATH) {
+ const char __user *path = (const char __user *)value;
+
+ if (!kapi_validate_path(path, param_spec)) {
+ return false;
+ }
+ /* Continue with additional constraint checks if needed */
+ }
+
+ switch (param_spec->constraint_type) {
+ case KAPI_CONSTRAINT_NONE:
+ return true;
+
+ case KAPI_CONSTRAINT_RANGE:
+ if (value < param_spec->min_value || value > param_spec->max_value) {
+ pr_warn("Parameter %s value %lld out of range [%lld, %lld]\n",
+ param_spec->name, value,
+ param_spec->min_value, param_spec->max_value);
+ return false;
+ }
+ return true;
+
+ case KAPI_CONSTRAINT_MASK:
+ if (value & ~param_spec->valid_mask) {
+ pr_warn("Parameter %s value 0x%llx contains invalid bits (valid mask: 0x%llx)\n",
+ param_spec->name, value, param_spec->valid_mask);
+ return false;
+ }
+ return true;
+
+ case KAPI_CONSTRAINT_ENUM:
+ if (!param_spec->enum_values || param_spec->enum_count == 0)
+ return true;
+
+ for (i = 0; i < param_spec->enum_count; i++) {
+ if (value == param_spec->enum_values[i])
+ return true;
+ }
+ pr_warn("Parameter %s value %lld not in valid enumeration\n",
+ param_spec->name, value);
+ return false;
+
+ case KAPI_CONSTRAINT_CUSTOM:
+ if (param_spec->validate)
+ return param_spec->validate(value);
+ return true;
+
+ default:
+ return true;
+ }
+}
+EXPORT_SYMBOL_GPL(kapi_validate_param);
+
+/**
+ * kapi_validate_param_with_context - Validate parameter with access to all params
+ * @param_spec: Parameter specification
+ * @value: Parameter value to validate
+ * @all_params: Array of all parameter values
+ * @param_count: Number of parameters
+ *
+ * Return: true if valid, false otherwise
+ */
+bool kapi_validate_param_with_context(const struct kapi_param_spec *param_spec,
+ s64 value, const s64 *all_params, int param_count)
+{
+ /* Special handling for user pointer type with dynamic sizing */
+ if (param_spec->type == KAPI_TYPE_USER_PTR) {
+ const void __user *ptr = (const void __user *)value;
+
+ /* NULL is allowed for optional parameters */
+ if (!ptr && (param_spec->flags & KAPI_PARAM_OPTIONAL))
+ return true;
+
+ if (!kapi_validate_user_ptr_with_params(param_spec, ptr, all_params, param_count)) {
+ pr_warn("Parameter %s: invalid user pointer %p\n",
+ param_spec->name, ptr);
+ return false;
+ }
+ /* Continue with additional constraint checks if needed */
+ }
+
+ /* For other types, fall back to regular validation */
+ return kapi_validate_param(param_spec, value);
+}
+EXPORT_SYMBOL_GPL(kapi_validate_param_with_context);
+
+/**
+ * kapi_validate_syscall_param - Validate syscall parameter with enforcement
+ * @spec: API specification
+ * @param_idx: Parameter index
+ * @value: Parameter value
+ *
+ * Return: -EINVAL if invalid, 0 if valid
+ */
+int kapi_validate_syscall_param(const struct kernel_api_spec *spec,
+ int param_idx, s64 value)
+{
+ const struct kapi_param_spec *param_spec;
+
+ if (!spec || param_idx >= spec->param_count)
+ return 0;
+
+ param_spec = &spec->params[param_idx];
+
+ if (!kapi_validate_param(param_spec, value)) {
+ if (strncmp(spec->name, "sys_", 4) == 0) {
+ /* For syscalls, we can return EINVAL to userspace */
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(kapi_validate_syscall_param);
+
+/**
+ * kapi_validate_syscall_params - Validate all syscall parameters together
+ * @spec: API specification
+ * @params: Array of parameter values
+ * @param_count: Number of parameters
+ *
+ * Return: -EINVAL if any parameter is invalid, 0 if all valid
+ */
+int kapi_validate_syscall_params(const struct kernel_api_spec *spec,
+ const s64 *params, int param_count)
+{
+ int i;
+
+ if (!spec || !params)
+ return 0;
+
+ /* Validate that we have the expected number of parameters */
+ if (param_count != spec->param_count) {
+ pr_warn("API %s: parameter count mismatch (expected %u, got %d)\n",
+ spec->name, spec->param_count, param_count);
+ return -EINVAL;
+ }
+
+ /* Validate each parameter with context */
+ for (i = 0; i < spec->param_count && i < KAPI_MAX_PARAMS; i++) {
+ const struct kapi_param_spec *param_spec = &spec->params[i];
+
+ if (!kapi_validate_param_with_context(param_spec, params[i], params, param_count)) {
+ if (strncmp(spec->name, "sys_", 4) == 0) {
+ /* For syscalls, we can return EINVAL to userspace */
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(kapi_validate_syscall_params);
+
+/**
+ * kapi_check_return_success - Check if return value indicates success
+ * @return_spec: Return specification
+ * @retval: Return value to check
+ *
+ * Returns true if the return value indicates success according to the spec.
+ */
+bool kapi_check_return_success(const struct kapi_return_spec *return_spec, s64 retval)
+{
+ u32 i;
+
+ if (!return_spec)
+ return true; /* No spec means we can't validate */
+
+ switch (return_spec->check_type) {
+ case KAPI_RETURN_EXACT:
+ return retval == return_spec->success_value;
+
+ case KAPI_RETURN_RANGE:
+ return retval >= return_spec->success_min &&
+ retval <= return_spec->success_max;
+
+ case KAPI_RETURN_ERROR_CHECK:
+ /* Success if NOT in error list */
+ if (return_spec->error_values) {
+ for (i = 0; i < return_spec->error_count; i++) {
+ if (retval == return_spec->error_values[i])
+ return false; /* Found in error list */
+ }
+ }
+ return true; /* Not in error list = success */
+
+ case KAPI_RETURN_FD:
+ /* File descriptors: >= 0 is success, < 0 is error */
+ return retval >= 0;
+
+ case KAPI_RETURN_CUSTOM:
+ if (return_spec->is_success)
+ return return_spec->is_success(retval);
+ fallthrough;
+
+ default:
+ return true; /* Unknown check type, assume success */
+ }
+}
+EXPORT_SYMBOL_GPL(kapi_check_return_success);
+
+/**
+ * kapi_validate_return_value - Validate that return value matches spec
+ * @spec: API specification
+ * @retval: Return value to validate
+ *
+ * Return: true if return value is valid according to spec, false otherwise.
+ *
+ * This function checks:
+ * 1. If the value indicates success, it must match the success criteria
+ * 2. If the value indicates error, it must be one of the specified error codes
+ */
+bool kapi_validate_return_value(const struct kernel_api_spec *spec, s64 retval)
+{
+ int i;
+ bool is_success;
+
+ if (!spec)
+ return true; /* No spec means we can't validate */
+
+ /* First check if this is a success return */
+ is_success = kapi_check_return_success(&spec->return_spec, retval);
+
+ if (is_success) {
+ /* Success case - already validated by kapi_check_return_success */
+ return true;
+ }
+
+ /* Special validation for file descriptor returns */
+ if (spec->return_spec.check_type == KAPI_RETURN_FD && is_success) {
+ /* For successful FD returns, validate it's a valid FD */
+ if (!kapi_validate_fd((int)retval)) {
+ pr_warn("API %s returned invalid file descriptor %lld\n",
+ spec->name, retval);
+ return false;
+ }
+ return true;
+ }
+
+ /* Error case - check if it's one of the specified errors */
+ if (spec->error_count == 0) {
+ /* No errors specified, so any error is potentially valid */
+ pr_debug("API %s returned unspecified error %lld\n",
+ spec->name, retval);
+ return true;
+ }
+
+ /* Check if the error is in our list of specified errors */
+ for (i = 0; i < spec->error_count && i < KAPI_MAX_ERRORS; i++) {
+ if (retval == spec->errors[i].error_code)
+ return true;
+ }
+
+ /* Error not in spec */
+ pr_warn("API %s returned unspecified error code %lld. Valid errors are:\n",
+ spec->name, retval);
+ for (i = 0; i < spec->error_count && i < KAPI_MAX_ERRORS; i++) {
+ pr_warn(" %s (%d): %s\n",
+ spec->errors[i].name,
+ spec->errors[i].error_code,
+ spec->errors[i].condition);
+ }
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(kapi_validate_return_value);
+
+/**
+ * kapi_validate_syscall_return - Validate syscall return value with enforcement
+ * @spec: API specification
+ * @retval: Return value
+ *
+ * Return: 0 if valid, -EINVAL if the return value doesn't match spec
+ *
+ * For syscalls, this can help detect kernel bugs where unspecified error
+ * codes are returned to userspace.
+ */
+int kapi_validate_syscall_return(const struct kernel_api_spec *spec, s64 retval)
+{
+ if (!spec)
+ return 0;
+
+ if (!kapi_validate_return_value(spec, retval)) {
+ /* Log the violation but don't change the return value */
+ WARN_ONCE(1, "Syscall %s returned unspecified value %lld\n",
+ spec->name, retval);
+ /* Could return -EINVAL here to enforce, but that might break userspace */
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(kapi_validate_syscall_return);
+
+/**
+ * kapi_check_context - Check if current context matches API requirements
+ * @spec: API specification to check against
+ */
+void kapi_check_context(const struct kernel_api_spec *spec)
+{
+ u32 ctx = spec->context_flags;
+ bool valid = false;
+
+ if (!ctx)
+ return;
+
+ /* Check if we're in an allowed context */
+ if ((ctx & KAPI_CTX_PROCESS) && !in_interrupt())
+ valid = true;
+
+ if ((ctx & KAPI_CTX_SOFTIRQ) && in_softirq())
+ valid = true;
+
+ if ((ctx & KAPI_CTX_HARDIRQ) && in_hardirq())
+ valid = true;
+
+ if ((ctx & KAPI_CTX_NMI) && in_nmi())
+ valid = true;
+
+ if (!valid) {
+ WARN_ONCE(1, "API %s called from invalid context\n", spec->name);
+ }
+
+ /* Check specific requirements */
+ if ((ctx & KAPI_CTX_ATOMIC) && preemptible()) {
+ WARN_ONCE(1, "API %s requires atomic context\n", spec->name);
+ }
+
+ if ((ctx & KAPI_CTX_SLEEPABLE) && !preemptible()) {
+ WARN_ONCE(1, "API %s requires sleepable context\n", spec->name);
+ }
+}
+EXPORT_SYMBOL_GPL(kapi_check_context);
+
+#endif /* CONFIG_KAPI_RUNTIME_CHECKS */
+
+/* DebugFS interface */
+#ifdef CONFIG_DEBUG_FS
+
+static struct dentry *kapi_debugfs_root;
+
+static int kapi_spec_show(struct seq_file *s, void *v)
+{
+ struct kernel_api_spec *spec = s->private;
+ char *buf;
+ int ret;
+
+ buf = kmalloc(PAGE_SIZE * 4, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = kapi_export_json(spec, buf, PAGE_SIZE * 4);
+ if (ret > 0)
+ seq_printf(s, "%s", buf);
+
+ kfree(buf);
+ return 0;
+}
+
+static int kapi_spec_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, kapi_spec_show, inode->i_private);
+}
+
+static const struct file_operations kapi_spec_fops = {
+ .open = kapi_spec_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int kapi_list_show(struct seq_file *s, void *v)
+{
+ struct kernel_api_spec *spec;
+ struct dynamic_api_spec *dyn_spec;
+
+ seq_printf(s, "Kernel API Specifications:\n\n");
+
+ /* List static specifications */
+ seq_printf(s, "Static APIs:\n");
+ for (spec = __start_kapi_specs; spec < __stop_kapi_specs; spec++) {
+ seq_printf(s, " %s (v%u): %s\n",
+ spec->name, spec->version, spec->description);
+ }
+
+ /* List dynamic specifications */
+ seq_printf(s, "\nDynamic APIs:\n");
+ mutex_lock(&api_spec_mutex);
+ list_for_each_entry(dyn_spec, &dynamic_api_specs, list) {
+ spec = dyn_spec->spec;
+ seq_printf(s, " %s (v%u): %s\n",
+ spec->name, spec->version, spec->description);
+ }
+ mutex_unlock(&api_spec_mutex);
+
+ return 0;
+}
+
+static int kapi_list_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, kapi_list_show, NULL);
+}
+
+static const struct file_operations kapi_list_fops = {
+ .open = kapi_list_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init kapi_debugfs_init(void)
+{
+ struct kernel_api_spec *spec;
+ struct dentry *spec_dir;
+
+ kapi_debugfs_root = debugfs_create_dir("kapi", NULL);
+ if (!kapi_debugfs_root)
+ return -ENOMEM;
+
+ /* Create list file */
+ debugfs_create_file("list", 0444, kapi_debugfs_root, NULL,
+ &kapi_list_fops);
+
+ /* Create directory for specifications */
+ spec_dir = debugfs_create_dir("specs", kapi_debugfs_root);
+
+ /* Create files for each static specification */
+ for (spec = __start_kapi_specs; spec < __stop_kapi_specs; spec++) {
+ debugfs_create_file(spec->name, 0444, spec_dir, spec,
+ &kapi_spec_fops);
+ }
+
+ return 0;
+}
+
+late_initcall(kapi_debugfs_init);
+
+#endif /* CONFIG_DEBUG_FS */
\ No newline at end of file
--
2.39.5
^ permalink raw reply related
* [RFC v2 02/22] eventpoll: add API specification for epoll_create1
From: Sasha Levin @ 2025-06-24 18:07 UTC (permalink / raw)
To: linux-kernel; +Cc: linux-doc, linux-api, workflows, tools, Sasha Levin
In-Reply-To: <20250624180742.5795-1-sashal@kernel.org>
Add kernel API specification for the epoll_create1() system call.
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/eventpoll.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 124 insertions(+)
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index d4dbffdedd08e..620de3ccc7708 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -21,6 +21,7 @@
#include <linux/hash.h>
#include <linux/spinlock.h>
#include <linux/syscalls.h>
+#include <linux/syscall_api_spec.h>
#include <linux/rbtree.h>
#include <linux/wait.h>
#include <linux/eventpoll.h>
@@ -2265,6 +2266,129 @@ static int do_epoll_create(int flags)
return error;
}
+
+/* Valid values for epoll_create1 flags parameter */
+static const s64 epoll_create1_valid_values[] = { 0, EPOLL_CLOEXEC };
+
+DEFINE_KERNEL_API_SPEC(sys_epoll_create1)
+ KAPI_DESCRIPTION("Create an epoll instance")
+ KAPI_LONG_DESC("Creates a new epoll instance and returns a file descriptor "
+ "referring to that instance. The file descriptor is used for all "
+ "subsequent calls to the epoll interface.")
+ KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE)
+
+ KAPI_PARAM(0, "flags", "int", "Creation flags for the epoll instance")
+ KAPI_PARAM_FLAGS(KAPI_PARAM_IN)
+ KAPI_PARAM_CONSTRAINT_TYPE(KAPI_CONSTRAINT_ENUM)
+ KAPI_PARAM_ENUM_VALUES(epoll_create1_valid_values)
+ KAPI_PARAM_CONSTRAINT("Must be 0 or EPOLL_CLOEXEC")
+ KAPI_PARAM_END
+
+ KAPI_RETURN("long", "File descriptor on success, negative error code on failure")
+ KAPI_RETURN_TYPE(KAPI_TYPE_INT)
+ KAPI_RETURN_CHECK_TYPE(KAPI_RETURN_FD)
+ KAPI_RETURN_END
+
+ KAPI_ERROR(0, -EINVAL, "EINVAL", "Invalid flags specified",
+ "The flags parameter contains invalid values. Only EPOLL_CLOEXEC is allowed.")
+ KAPI_ERROR(1, -EMFILE, "EMFILE", "Per-process file descriptor limit reached",
+ "The per-process limit on the number of open file descriptors has been reached.")
+ KAPI_ERROR(2, -ENFILE, "ENFILE", "System file table overflow",
+ "The system-wide limit on the total number of open files has been reached.")
+ KAPI_ERROR(3, -ENOMEM, "ENOMEM", "Insufficient kernel memory",
+ "There was insufficient kernel memory to create the epoll instance.")
+ KAPI_ERROR(4, -EINTR, "EINTR", "Interrupted by signal",
+ "The system call was interrupted by a signal before the epoll instance could be created.")
+
+ .error_count = 5,
+ .param_count = 1,
+ .since_version = "2.6.27",
+ KAPI_EXAMPLES("int epfd = epoll_create1(EPOLL_CLOEXEC);")
+ KAPI_NOTES("EPOLL_CLOEXEC sets the close-on-exec (FD_CLOEXEC) flag on the new file descriptor. "
+ "When all file descriptors referring to an epoll instance are closed, the kernel "
+ "destroys the instance and releases associated resources. "
+ "Memory consumption: Each registered fd uses approximately 90 bytes on 32-bit kernels "
+ "and 160 bytes on 64-bit kernels. The total number of file descriptors registered "
+ "across all epoll instances is limited by /proc/sys/fs/epoll/max_user_watches. "
+ "When using dup() or fork(), multiple file descriptors may refer to the same epoll "
+ "instance and all will receive events.")
+
+ /* Side effects */
+ KAPI_SIDE_EFFECT(0, KAPI_EFFECT_RESOURCE_CREATE | KAPI_EFFECT_ALLOC_MEMORY,
+ "epoll instance",
+ "Creates a new epoll instance and allocates kernel memory for it")
+ KAPI_EFFECT_REVERSIBLE
+ KAPI_SIDE_EFFECT_END
+
+ KAPI_SIDE_EFFECT(1, KAPI_EFFECT_RESOURCE_CREATE,
+ "file descriptor",
+ "Allocates a new file descriptor in the process's file descriptor table")
+ KAPI_EFFECT_REVERSIBLE
+ KAPI_SIDE_EFFECT_END
+
+ KAPI_SIDE_EFFECT_COUNT(2)
+
+ /* State transitions */
+ KAPI_STATE_TRANS(0, "epoll instance", "non-existent", "created and empty",
+ "A new epoll instance is created with no monitored file descriptors")
+ KAPI_STATE_TRANS_END
+
+ KAPI_STATE_TRANS_COUNT(1)
+
+ /* Signal specifications */
+ KAPI_SIGNAL(0, SIGINT, "SIGINT", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_RETURN)
+ KAPI_SIGNAL_CONDITION("During creation if process receives SIGINT")
+ KAPI_SIGNAL_DESC("If interrupted during kernel memory allocation, returns -EINTR")
+ KAPI_SIGNAL_TIMING(KAPI_SIGNAL_TIME_DURING)
+ KAPI_SIGNAL_PRIORITY(1)
+ KAPI_SIGNAL_INTERRUPTIBLE
+ KAPI_SIGNAL_ERROR(-EINTR)
+ KAPI_SIGNAL_STATE_REQ(KAPI_SIGNAL_STATE_RUNNING | KAPI_SIGNAL_STATE_SLEEPING)
+ KAPI_SIGNAL_RESTARTABLE
+ KAPI_SIGNAL_END
+
+ KAPI_SIGNAL(1, SIGTERM, "SIGTERM", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_RETURN)
+ KAPI_SIGNAL_CONDITION("During creation if process receives SIGTERM")
+ KAPI_SIGNAL_DESC("If interrupted during kernel memory allocation, returns -EINTR")
+ KAPI_SIGNAL_TIMING(KAPI_SIGNAL_TIME_DURING)
+ KAPI_SIGNAL_PRIORITY(1)
+ KAPI_SIGNAL_INTERRUPTIBLE
+ KAPI_SIGNAL_ERROR(-EINTR)
+ KAPI_SIGNAL_STATE_REQ(KAPI_SIGNAL_STATE_RUNNING | KAPI_SIGNAL_STATE_SLEEPING)
+ KAPI_SIGNAL_RESTARTABLE
+ KAPI_SIGNAL_END
+
+ KAPI_SIGNAL(2, SIGKILL, "SIGKILL", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_TERMINATE)
+ KAPI_SIGNAL_CONDITION("At any point during the syscall")
+ KAPI_SIGNAL_DESC("Process is terminated immediately, epoll instance creation may be incomplete")
+ KAPI_SIGNAL_TIMING(KAPI_SIGNAL_TIME_ANYTIME)
+ KAPI_SIGNAL_PRIORITY(0)
+ KAPI_SIGNAL_QUEUE("uncatchable")
+ KAPI_SIGNAL_END
+
+ .signal_count = 3,
+
+ /* Additional constraints */
+ KAPI_CONSTRAINT(0, "User Watch Limit",
+ "Although epoll_create1() itself doesn't register any watches, the "
+ "user is subject to a global limit on total watches across all epoll "
+ "instances. This limit is configured via /proc/sys/fs/epoll/max_user_watches "
+ "(default: 1/25 of lowmem or 1/32 of total memory). Each registered "
+ "file descriptor counts against this limit.")
+ KAPI_CONSTRAINT_EXPR("current_user_watches < max_user_watches")
+ KAPI_CONSTRAINT_END
+
+ KAPI_CONSTRAINT(1, "Memory Accounting",
+ "Each epoll instance consumes kernel memory that is not swappable. "
+ "The instance itself uses approximately 1KB, plus additional memory "
+ "for each registered file descriptor (90 bytes on 32-bit, 160 bytes "
+ "on 64-bit systems). This memory is charged to the user's locked "
+ "memory limit if memory cgroups are enabled.")
+ KAPI_CONSTRAINT_END
+
+ KAPI_CONSTRAINT_COUNT(2)
+
+KAPI_END_SPEC;
SYSCALL_DEFINE1(epoll_create1, int, flags)
{
return do_epoll_create(flags);
--
2.39.5
^ permalink raw reply related
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