From: Sasha Levin <sashal@kernel.org>
To: linux-api@vger.kernel.org, linux-kernel@vger.kernel.org
Cc: Sasha Levin <sashal@kernel.org>,
linux-doc@vger.kernel.org, linux-fsdevel@vger.kernel.org,
linux-kbuild@vger.kernel.org, linux-kselftest@vger.kernel.org,
workflows@vger.kernel.org, tools@kernel.org, x86@kernel.org,
Thomas Gleixner <tglx@kernel.org>,
"Paul E . McKenney" <paulmck@kernel.org>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Jonathan Corbet <corbet@lwn.net>,
Dmitry Vyukov <dvyukov@google.com>,
Randy Dunlap <rdunlap@infradead.org>,
Cyril Hrubis <chrubis@suse.cz>, Kees Cook <kees@kernel.org>,
Jake Edge <jake@lwn.net>,
David Laight <david.laight.linux@gmail.com>,
Gabriele Paoloni <gpaoloni@redhat.com>,
Mauro Carvalho Chehab <mchehab@kernel.org>,
Christian Brauner <brauner@kernel.org>,
Alexander Viro <viro@zeniv.linux.org.uk>,
Andrew Morton <akpm@linux-foundation.org>,
Masahiro Yamada <masahiroy@kernel.org>,
Shuah Khan <skhan@linuxfoundation.org>,
Arnd Bergmann <arnd@arndb.de>,
Nathan Chancellor <nathan@kernel.org>,
Steven Rostedt <rostedt@goodmis.org>,
Masami Hiramatsu <mhiramat@kernel.org>,
Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Subject: [PATCH v5 01/11] kernel/api: introduce kernel API specification framework
Date: Thu, 8 Oct 2026 04:49:41 -0400 [thread overview]
Message-ID: <20261008084956.2911790-2-sashal@kernel.org> (raw)
In-Reply-To: <20261008084956.2911790-1-sashal@kernel.org>
Add a 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)
- Support for both internal kernel APIs and system calls
The framework stores specifications in a dedicated ELF section and
provides infrastructure for:
- Runtime querying of API documentation
- Integration with existing SYSCALL_DEFINE macros
A KUnit suite (CONFIG_KAPI_KUNIT_TEST) covers registration, lookup,
parameter and return value validation, and JSON export.
No specifications are added here; subsequent patches add them for
individual syscalls. With CONFIG_KAPI_SPEC=n the SYSCALL_DEFINEx()
expansion is unchanged. CONFIG_KAPI_RUNTIME_CHECKS is a debug option:
a syscall whose arguments violate its specification returns -EINVAL
without running.
Assisted-by: LLM
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
Documentation/dev-tools/index.rst | 1 +
Documentation/dev-tools/kernel-api-spec.rst | 615 ++++++++
MAINTAINERS | 11 +
arch/x86/include/asm/syscall_wrapper.h | 3 +-
include/asm-generic/vmlinux.lds.h | 14 +
include/linux/kapi_syscall.h | 35 +
include/linux/kernel_api_spec.h | 1226 ++++++++++++++++
include/linux/syscalls.h | 42 +-
init/Kconfig | 2 +
kernel/Makefile | 1 +
kernel/api/Kconfig | 54 +
kernel/api/Makefile | 10 +
kernel/api/internal.h | 25 +
kernel/api/kapi_kunit.c | 720 ++++++++++
kernel/api/kernel_api_spec.c | 1415 +++++++++++++++++++
15 files changed, 4172 insertions(+), 2 deletions(-)
create mode 100644 Documentation/dev-tools/kernel-api-spec.rst
create mode 100644 include/linux/kapi_syscall.h
create mode 100644 include/linux/kernel_api_spec.h
create mode 100644 kernel/api/Kconfig
create mode 100644 kernel/api/Makefile
create mode 100644 kernel/api/internal.h
create mode 100644 kernel/api/kapi_kunit.c
create mode 100644 kernel/api/kernel_api_spec.c
diff --git a/Documentation/dev-tools/index.rst b/Documentation/dev-tools/index.rst
index 59cbb77b33ff4..8d3768645d96c 100644
--- a/Documentation/dev-tools/index.rst
+++ b/Documentation/dev-tools/index.rst
@@ -36,6 +36,7 @@ Documentation/process/debugging/index.rst
kunit/index
ktap
checkuapi
+ kernel-api-spec
gpio-sloppy-logic-analyzer
autofdo
propeller
diff --git a/Documentation/dev-tools/kernel-api-spec.rst b/Documentation/dev-tools/kernel-api-spec.rst
new file mode 100644
index 0000000000000..4fb412b2720d3
--- /dev/null
+++ b/Documentation/dev-tools/kernel-api-spec.rst
@@ -0,0 +1,615 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================================
+Kernel API Specification Framework
+======================================
+
+:Author: Sasha Levin <sashal@kernel.org>
+
+.. contents:: Table of Contents
+ :depth: 3
+ :local:
+
+Introduction
+============
+
+The Kernel API Specification Framework (KAPI) describes kernel APIs in a
+machine-readable form. The descriptions are written as kerneldoc annotations
+next to the implementation and compiled into the kernel. They can be used to
+check system call arguments at runtime and can be read back through debugfs or
+the ``kapi`` tool.
+
+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. **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/``
+ - Per-API detailed specification output
+ - List of all registered API specifications
+
+3. **kapi Tool** (``tools/kapi/``)
+
+ - Userspace utility for extracting specifications
+ - Multiple input sources (source, binary, debugfs)
+ - Multiple output formats (plain, JSON, RST)
+ - Testing and validation utilities
+
+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)
+ │
+ ├── Parameters (up to 16)
+ │ └── kapi_param_spec
+ │ ├── name
+ │ ├── type (int, pointer, fd, path, etc.)
+ │ ├── flags (in, out, inout, optional, etc.)
+ │ ├── constraints (range, mask, enum values)
+ │ └── description
+ │
+ ├── Return Value
+ │ └── kapi_return_spec
+ │ ├── type
+ │ ├── success conditions
+ │ └── validation rules
+ │
+ ├── Error Conditions (up to 32)
+ │ └── kapi_error_spec
+ │ ├── error code
+ │ ├── name
+ │ ├── condition
+ │ └── description
+ │
+ ├── 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
+-----------------------
+
+API specifications are written as KAPI-annotated kerneldoc comments directly in
+the source file, immediately preceding the function implementation. With
+``CONFIG_KAPI_SPEC`` enabled, Kbuild runs ``kernel-doc -apispec`` on each
+built-in C file that has a ``contexts:`` (or ``context-flags:``) line plus at
+least one of ``api-type:``, ``param:``, ``error:``, ``capability:``,
+``signal:``, ``lock:``, ``state-trans:``, ``constraint:``, ``side-effect:`` or
+``long-desc:``, and compiles the generated header into that file. The ``kapi``
+tool reads the same annotations, or the specifications in a built kernel, for
+use outside the kernel build.
+
+The following is an excerpt of the ``sys_read`` specification in
+``fs/read_write.c``, trimmed for length:
+
+.. code-block:: c
+
+ /**
+ * sys_read - Read data from a file descriptor
+ * @fd: File descriptor to read from
+ * @buf: User-space buffer to read data into
+ * @count: Maximum number of bytes to read
+ *
+ * long-desc: Attempts to read up to count bytes from file descriptor fd into
+ * the buffer starting at buf. ...
+ *
+ * contexts: process, sleepable
+ *
+ * param: fd
+ * type: fd, input
+ * constraint-type: range(0, INT_MAX)
+ *
+ * param: buf
+ * type: user_ptr, output
+ * constraint-type: buffer(2)
+ *
+ * param: count
+ * type: uint, input
+ *
+ * return:
+ * type: int
+ * check-type: range
+ * success: >= 0
+ * desc: On success, returns the number of bytes read (non-negative). ...
+ *
+ * error: EBADF, Bad file descriptor
+ * desc: fd is not a valid file descriptor, or fd was not opened for
+ * reading. ...
+ */
+ SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
+
+DSL reference:
+
+* ``contexts:`` — comma-separated list of call contexts. Accepted tokens:
+ ``process``, ``softirq``, ``hardirq``, ``nmi``, ``atomic``, ``sleepable``,
+ ``preempt_disabled``, ``irq_disabled``. ``context-flags:`` with
+ ``|``-joined ``KAPI_CTX_*`` constants is equivalent.
+* ``type:`` — parameter type plus direction/qualifier flags on a single
+ line. Type aliases (case-insensitive): ``int``, ``uint``, ``ptr``,
+ ``fd``, ``path``, ``user_ptr`` (or ``uptr``), ``struct``, ``union``,
+ ``enum``, ``func_ptr``, ``array``, ``custom``. Flag aliases:
+ ``input``, ``output``, ``inout``, ``user``, ``optional``, ``const``,
+ ``volatile``, ``dma``, ``aligned``.
+* ``constraint-type:`` — a ``KAPI_CONSTRAINT_*`` enum token or a
+ function-call expression. ``range(lo, hi)``, ``mask(expr)``,
+ ``enum(v1, v2, …)``, ``buffer(size_param_idx)``, ``alignment(N)``,
+ ``user_string``, ``user_path``, ``user_ptr``, ``power_of_two``,
+ ``page_aligned``, ``nonzero``. ``user_string`` takes its length limits
+ from ``range:``. The function-call
+ form populates the matching aux fields
+ (``range:`` / ``valid-mask:`` / ``size-param:``).
+* ``lock: … type:`` accepts ``mutex``, ``spinlock``, ``rwlock``,
+ ``seqlock``, ``rcu``, ``semaphore``, ``custom`` or ``KAPI_LOCK_*``.
+* ``signal: … direction:`` accepts ``receive``, ``send``, ``handle``,
+ ``block``, ``ignore`` (bitmask, joinable with ``|`` or ``,``).
+* ``signal: … action:`` accepts ``default``, ``terminate``, ``coredump``,
+ ``stop``, ``continue``, ``custom``, ``return``, ``restart``,
+ ``queue``, ``discard``, ``transform``.
+* ``signal: … timing:`` accepts ``before``, ``during``, ``after``.
+* ``capability: … type:`` accepts ``bypass_check``, ``increase_limit``,
+ ``override_restriction``, ``grant_permission``, ``modify_behavior``,
+ ``access_resource``, ``perform_operation``.
+* ``side-effect:`` accepts the snake_case effect names
+ (``alloc_memory``, ``free_memory``, ``modify_state``, ``signal_send``,
+ ``file_position``, ``lock_acquire``, ``lock_release``,
+ ``resource_create``, ``resource_destroy``, ``schedule``, ``hardware``,
+ ``network``, ``filesystem``, ``process_state``, ``irreversible``)
+ joined with ``|`` — for example ``side-effect: resource_create | alloc_memory``.
+* ``return: … type:`` reuses the ``type:`` aliases above.
+* ``return: … check-type:`` accepts ``exact``, ``range``,
+ ``fd``, ``no_return``. ``success:``
+ gives the success value for ``exact`` (an integer, optionally
+ written ``= N``) and the lower bound for ``range`` (``>= N``); the
+ other check types do not use it. The ``type:`` of a return block
+ is also kept as written (for example ``int``) in the
+ human-readable ``type_name`` of ``struct kapi_return_spec``.
+* ``error:`` takes a ``NAME, one-line summary`` header followed
+ by optional indented ``desc:`` / ``condition:`` subfields.
+* ``lock:`` and ``signal:`` take an indented ``desc:`` subfield
+ for the long-form description; ``signal:`` also accepts
+ ``number:`` (the signal constant, for example ``SIGPIPE``),
+ ``errno:``, ``priority:``, ``restartable:``, ``interruptible:``,
+ and ``queue:`` subfields.
+* ``state-trans:`` takes ``from:``, ``to:``, ``object:``,
+ optional ``condition:``, and ``desc:`` subfields. The condition is
+ stored in its own field, apart from the description.
+* ``long-desc:`` is a free-form multi-line prose block that
+ populates ``long_description`` in the spec. ``notes:`` is a
+ free-form block of the same kind. In both, a blank line starts a
+ new paragraph, a line starting with ``- `` stays on its own line so
+ bullet lists survive, and any other wrapped line is joined to the
+ previous one with a space. ``examples:`` keeps one example per
+ line, preserving the relative indentation of nested code. The
+ line breaks are stored as ``\n`` in the generated strings, which
+ have no length limit.
+* A subfield line inside a block starts with one of the subfield
+ names of that block followed by ``:``. Every other line, even one
+ with a colon in the middle of a sentence, continues the previous
+ subfield.
+* ``param-count:`` is optional; the parser counts ``param:`` blocks and
+ warns when an explicit count disagrees.
+
+System Call Specification
+-------------------------
+
+System calls are documented inline in the implementation file (e.g., ``fs/open.c``)
+using KAPI-annotated kerneldoc comments. When ``CONFIG_KAPI_RUNTIME_CHECKS`` is
+enabled, the ``SYSCALL_DEFINEx`` macros automatically look up the specification
+and validate parameters before and after the syscall executes.
+
+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
+
+Validation Behavior
+-------------------
+
+When ``CONFIG_KAPI_RUNTIME_CHECKS`` is enabled, every system call that has a
+specification is validated in its ``SYSCALL_DEFINEx()`` wrapper: the arguments
+are checked against the parameter constraints before the handler runs, and the
+return value is checked against the return specification afterwards. Violations
+are reported via ``pr_warn_ratelimited`` to avoid flooding the kernel log. On
+the return side only a successful ``fd`` return that is not a valid file
+descriptor is reported. Any other value that does not satisfy the success check
+is treated as an error and accepted, and error codes that the specification
+does not list are only logged at debug level.
+The execution context recorded in a specification is not checked at runtime.
+The option is available on x86 and on architectures that use the generic
+``__SYSCALL_DEFINEx()``.
+
+Custom Validators
+-----------------
+
+``KAPI_CONSTRAINT_CUSTOM`` calls the ``validate`` function of the parameter
+specification. Kerneldoc annotations cannot set it, so it is only available to a
+``struct kapi_param_spec`` that is filled in by hand:
+
+.. code-block:: c
+
+ static bool validate_buffer_size(s64 value)
+ {
+ size_t size = (size_t)value;
+
+ return size > 0 && size <= MAX_BUFFER_SIZE;
+ }
+
+ /* In the parameter definition: */
+ .constraint_type = KAPI_CONSTRAINT_CUSTOM,
+ .validate = validate_buffer_size,
+
+Performance Considerations
+==========================
+
+Memory Overhead
+---------------
+
+Each compiled spec is 26400 bytes (``readelf -sW vmlinux | grep
+__kapi_spec_``), dominated by the fixed-bound arrays
+``struct_specs[8]`` (11584 bytes), ``signal_masks[32]`` (4864),
+``signals[32]`` (3328) and ``params[16]`` (1920). With
+the five syscall specs in this series, ``.kapi_specs`` and the backing
+``.rodata`` objects occupy ~132 KB. Building with ``CONFIG_KAPI_SPEC=n``
+emits no code or data from the framework.
+
+Runtime Overhead
+----------------
+
+When ``CONFIG_KAPI_RUNTIME_CHECKS`` is enabled, each validated
+call pays for a parameter walk plus the per-constraint check
+(range/mask/enum/align/user-ptr/user-path/user-string).
+The cost depends on the parameter count and the constraints involved;
+profile before enabling on workloads where syscall latency matters.
+``CONFIG_KAPI_RUNTIME_CHECKS=n`` compiles the validators away
+entirely.
+
+The kapi Tool
+=============
+
+Overview
+--------
+
+The ``kapi`` tool is a userspace utility that extracts and displays kernel API
+specifications from multiple sources. It provides a unified interface to access
+API documentation whether from compiled kernels, source code, or runtime systems.
+
+Installation
+------------
+
+Build the tool from the kernel source tree::
+
+ $ cd tools/kapi
+ $ cargo build --release
+
+ # Optional: Install system-wide
+ $ cargo install --path .
+
+The tool requires Rust and Cargo to build. The binary will be available at
+``tools/kapi/target/release/kapi``.
+
+Command-Line Usage
+------------------
+
+Basic syntax::
+
+ kapi [OPTIONS] [API_NAME]
+
+Options:
+
+- ``--vmlinux <PATH>``: Extract from compiled kernel binary
+- ``--source <PATH>``: Extract from kernel source code
+- ``--debugfs <PATH>``: Extract from debugfs (default: /sys/kernel/debug)
+- ``-f, --format <FORMAT>``: Output format (plain, json, rst)
+- ``-h, --help``: Display help information
+- ``-V, --version``: Display version information
+
+Input Modes
+-----------
+
+**1. Source Code Mode**
+
+Extract specifications directly from kernel source::
+
+ # Scan entire kernel source tree
+ $ kapi --source /path/to/linux
+
+ # Extract from specific file
+ $ kapi --source fs/open.c
+
+ # Get details for specific API
+ $ kapi --source /path/to/linux sys_close
+
+**2. Vmlinux Mode**
+
+Extract from compiled kernel with debug symbols::
+
+ # List all APIs in vmlinux
+ $ kapi --vmlinux ./vmlinux
+
+ # Get specific syscall details
+ $ kapi --vmlinux ./vmlinux sys_read
+
+**3. Debugfs Mode**
+
+Extract from running kernel via debugfs::
+
+ # Use default debugfs path
+ $ kapi
+
+ # Use custom debugfs mount
+ $ kapi --debugfs /mnt/debugfs
+
+ # Get specific API from running kernel
+ $ kapi sys_write
+
+Output Formats
+--------------
+
+The samples below are shortened; ``...`` marks omitted output.
+
+**Plain Text Format** (default)::
+
+ $ kapi --source . sys_read
+
+ Detailed information for sys_read:
+ ==================================
+ Description: Read data from a file descriptor
+
+ Detailed Description:
+ Attempts to read up to count bytes from file descriptor fd into the buffer starting at buf. ...
+
+ Execution Context:
+ - KAPI_CTX_PROCESS
+ - KAPI_CTX_SLEEPABLE
+
+ Parameters (3):
+ [0] fd (unsigned int fd)
+ File descriptor to read from
+ Flags: IN
+ ...
+
+**JSON Format**::
+
+ $ kapi --source . --format json sys_read
+ {
+ "api_details": {
+ "name": "sys_read",
+ "description": "Read data from a file descriptor",
+ "long_description": "Attempts to read up to count bytes from file descriptor fd into the buffer starting at buf. ...",
+ "context_flags": [
+ "KAPI_CTX_PROCESS",
+ "KAPI_CTX_SLEEPABLE"
+ ],
+ ...
+ }
+ }
+
+**ReStructuredText Format**::
+
+ $ kapi --source . --format rst sys_read
+
+ sys_read
+ ========
+
+ **Read data from a file descriptor**
+
+ Attempts to read up to count bytes from file descriptor fd into the buffer starting at buf. ...
+
+Usage Examples
+--------------
+
+**Generate complete API documentation**::
+
+ # Export all kernel APIs to JSON
+ $ kapi --source /path/to/linux --format json > kernel-apis.json
+
+ # Generate RST documentation for all syscalls
+ $ kapi --vmlinux ./vmlinux --format rst > syscalls.rst
+
+ # List APIs from specific subsystem
+ $ kapi --source fs/
+
+**Integration with other tools**::
+
+ # List the names of all APIs
+ $ kapi --format json | jq -r '.apis[].name'
+
+ # Generate markdown documentation
+ $ kapi --format rst sys_madvise | pandoc -f rst -t markdown
+
+**Debugging and analysis**::
+
+ # Check if specific API exists
+ $ kapi --source . my_custom_api || echo "API not found"
+
+Implementation Details
+----------------------
+
+The tool extracts API specifications from three sources:
+
+1. **Source Code**: Parses KAPI-annotated kerneldoc comments in C files, using
+ the same selection rule as Kbuild; regular expressions only locate the
+ ``SYSCALL_DEFINEx()`` or function that follows each comment
+2. **Vmlinux**: Reads the ``.kapi_specs`` ELF section from compiled kernels
+3. **Debugfs**: Reads from ``/sys/kernel/debug/kapi/`` filesystem interface
+
+The tool supports all KAPI specification types:
+
+- System calls (kerneldoc annotations)
+- Kernel functions (kerneldoc annotations with KAPI tags)
+
+IDE Integration
+---------------
+
+Modern IDEs can use the specification data for:
+
+- Parameter hints
+- Type checking
+- Context validation
+- Error code documentation
+
+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**:
+
+.. code-block:: c
+
+ /**
+ * @optional_arg: Optional argument (may be NULL)
+ *
+ * param: optional_arg
+ * type: KAPI_TYPE_PTR
+ * flags: KAPI_PARAM_IN | KAPI_PARAM_OPTIONAL
+ */
+
+**Buffer with Size Parameter**:
+
+.. code-block:: c
+
+ /**
+ * @buf: User-space buffer
+ *
+ * param: buf
+ * type: KAPI_TYPE_USER_PTR
+ * flags: KAPI_PARAM_OUT | KAPI_PARAM_USER
+ * constraint-type: KAPI_CONSTRAINT_BUFFER
+ * size-param: 2
+ */
+
+**Callback Functions**:
+
+.. code-block:: c
+
+ /**
+ * @callback: Callback function
+ *
+ * param: callback
+ * type: KAPI_TYPE_FUNC_PTR
+ * flags: KAPI_PARAM_IN
+ */
+
+Troubleshooting
+===============
+
+Common Issues
+-------------
+
+**Specification Not Found**
+
+A syscall that is missing from ``/sys/kernel/debug/kapi/list`` has no
+specification. Ensure the KAPI-annotated kerneldoc comment is in the same
+translation unit as the function implementation, is named ``sys_<name>`` for
+``SYSCALL_DEFINEx(<name>, ...)``, and has a ``contexts:`` line plus one more
+KAPI section as described above.
+
+**Validation Failures**::
+
+ kapi: Parameter fd: invalid file descriptor -1
+
+ Solution: Check parameter constraints or adjust specification if
+ the constraint is incorrect.
+
+Debug Options
+-------------
+
+Violations are logged with ``pr_warn_ratelimited()``. Error codes that a
+specification does not list are logged with ``pr_debug()``; with
+``CONFIG_DYNAMIC_DEBUG`` they can be enabled with::
+
+ echo 'file kernel_api_spec.c +p' > /sys/kernel/debug/dynamic_debug/control
+
+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. 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 65e8a4b5c90b1..cd15c6a776ae3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14118,6 +14118,17 @@ 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/dev-tools/kernel-api-spec.rst
+F: include/linux/kapi_syscall.h
+F: include/linux/kernel_api_spec.h
+F: kernel/api/
+F: tools/kapi/
+F: tools/lib/python/kdoc/kdoc_apispec.py
+
KERNEL AUTOMOUNTER
M: Ian Kent <raven@themaw.net>
L: autofs@vger.kernel.org
diff --git a/arch/x86/include/asm/syscall_wrapper.h b/arch/x86/include/asm/syscall_wrapper.h
index 7e88705e907f4..54b70c2cac8ac 100644
--- a/arch/x86/include/asm/syscall_wrapper.h
+++ b/arch/x86/include/asm/syscall_wrapper.h
@@ -223,11 +223,12 @@ extern long __ia32_sys_ni_syscall(const struct pt_regs *regs);
#define __SYSCALL_DEFINEx(x, name, ...) \
static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \
static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\
+ __KAPI_SYSCALL_DEFINEx(x, name, __VA_ARGS__) \
__X64_SYS_STUBx(x, name, __VA_ARGS__) \
__IA32_SYS_STUBx(x, name, __VA_ARGS__) \
static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \
{ \
- long ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\
+ long ret = __KAPI_DO_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; \
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index b2988aa12f664..b754fa182a6a3 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -296,6 +296,19 @@
#define TRACE_SYSCALLS()
#endif
+#ifdef CONFIG_KAPI_SPEC
+/*
+ * .kapi_specs is an array of pointers (see DEFINE_KERNEL_API_SPEC()). Align
+ * __start_kapi_specs to at least pointer alignment so that no padding
+ * separates it from the first entry.
+ */
+#define KAPI_SPECS() \
+ . = ALIGN(8); \
+ BOUNDED_SECTION_BY(.kapi_specs, _kapi_specs)
+#else
+#define KAPI_SPECS()
+#endif
+
#ifdef CONFIG_BPF_EVENTS
#define BPF_RAW_TP() STRUCT_ALIGN(); \
BOUNDED_SECTION_BY(__bpf_raw_tp_map, __bpf_raw_tp)
@@ -485,6 +498,7 @@
. = ALIGN(8); \
BOUNDED_SECTION_BY(__tracepoints_ptrs, ___tracepoints_ptrs) \
*(__tracepoints_strings)/* Tracepoints: strings */ \
+ KAPI_SPECS() \
} \
\
.rodata1 : AT(ADDR(.rodata1) - LOAD_OFFSET) { \
diff --git a/include/linux/kapi_syscall.h b/include/linux/kapi_syscall.h
new file mode 100644
index 0000000000000..bc9387e0e052c
--- /dev/null
+++ b/include/linux/kapi_syscall.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
+ *
+ * Spec lookup and syscall validation entry points of the kernel API
+ * specification framework, kept apart from <linux/kernel_api_spec.h> so that
+ * <linux/syscalls.h> can declare them cheaply.
+ */
+
+#ifndef _LINUX_KAPI_SYSCALL_H
+#define _LINUX_KAPI_SYSCALL_H
+
+#include <linux/types.h>
+
+struct kernel_api_spec;
+
+const struct kernel_api_spec *kapi_get_spec(const char *name);
+
+#ifdef CONFIG_KAPI_RUNTIME_CHECKS
+int kapi_validate_syscall_params(const struct kernel_api_spec *spec,
+ const s64 *params, int param_count);
+int kapi_validate_syscall_return(const struct kernel_api_spec *spec, s64 retval);
+#else
+static inline int kapi_validate_syscall_params(const struct kernel_api_spec *spec,
+ const s64 *params, int param_count)
+{
+ return 0;
+}
+static inline int kapi_validate_syscall_return(const struct kernel_api_spec *spec, s64 retval)
+{
+ return 0;
+}
+#endif
+
+#endif /* _LINUX_KAPI_SYSCALL_H */
diff --git a/include/linux/kernel_api_spec.h b/include/linux/kernel_api_spec.h
new file mode 100644
index 0000000000000..f72ef5cd73b38
--- /dev/null
+++ b/include/linux/kernel_api_spec.h
@@ -0,0 +1,1226 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
+ *
+ * kernel_api_spec.h - Kernel API Specification Framework
+ *
+ * Structures and macros for specifying kernel APIs in a human and
+ * machine-readable form: parameters, return values, error conditions,
+ * and constraints.
+ */
+
+#ifndef _LINUX_KERNEL_API_SPEC_H
+#define _LINUX_KERNEL_API_SPEC_H
+
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <linux/kapi_syscall.h>
+#include <linux/kernel.h>
+#include <linux/stringify.h>
+#include <linux/types.h>
+
+struct sigaction;
+
+#define KAPI_MAX_PARAMS 16
+#define KAPI_MAX_ERRORS 32
+#define KAPI_MAX_CONSTRAINTS 32
+#define KAPI_MAX_LOCKS 16
+#define KAPI_MAX_SIGNALS 32
+#define KAPI_MAX_NAME_LEN 128
+#define KAPI_MAX_DESC_LEN 512
+#define KAPI_MAX_CAPABILITIES 8
+
+/* Magic numbers for section validation (ASCII mnemonics) */
+#define KAPI_MAGIC_PARAMS 0x4B415031 /* 'KAP1' */
+#define KAPI_MAGIC_RETURN 0x4B415232 /* 'KAR2' */
+#define KAPI_MAGIC_ERRORS 0x4B414533 /* 'KAE3' */
+#define KAPI_MAGIC_LOCKS 0x4B414C34 /* 'KAL4' */
+#define KAPI_MAGIC_CONSTRAINTS 0x4B414335 /* 'KAC5' */
+#define KAPI_MAGIC_INFO 0x4B414936 /* 'KAI6' */
+#define KAPI_MAGIC_SIGNALS 0x4B415337 /* 'KAS7' */
+#define KAPI_MAGIC_SIGMASK 0x4B414D38 /* 'KAM8' */
+#define KAPI_MAGIC_STRUCTS 0x4B415439 /* 'KAT9' */
+#define KAPI_MAGIC_EFFECTS 0x4B414641 /* 'KAFA' */
+#define KAPI_MAGIC_TRANS 0x4B415442 /* 'KATB' */
+#define KAPI_MAGIC_CAPS 0x4B414343 /* 'KACC' */
+
+/**
+ * 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 - range-checked only
+ * @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 - range-checked only */
+ 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 = (KAPI_PARAM_IN | KAPI_PARAM_OUT),
+ 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_ALIGNMENT: Alignment constraint (must be aligned to specified boundary)
+ * @KAPI_CONSTRAINT_POWER_OF_TWO: Value must be a power of two
+ * @KAPI_CONSTRAINT_PAGE_ALIGNED: Value must be page-aligned
+ * @KAPI_CONSTRAINT_NONZERO: Value must be non-zero
+ * @KAPI_CONSTRAINT_USER_STRING: Userspace null-terminated string with length range
+ * @KAPI_CONSTRAINT_USER_PATH: Userspace pathname string (validated for accessibility and PATH_MAX)
+ * @KAPI_CONSTRAINT_USER_PTR: Userspace pointer (validated for accessibility and size)
+ * @KAPI_CONSTRAINT_BUFFER: Userspace buffer pointer (validated by copy_to/from_user)
+ * @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_ALIGNMENT,
+ KAPI_CONSTRAINT_POWER_OF_TWO,
+ KAPI_CONSTRAINT_PAGE_ALIGNED,
+ KAPI_CONSTRAINT_NONZERO,
+ KAPI_CONSTRAINT_USER_STRING,
+ KAPI_CONSTRAINT_USER_PATH,
+ KAPI_CONSTRAINT_USER_PTR,
+ KAPI_CONSTRAINT_BUFFER,
+ 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: 1-based index of the parameter that determines size,
+ * or 0 if this parameter has a fixed size
+ * @size_multiplier: Multiplier for size calculation (e.g., sizeof(struct))
+ */
+struct kapi_param_spec {
+ const char *name;
+ const char *type_name;
+ 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);
+ const char *description;
+ const char *constraints;
+ int size_param_idx; /* 1-based param index for dynamic size; 0 if N/A */
+ size_t size_multiplier; /* Size per unit (e.g., sizeof(struct epoll_event)) */
+};
+
+/**
+ * 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;
+ const char *name;
+ const char *condition;
+ const char *description;
+};
+
+/**
+ * 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
+ * @KAPI_RETURN_NO_RETURN: Function does not return (e.g., exec on success)
+ */
+enum kapi_return_check_type {
+ KAPI_RETURN_EXACT,
+ KAPI_RETURN_RANGE,
+ KAPI_RETURN_ERROR_CHECK,
+ KAPI_RETURN_FD,
+ KAPI_RETURN_CUSTOM,
+ KAPI_RETURN_NO_RETURN,
+};
+
+/**
+ * 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 {
+ const char *type_name;
+ 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);
+ const char *description;
+};
+
+/**
+ * enum kapi_lock_scope - Lock acquisition/release scope
+ * @KAPI_LOCK_INTERNAL: Lock is acquired and released within the function (common case)
+ * @KAPI_LOCK_ACQUIRES: Function acquires lock but does not release it
+ * @KAPI_LOCK_RELEASES: Function releases lock (must be held on entry)
+ * @KAPI_LOCK_CALLER_HELD: Lock must be held by caller throughout the call
+ */
+enum kapi_lock_scope {
+ KAPI_LOCK_INTERNAL = 0,
+ KAPI_LOCK_ACQUIRES,
+ KAPI_LOCK_RELEASES,
+ KAPI_LOCK_CALLER_HELD,
+};
+
+/**
+ * struct kapi_lock_spec - Lock requirement specification
+ * @lock_name: Name of the lock
+ * @lock_type: Type of lock
+ * @scope: Lock scope (internal, acquires, releases, or caller-held)
+ * @description: Additional lock requirements
+ */
+struct kapi_lock_spec {
+ const char *lock_name;
+ enum kapi_lock_type lock_type;
+ enum kapi_lock_scope scope;
+ const char *description;
+};
+
+/**
+ * struct kapi_constraint_spec - Additional constraint specification
+ * @name: Constraint name
+ * @description: Constraint description
+ * @expression: Formal expression (if applicable)
+ */
+struct kapi_constraint_spec {
+ const char *name;
+ const char *description;
+ const char *expression;
+};
+
+/**
+ * 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;
+ const char *signal_name;
+ u32 direction;
+ enum kapi_signal_action action;
+ const char *target;
+ const char *condition;
+ const char *description;
+ bool restartable;
+ u32 sa_flags_required;
+ u32 sa_flags_forbidden;
+ int error_on_signal;
+ int transform_to;
+ const char *timing;
+ u8 priority;
+ bool interruptible;
+ const char *queue_behavior;
+ u32 state_required;
+ u32 state_forbidden;
+};
+
+/**
+ * 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 {
+ const char *mask_name;
+ int signals[KAPI_MAX_SIGNALS];
+ u32 signal_count;
+ const char *description;
+};
+
+/**
+ * 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)
+ * @enum_values: Comma-separated list of valid enum values (for enum types)
+ * @description: Field description
+ */
+struct kapi_struct_field {
+ const char *name;
+ enum kapi_param_type type;
+ const char *type_name;
+ size_t offset;
+ size_t size;
+ u32 flags;
+ enum kapi_constraint_type constraint_type;
+ s64 min_value;
+ s64 max_value;
+ u64 valid_mask;
+ const char *enum_values; /* Comma-separated list of valid enum values */
+ const char *description;
+};
+
+/**
+ * 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 {
+ const char *name;
+ size_t size;
+ size_t alignment;
+ u32 field_count;
+ struct kapi_struct_field fields[KAPI_MAX_PARAMS];
+ const char *description;
+};
+
+/**
+ * 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;
+ const char *cap_name;
+ enum kapi_capability_action action;
+ const char *allows;
+ const char *without_cap;
+ const char *check_condition;
+ u8 priority;
+ int alternative[KAPI_MAX_CAPABILITIES];
+ u32 alternative_count;
+};
+
+/**
+ * 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
+ * @KAPI_EFFECT_IRREVERSIBLE: Effect cannot be undone
+ */
+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),
+ KAPI_EFFECT_IRREVERSIBLE = (1 << 14),
+};
+
+/**
+ * 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;
+ const char *target;
+ const char *condition;
+ const char *description;
+ bool reversible;
+};
+
+/**
+ * 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 {
+ const char *from_state;
+ const char *to_state;
+ const char *condition;
+ const char *object;
+ const char *description;
+};
+
+#define KAPI_MAX_STRUCT_SPECS 8
+#define KAPI_MAX_SIDE_EFFECTS 32
+#define KAPI_MAX_STATE_TRANS 8
+
+/**
+ * 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
+ * @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
+ * @capability_count: Number of required capabilities
+ * @capabilities: Required capability specifications
+ * @param_magic: Magic value marking the start of the params array
+ * @return_magic: Magic value marking the return spec
+ * @error_magic: Magic value marking the start of the errors array
+ * @lock_magic: Magic value marking the start of the locks array
+ * @constraint_magic: Magic value marking the constraints array
+ * @info_magic: Magic value marking the info block (examples, notes)
+ * @signal_magic: Magic value marking the start of the signals array
+ * @sigmask_magic: Magic value marking the signal masks array
+ * @struct_magic: Magic value marking the struct specs array
+ * @effect_magic: Magic value marking the side effects array
+ * @trans_magic: Magic value marking the state transitions array
+ * @cap_magic: Magic value marking the capabilities array
+ */
+struct kernel_api_spec {
+ const char *name;
+ u32 version;
+ const char *description;
+ const char *long_description;
+ u32 context_flags;
+
+ /* Parameters */
+ u32 param_magic; /* 0x4B415031 = 'KAP1' */
+ u32 param_count;
+ struct kapi_param_spec params[KAPI_MAX_PARAMS];
+
+ /* Return value */
+ u32 return_magic; /* 0x4B415232 = 'KAR2' */
+ struct kapi_return_spec return_spec;
+
+ /* Errors */
+ u32 error_magic; /* 0x4B414533 = 'KAE3' */
+ u32 error_count;
+ struct kapi_error_spec errors[KAPI_MAX_ERRORS];
+
+ /* Locking */
+ u32 lock_magic; /* 0x4B414C34 = 'KAL4' */
+ u32 lock_count;
+ struct kapi_lock_spec locks[KAPI_MAX_LOCKS];
+
+ /* Constraints */
+ u32 constraint_magic; /* 0x4B414335 = 'KAC5' */
+ u32 constraint_count;
+ struct kapi_constraint_spec constraints[KAPI_MAX_CONSTRAINTS];
+
+ /* Additional information */
+ u32 info_magic; /* 0x4B414936 = 'KAI6' */
+ const char *examples;
+ const char *notes;
+
+ /* Signal specifications */
+ u32 signal_magic; /* 0x4B415337 = 'KAS7' */
+ u32 signal_count;
+ struct kapi_signal_spec signals[KAPI_MAX_SIGNALS];
+
+ /* Signal mask specifications */
+ u32 sigmask_magic; /* 0x4B414D38 = 'KAM8' */
+ u32 signal_mask_count;
+ struct kapi_signal_mask_spec signal_masks[KAPI_MAX_SIGNALS];
+
+ /* Structure specifications */
+ u32 struct_magic; /* 0x4B415439 = 'KAT9' */
+ u32 struct_spec_count;
+ struct kapi_struct_spec struct_specs[KAPI_MAX_STRUCT_SPECS];
+
+ /* Side effects */
+ u32 effect_magic; /* 0x4B414641 = 'KAFA' */
+ u32 side_effect_count;
+ struct kapi_side_effect side_effects[KAPI_MAX_SIDE_EFFECTS];
+
+ /* State transitions */
+ u32 trans_magic; /* 0x4B415442 = 'KATB' */
+ u32 state_trans_count;
+ struct kapi_state_transition state_transitions[KAPI_MAX_STATE_TRANS];
+
+ /* Capability specifications */
+ u32 cap_magic; /* 0x4B414343 = 'KACC' */
+ u32 capability_count;
+ struct kapi_capability_spec capabilities[KAPI_MAX_CAPABILITIES];
+};
+
+/* Macros for defining API specifications */
+
+/**
+ * DEFINE_KERNEL_API_SPEC - Define a kernel API specification
+ * @func_name: Function name to specify
+ *
+ * The ``.kapi_specs`` section holds an array of pointers to
+ * fully-defined ``kernel_api_spec`` instances, tightly packed so
+ * iteration ``for (pp = __start_kapi_specs; pp < __stop_kapi_specs;
+ * pp++)`` advances by one pointer each step regardless of the real
+ * spec struct size.
+ */
+#define DEFINE_KERNEL_API_SPEC(func_name) \
+ extern const struct kernel_api_spec __kapi_spec_##func_name; \
+ static const struct kernel_api_spec * const \
+ __kapi_spec_ptr_##func_name __used __section(".kapi_specs") = \
+ &__kapi_spec_##func_name; \
+ const struct kernel_api_spec __kapi_spec_##func_name = { \
+ .name = __stringify(func_name), \
+ .version = 1,
+
+/**
+ * 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,
+
+#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,
+
+/**
+ * KAPI_PARAM_ENUM_VALUES - Set the valid values of an enumerated parameter
+ * @...: Variadic list of valid values
+ */
+#define KAPI_PARAM_ENUM_VALUES(...) \
+ .enum_values = (const s64[]){ __VA_ARGS__ }, \
+ .enum_count = sizeof((const s64[]){ __VA_ARGS__ }) / sizeof(s64),
+
+#define KAPI_PARAM_ALIGNMENT(align) \
+ .alignment = align,
+
+/*
+ * Store the 1-based parameter index so the zero-initialised default
+ * (no dynamic sizing) remains distinguishable from "uses param 0".
+ */
+#define KAPI_PARAM_SIZE_PARAM(idx) \
+ .size_param_idx = (idx) + 1,
+
+/**
+ * KAPI_PARAM_COUNT - Set the number of parameters
+ * @n: Number of parameters
+ */
+#define KAPI_PARAM_COUNT(n) \
+ .param_magic = KAPI_MAGIC_PARAMS, \
+ .param_count = n,
+
+/**
+ * KAPI_RETURN - Define return value specification
+ * @rtype: Return type name
+ * @rdesc: Return value description
+ */
+#define KAPI_RETURN(rtype, rdesc) \
+ .return_magic = KAPI_MAGIC_RETURN, \
+ .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,
+
+/**
+ * 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_ERROR_COUNT - Set the number of errors
+ * @n: Number of errors
+ */
+#define KAPI_ERROR_COUNT(n) \
+ .error_magic = KAPI_MAGIC_ERRORS, \
+ .error_count = n,
+
+/**
+ * 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 \
+ .scope = KAPI_LOCK_ACQUIRES,
+
+#define KAPI_LOCK_RELEASED \
+ .scope = KAPI_LOCK_RELEASES,
+
+#define KAPI_LOCK_HELD_ENTRY \
+ .scope = KAPI_LOCK_CALLER_HELD,
+
+#define KAPI_LOCK_HELD_EXIT \
+ .scope = KAPI_LOCK_CALLER_HELD,
+
+#define KAPI_LOCK_DESC(ldesc) \
+ .description = ldesc,
+
+/**
+ * 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,
+
+/**
+ * KAPI_EXAMPLES - Set API usage examples
+ * @ex: Examples string
+ */
+#define KAPI_EXAMPLES(ex) \
+ .info_magic = KAPI_MAGIC_INFO, \
+ .examples = ex,
+
+/**
+ * KAPI_NOTES - Set API notes
+ * @n: 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_COUNT(n) \
+ .signal_magic = KAPI_MAGIC_SIGNALS, \
+ .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,
+
+/*
+ * KAPI_SIGNAL_MASK_SIGNALS - Specify signals in a signal mask
+ * @...: Variadic list of signal numbers
+ *
+ * Usage:
+ * KAPI_SIGNAL_MASK(0, "blocked", "Signals blocked during operation")
+ * KAPI_SIGNAL_MASK_SIGNALS(SIGINT, SIGTERM, SIGQUIT)
+ * },
+ */
+#define KAPI_SIGNAL_MASK_SIGNALS(...) \
+ .signals = { __VA_ARGS__ }, \
+ .signal_count = sizeof((int[]){ __VA_ARGS__ }) / sizeof(int),
+
+/**
+ * KAPI_SIGNAL_MASK_COUNT - Set the number of signal mask specifications
+ * @n: Number of signal masks
+ */
+#define KAPI_SIGNAL_MASK_COUNT(n) \
+ .sigmask_magic = KAPI_MAGIC_SIGMASK, \
+ .signal_mask_count = n,
+
+/**
+ * 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) \
+ .constraint_type = KAPI_CONSTRAINT_ENUM, \
+ .enum_values = values,
+
+/* Counter for structure specifications */
+#define KAPI_STRUCT_SPEC_COUNT(n) \
+ .struct_magic = KAPI_MAGIC_STRUCTS, \
+ .struct_spec_count = n,
+
+/* Additional lock-related macros */
+#define KAPI_LOCK_COUNT(n) \
+ .lock_magic = KAPI_MAGIC_LOCKS, \
+ .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,
+
+#define KAPI_EFFECT_CONDITION(cond) \
+ .condition = cond,
+
+#define KAPI_EFFECT_REVERSIBLE \
+ .reversible = true,
+
+/**
+ * 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,
+
+/* Counters for side effects and state transitions */
+#define KAPI_SIDE_EFFECT_COUNT(n) \
+ .effect_magic = KAPI_MAGIC_EFFECTS, \
+ .side_effect_count = n,
+
+#define KAPI_STATE_TRANS_COUNT(n) \
+ .trans_magic = KAPI_MAGIC_TRANS, \
+ .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)
+
+/* 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"
+#define KAPI_SIGNAL_TIME_BEFORE "before"
+#define KAPI_SIGNAL_TIME_AFTER "after"
+
+/* 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 BIT(0)
+#define KAPI_SIGNAL_STATE_SLEEPING BIT(1)
+#define KAPI_SIGNAL_STATE_STOPPED BIT(2)
+#define KAPI_SIGNAL_STATE_TRACED BIT(3)
+#define KAPI_SIGNAL_STATE_ZOMBIE BIT(4)
+#define KAPI_SIGNAL_STATE_DEAD BIT(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,
+
+/*
+ * KAPI_CAP_ALTERNATIVE - Capabilities that can be used instead
+ * @...: Variadic list of capability numbers
+ */
+#define KAPI_CAP_ALTERNATIVE(...) \
+ .alternative = { __VA_ARGS__ }, \
+ .alternative_count = sizeof((int[]){ __VA_ARGS__ }) / sizeof(int),
+
+/* Counter for capability specifications */
+#define KAPI_CAPABILITY_COUNT(n) \
+ .cap_magic = KAPI_MAGIC_CAPS, \
+ .capability_count = n,
+
+/* Validation and runtime checking */
+
+#ifdef CONFIG_KAPI_RUNTIME_CHECKS
+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);
+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);
+void kapi_check_context(const struct kernel_api_spec *spec);
+#else
+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 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 void kapi_check_context(const struct kernel_api_spec *spec) {}
+#endif
+
+/* Export/query functions */
+int kapi_export_json(const struct kernel_api_spec *spec, char *buf, size_t size);
+
+/* Registration for dynamic APIs */
+int kapi_register_spec(const struct kernel_api_spec *spec);
+void kapi_unregister_spec(const char *name);
+
+#define KAPI_CONSTRAINT_COUNT(n) \
+ .constraint_magic = KAPI_MAGIC_CONSTRAINTS, \
+ .constraint_count = n,
+
+#endif /* _LINUX_KERNEL_API_SPEC_H */
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 8413b624ad478..b062bac9ea1ed 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -94,6 +94,10 @@ struct file_attr;
#include <linux/personality.h>
#include <trace/syscall.h>
+#ifdef CONFIG_KAPI_RUNTIME_CHECKS
+#include <linux/kapi_syscall.h>
+#endif
+
#ifdef CONFIG_ARCH_HAS_SYSCALL_WRAPPER
/*
* It may be useful for an architecture to override the definitions of the
@@ -237,6 +241,41 @@ static inline int is_syscall_trace_event(struct trace_event_call *tp_event)
#define __PROTECT(...) asmlinkage_protect(__VA_ARGS__)
+#ifdef CONFIG_KAPI_RUNTIME_CHECKS
+/*
+ * Convert integers as (s64)(a) does, and pointers through unsigned long so
+ * they are not sign-extended on 32-bit. (unsigned long)(t)0 is an integer
+ * constant expression only if t is an integer type.
+ */
+#define __SC_CAST_TO_S64(t, a) \
+ ((__force s64)__builtin_choose_expr( \
+ __is_constexpr((__force unsigned long)(t)0), \
+ (a), (__force unsigned long)(a)))
+
+#define __KAPI_DO_SYS(name) __do_kapi_sys##name
+#define __KAPI_SYSCALL_DEFINEx(x, name, ...) \
+ static inline long __do_kapi_sys##name(__MAP(x, __SC_DECL, __VA_ARGS__)) \
+ { \
+ const struct kernel_api_spec *__spec = kapi_get_spec("sys" #name); \
+ long ret; \
+ \
+ 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; \
+ } \
+ ret = __do_sys##name(__MAP(x, __SC_ARGS, __VA_ARGS__)); \
+ if (__spec) \
+ kapi_validate_syscall_return(__spec, (s64)ret); \
+ return ret; \
+ }
+#else
+#define __KAPI_DO_SYS(name) __do_sys##name
+#define __KAPI_SYSCALL_DEFINEx(x, name, ...)
+#endif
+
/*
* The asmlinkage stub is aliased to a function named __se_sys_*() which
* sign-extends 32-bit ints to longs whenever needed. The actual work is
@@ -255,10 +294,11 @@ static inline int is_syscall_trace_event(struct trace_event_call *tp_event)
__attribute__((alias(__stringify(__se_sys##name)))); \
ALLOW_ERROR_INJECTION(sys##name, ERRNO); \
static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\
+ __KAPI_SYSCALL_DEFINEx(x, name, __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_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\
+ long ret = __KAPI_DO_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; \
diff --git a/init/Kconfig b/init/Kconfig
index ad592fdf29af4..66c23c4147498 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -2306,6 +2306,8 @@ source "kernel/Kconfig.kexec"
source "kernel/liveupdate/Kconfig"
+source "kernel/api/Kconfig"
+
endmenu # General setup
source "arch/Kconfig"
diff --git a/kernel/Makefile b/kernel/Makefile
index 1e1a31673577d..7fd43127d5eee 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -59,6 +59,7 @@ obj-y += dma/
obj-y += entry/
obj-y += unwind/
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..1cd55b252f0d5
--- /dev/null
+++ b/kernel/api/Kconfig
@@ -0,0 +1,54 @@
+# 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 JSON format through debugfs.
+
+ If unsure, say N.
+
+config KAPI_RUNTIME_CHECKS
+ bool "Runtime API specification checks"
+ depends on KAPI_SPEC
+ depends on DEBUG_KERNEL
+ # The hook is only in the generic and the x86 __SYSCALL_DEFINEx()
+ depends on X86 || !ARCH_HAS_SYSCALL_WRAPPER
+ help
+ Validate the arguments and the return value of system calls that
+ have an API specification, in their SYSCALL_DEFINEx() wrapper.
+ Violations are reported with pr_warn_ratelimited(). This adds
+ overhead to every system call.
+
+ DEBUG-ONLY: Enabling this changes the errno seen by userspace for
+ syscalls that violate their parameter specification. On violation
+ the validator short-circuits the syscall and returns -EINVAL
+ before the real handler runs, masking whatever errno the handler
+ would otherwise have produced. Do not enable on production
+ kernels.
+
+ If unsure, say N.
+
+config KAPI_KUNIT_TEST
+ tristate "KUnit tests for KAPI framework" if !KUNIT_ALL_TESTS
+ depends on KAPI_SPEC
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ KUnit tests for the Kernel API Specification Framework.
+ Tests registration, lookup, validation constraints, and
+ JSON export functionality.
+
+ If unsure, say N.
diff --git a/kernel/api/Makefile b/kernel/api/Makefile
new file mode 100644
index 0000000000000..6e14ca243980c
--- /dev/null
+++ b/kernel/api/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Kernel API Specification Framework
+#
+
+# Core API specification framework
+obj-y += kernel_api_spec.o
+
+# KUnit tests
+obj-$(CONFIG_KAPI_KUNIT_TEST) += kapi_kunit.o
diff --git a/kernel/api/internal.h b/kernel/api/internal.h
new file mode 100644
index 0000000000000..b6112bcc2e8d0
--- /dev/null
+++ b/kernel/api/internal.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
+ *
+ * Internal declarations shared by the KAPI core and its debugfs
+ * interface. Not part of the public kernel API.
+ */
+
+#ifndef _KERNEL_API_INTERNAL_H
+#define _KERNEL_API_INTERNAL_H
+
+#include <linux/kernel_api_spec.h>
+
+/*
+ * Section boundaries for the `.kapi_specs` array. Defined by the
+ * linker script in include/asm-generic/vmlinux.lds.h.
+ */
+extern const struct kernel_api_spec * const __start_kapi_specs[];
+extern const struct kernel_api_spec * const __stop_kapi_specs[];
+
+const char *kapi_param_type_to_string(enum kapi_param_type type);
+const char *kapi_lock_type_to_string(enum kapi_lock_type type);
+const char *kapi_lock_scope_to_string(enum kapi_lock_scope scope);
+
+#endif /* _KERNEL_API_INTERNAL_H */
diff --git a/kernel/api/kapi_kunit.c b/kernel/api/kapi_kunit.c
new file mode 100644
index 0000000000000..4f6f874c33b86
--- /dev/null
+++ b/kernel/api/kapi_kunit.c
@@ -0,0 +1,720 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
+ *
+ * KUnit tests for the Kernel API Specification Framework
+ *
+ * Tests registration, lookup, validation, and JSON export functionality.
+ */
+
+#include <kunit/test.h>
+#include <linux/kernel_api_spec.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/capability.h>
+#include <linux/mm.h>
+#include <linux/signal.h>
+#include <linux/uaccess.h>
+
+static void init_test_spec(struct kernel_api_spec *spec, const char *name)
+{
+ memset(spec, 0, sizeof(*spec));
+ spec->name = name;
+ spec->version = 1;
+ spec->description = "Test API";
+}
+
+/* kapi_register_spec with valid spec returns 0 */
+static void test_register_valid(struct kunit *test)
+{
+ struct kernel_api_spec *spec;
+ int ret;
+
+ spec = kzalloc_obj(*spec, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+
+ init_test_spec(spec, "test_register_valid");
+
+ ret = kapi_register_spec(spec);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ kapi_unregister_spec("test_register_valid");
+ kfree(spec);
+}
+
+/* kapi_get_spec returns registered spec */
+static void test_lookup_registered(struct kunit *test)
+{
+ struct kernel_api_spec *spec;
+ const struct kernel_api_spec *found;
+ int ret;
+
+ spec = kzalloc_obj(*spec, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+
+ init_test_spec(spec, "test_lookup_func");
+
+ ret = kapi_register_spec(spec);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ found = kapi_get_spec("test_lookup_func");
+ KUNIT_EXPECT_PTR_EQ(test, found, (const struct kernel_api_spec *)spec);
+
+ kapi_unregister_spec("test_lookup_func");
+ kfree(spec);
+}
+
+/* Double registration returns -EEXIST */
+static void test_double_register(struct kunit *test)
+{
+ struct kernel_api_spec *spec;
+ int ret;
+
+ spec = kzalloc_obj(*spec, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+
+ init_test_spec(spec, "test_double_reg");
+
+ ret = kapi_register_spec(spec);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = kapi_register_spec(spec);
+ KUNIT_EXPECT_EQ(test, ret, -EEXIST);
+
+ kapi_unregister_spec("test_double_reg");
+ kfree(spec);
+}
+
+/* Unregister makes spec unfindable */
+static void test_unregister(struct kunit *test)
+{
+ struct kernel_api_spec *spec;
+ const struct kernel_api_spec *found;
+ int ret;
+
+ spec = kzalloc_obj(*spec, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+
+ init_test_spec(spec, "test_unreg_func");
+
+ ret = kapi_register_spec(spec);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ kapi_unregister_spec("test_unreg_func");
+
+ found = kapi_get_spec("test_unreg_func");
+ KUNIT_EXPECT_NULL(test, found);
+
+ kfree(spec);
+}
+
+/* kapi_get_spec(NULL) returns NULL */
+static void test_get_spec_null(struct kunit *test)
+{
+ const struct kernel_api_spec *found;
+
+ found = kapi_get_spec(NULL);
+ KUNIT_EXPECT_NULL(test, found);
+}
+
+/* kapi_register_spec(NULL) returns -EINVAL */
+static void test_register_null(struct kunit *test)
+{
+ int ret;
+
+ ret = kapi_register_spec(NULL);
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+}
+
+/* Spec with a NULL name is rejected */
+static void test_register_null_name(struct kunit *test)
+{
+ struct kernel_api_spec *spec;
+ int ret;
+
+ spec = kzalloc_obj(*spec, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+
+ /* spec->name == NULL after zero-init; registration rejects it. */
+ ret = kapi_register_spec(spec);
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+
+ kfree(spec);
+}
+
+#ifdef CONFIG_KAPI_RUNTIME_CHECKS
+
+/* RANGE constraint - value in range is valid */
+static void test_constraint_range_valid(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+
+ param.name = "test_param";
+ param.constraint_type = KAPI_CONSTRAINT_RANGE;
+ param.min_value = 0;
+ param.max_value = 100;
+
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 0));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 50));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 100));
+
+ param.min_value = -10;
+ param.max_value = -1;
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, -10));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, -1));
+
+ /* An unsigned maximum such as U64_MAX reads as -1 */
+ param.min_value = 0;
+ param.max_value = (s64)U64_MAX;
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, S64_MAX));
+}
+
+/* RANGE constraint - value out of range is invalid */
+static void test_constraint_range_invalid(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+
+ param.name = "test_param";
+ param.constraint_type = KAPI_CONSTRAINT_RANGE;
+ param.min_value = 0;
+ param.max_value = 100;
+
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, -1));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 101));
+
+ param.min_value = -10;
+ param.max_value = -1;
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, -11));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 0));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 5));
+}
+
+/* MASK constraint - valid bits pass */
+static void test_constraint_mask_valid(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+
+ param.name = "test_flags";
+ param.constraint_type = KAPI_CONSTRAINT_MASK;
+ param.valid_mask = 0xFF;
+
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 0x00));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 0x0F));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 0xFF));
+}
+
+/* MASK constraint - extra bits fail */
+static void test_constraint_mask_invalid(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+
+ param.name = "test_flags";
+ param.constraint_type = KAPI_CONSTRAINT_MASK;
+ param.valid_mask = 0xFF;
+
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 0x100));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 0x1FF));
+}
+
+/* POWER_OF_TWO constraint */
+static void test_constraint_power_of_two(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+
+ param.name = "test_pot";
+ param.constraint_type = KAPI_CONSTRAINT_POWER_OF_TWO;
+
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 1));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 2));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 4));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 8));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 0));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 3));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 5));
+}
+
+/* PAGE_ALIGNED constraint */
+static void test_constraint_page_aligned(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+
+ param.name = "test_page";
+ param.constraint_type = KAPI_CONSTRAINT_PAGE_ALIGNED;
+
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 0));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, PAGE_SIZE));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 2 * PAGE_SIZE));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 1));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, PAGE_SIZE - 1));
+}
+
+/* NONZERO constraint */
+static void test_constraint_nonzero(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+
+ param.name = "test_nz";
+ param.constraint_type = KAPI_CONSTRAINT_NONZERO;
+
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 0));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 1));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, -1));
+}
+
+/* Return value validation - success */
+static void test_return_validation(struct kunit *test)
+{
+ struct kernel_api_spec *spec;
+
+ spec = kunit_kzalloc(test, sizeof(*spec), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+
+ spec->name = "test_ret";
+ spec->return_magic = KAPI_MAGIC_RETURN;
+ spec->return_spec.check_type = KAPI_RETURN_EXACT;
+ spec->return_spec.success_value = 0;
+
+ KUNIT_EXPECT_TRUE(test, kapi_validate_return_value(spec, 0));
+}
+
+/* Return value validation - known error */
+static void test_return_known_error(struct kunit *test)
+{
+ struct kernel_api_spec *spec;
+
+ spec = kunit_kzalloc(test, sizeof(*spec), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+
+ spec->name = "test_ret_err";
+ spec->return_magic = KAPI_MAGIC_RETURN;
+ spec->return_spec.check_type = KAPI_RETURN_FD;
+ spec->error_count = 1;
+ spec->errors[0].error_code = -ENOENT;
+ spec->errors[0].name = "ENOENT";
+
+ /* -ENOENT is in the error list, so it's valid */
+ KUNIT_EXPECT_TRUE(test, kapi_validate_return_value(spec, -ENOENT));
+}
+
+/* Return value validation - unknown error */
+static void test_return_unknown_error(struct kunit *test)
+{
+ struct kernel_api_spec *spec;
+
+ spec = kunit_kzalloc(test, sizeof(*spec), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+
+ spec->name = "test_ret_unk";
+ spec->return_magic = KAPI_MAGIC_RETURN;
+ spec->return_spec.check_type = KAPI_RETURN_FD;
+ spec->error_count = 1;
+ spec->errors[0].error_code = -ENOENT;
+ spec->errors[0].name = "ENOENT";
+
+ /* -EPERM is not in the error list, but unlisted errors are accepted
+ * since filesystem/device-specific errors may not be exhaustively listed
+ */
+ KUNIT_EXPECT_TRUE(test, kapi_validate_return_value(spec, -EPERM));
+}
+
+/* ALIGNMENT constraint */
+static void test_constraint_alignment(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+
+ param.name = "test_align";
+ param.constraint_type = KAPI_CONSTRAINT_ALIGNMENT;
+ param.alignment = 8;
+
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 0));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 8));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 16));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 1));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 7));
+}
+
+/* FD validation rejects values > INT_MAX */
+static void test_fd_int_overflow(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+
+ param.name = "test_fd";
+ param.type = KAPI_TYPE_FD;
+ param.constraint_type = KAPI_CONSTRAINT_NONE;
+
+ /* Value that overflows int: 0x100000003 -> truncates to 3 */
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 0x100000003LL));
+}
+
+/* ENUM constraint */
+static const s64 test_enum_vals[] = { 1, 5, 10 };
+
+static void test_constraint_enum(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+
+ param.name = "test_enum";
+ param.constraint_type = KAPI_CONSTRAINT_ENUM;
+ param.enum_values = test_enum_vals;
+ param.enum_count = ARRAY_SIZE(test_enum_vals);
+
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 1));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 5));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 10));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 0));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 3));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 11));
+}
+
+/* BUFFER constraint always accepts (size checked at runtime) */
+static void test_constraint_buffer(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+
+ param.name = "test_buf";
+ param.constraint_type = KAPI_CONSTRAINT_BUFFER;
+
+ /* Buffer constraint doesn't validate the value itself */
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 0));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 4096));
+}
+
+/* RETURN_RANGE check type */
+static void test_return_range(struct kunit *test)
+{
+ struct kernel_api_spec *spec;
+
+ spec = kunit_kzalloc(test, sizeof(*spec), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+
+ spec->name = "test_ret_range";
+ spec->return_magic = KAPI_MAGIC_RETURN;
+ spec->return_spec.check_type = KAPI_RETURN_RANGE;
+ spec->return_spec.success_min = 0;
+ spec->return_spec.success_max = 100;
+
+ KUNIT_EXPECT_TRUE(test, kapi_validate_return_value(spec, 0));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_return_value(spec, 50));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_return_value(spec, 100));
+}
+
+static void init_dyn_buf_param(struct kapi_param_spec *param)
+{
+ param->name = "buf";
+ param->type = KAPI_TYPE_USER_PTR;
+ param->constraint_type = KAPI_CONSTRAINT_BUFFER;
+ param->size_param_idx = 2;
+}
+
+static bool validate_dyn_buf(const struct kapi_param_spec *param,
+ const void __user *ptr, s64 count)
+{
+ s64 params[] = { (s64)(unsigned long)ptr, count };
+
+ return kapi_validate_param_with_context(param, params[0], params,
+ ARRAY_SIZE(params));
+}
+
+/* Dynamic buffer: any pointer is accepted when the size is 0 */
+static void test_dyn_buf_zero_count(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+ const void __user *kernel_addr = (void __user *)-(unsigned long)PAGE_SIZE;
+
+ init_dyn_buf_param(¶m);
+
+ KUNIT_EXPECT_TRUE(test, validate_dyn_buf(¶m, NULL, 0));
+ KUNIT_EXPECT_TRUE(test, validate_dyn_buf(¶m, (void __user *)PAGE_SIZE, 0));
+ KUNIT_EXPECT_TRUE(test, validate_dyn_buf(¶m, kernel_addr, 0));
+}
+
+/* Dynamic buffer: NULL is rejected when the size is non-zero */
+static void test_dyn_buf_null_nonzero_count(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+
+ init_dyn_buf_param(¶m);
+
+ KUNIT_EXPECT_FALSE(test, validate_dyn_buf(¶m, NULL, 1));
+ KUNIT_EXPECT_FALSE(test, validate_dyn_buf(¶m, NULL, PAGE_SIZE));
+}
+
+/*
+ * Some architectures (separate user address space, !MMU) accept any address
+ * in access_ok(), so the rejection cases below only apply where it bounds
+ * user ranges.
+ */
+static bool kapi_rejects_kernel_addr(const void __user *addr)
+{
+ return !access_ok(addr, 1);
+}
+
+static bool kapi_bounds_user_range(const void __user *base)
+{
+ return access_ok(base, PAGE_SIZE) && !access_ok(base, 3 * PAGE_SIZE);
+}
+
+/* Dynamic buffer: addresses outside user space are rejected when the size is non-zero */
+static void test_dyn_buf_kernel_addr(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+ const void __user *kernel_addr = (void __user *)-(unsigned long)PAGE_SIZE;
+ const void __user *top = (void __user *)ULONG_MAX;
+
+ init_dyn_buf_param(¶m);
+
+ if (!kapi_rejects_kernel_addr(kernel_addr) || !kapi_rejects_kernel_addr(top))
+ kunit_skip(test, "access_ok() accepts kernel addresses on this architecture");
+
+ KUNIT_EXPECT_FALSE(test, validate_dyn_buf(¶m, kernel_addr, 1));
+ KUNIT_EXPECT_FALSE(test, validate_dyn_buf(¶m, top, 1));
+}
+
+/* Dynamic buffer: an unset size_multiplier means byte units */
+static void test_dyn_buf_default_multiplier(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+ const void __user *near_top = (void __user *)(TASK_SIZE_MAX - 2 * PAGE_SIZE);
+
+ init_dyn_buf_param(¶m);
+
+ KUNIT_EXPECT_TRUE(test, validate_dyn_buf(¶m, (void __user *)PAGE_SIZE, 16));
+ KUNIT_EXPECT_TRUE(test, validate_dyn_buf(¶m, near_top, 0));
+
+ if (!kapi_bounds_user_range(near_top))
+ kunit_skip(test, "access_ok() does not bound user ranges on this architecture");
+
+ KUNIT_EXPECT_TRUE(test, validate_dyn_buf(¶m, near_top, PAGE_SIZE));
+ KUNIT_EXPECT_FALSE(test, validate_dyn_buf(¶m, near_top, 3 * PAGE_SIZE));
+}
+
+/* Dynamic buffer: explicit multiplier scales the size, negative and overflowing counts fail */
+static void test_dyn_buf_multiplier_and_bad_count(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+ const void __user *near_top = (void __user *)(TASK_SIZE_MAX - 2 * PAGE_SIZE);
+
+ init_dyn_buf_param(¶m);
+ param.size_multiplier = 8;
+
+ KUNIT_EXPECT_FALSE(test, validate_dyn_buf(¶m, (void __user *)PAGE_SIZE,
+ (s64)(SIZE_MAX / 8 + 1)));
+ KUNIT_EXPECT_FALSE(test, validate_dyn_buf(¶m, (void __user *)PAGE_SIZE, -1));
+
+ if (!kapi_bounds_user_range(near_top))
+ kunit_skip(test, "access_ok() does not bound user ranges on this architecture");
+
+ KUNIT_EXPECT_TRUE(test, validate_dyn_buf(¶m, near_top, PAGE_SIZE / 8));
+ KUNIT_EXPECT_FALSE(test, validate_dyn_buf(¶m, near_top, PAGE_SIZE));
+}
+
+#endif /* CONFIG_KAPI_RUNTIME_CHECKS */
+
+static const struct kernel_api_spec kapi_test_macro_spec = {
+ .name = "test_macro_spec",
+ KAPI_SIGNAL_MASK_COUNT(1)
+ KAPI_SIGNAL_MASK(0, "blocked", "Signals blocked while waiting")
+ KAPI_SIGNAL_MASK_SIGNALS(SIGINT, SIGTERM, SIGQUIT)
+ },
+ KAPI_CAPABILITY_COUNT(1)
+ KAPI_CAPABILITY(0, CAP_SYS_ADMIN, "CAP_SYS_ADMIN", KAPI_CAP_BYPASS_CHECK)
+ KAPI_CAP_ALTERNATIVE(CAP_SYS_RESOURCE, CAP_NET_ADMIN)
+ },
+};
+
+/* Signal mask and capability alternative macros fill in their counts */
+static void test_signal_mask_and_cap_alternative(struct kunit *test)
+{
+ const struct kernel_api_spec *spec = &kapi_test_macro_spec;
+ char *buf;
+
+ KUNIT_EXPECT_EQ(test, spec->signal_mask_count, 1U);
+ KUNIT_EXPECT_EQ(test, spec->signal_masks[0].signal_count, 3U);
+ KUNIT_EXPECT_EQ(test, spec->signal_masks[0].signals[2], SIGQUIT);
+ KUNIT_EXPECT_EQ(test, spec->capabilities[0].alternative_count, 2U);
+ KUNIT_EXPECT_EQ(test, spec->capabilities[0].alternative[1], CAP_NET_ADMIN);
+
+ buf = kunit_kzalloc(test, 8192, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, buf);
+ KUNIT_ASSERT_GT(test, kapi_export_json(spec, buf, 8192), 0);
+ KUNIT_EXPECT_NOT_NULL(test, strstr(buf, "\"blocked\""));
+ KUNIT_EXPECT_NOT_NULL(test, strstr(buf, "\"alternatives\""));
+}
+
+/* Unregister non-existent spec is a no-op */
+static void test_unregister_nonexistent(struct kunit *test)
+{
+ /* Should not crash or error */
+ kapi_unregister_spec("nonexistent_spec_xyz");
+}
+
+/* Multiple specs can be registered and looked up */
+static void test_multiple_specs(struct kunit *test)
+{
+ struct kernel_api_spec *spec1, *spec2;
+ const struct kernel_api_spec *found;
+
+ spec1 = kzalloc_obj(*spec1, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec1);
+ spec2 = kzalloc_obj(*spec2, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec2);
+
+ init_test_spec(spec1, "multi_spec_1");
+ init_test_spec(spec2, "multi_spec_2");
+
+ KUNIT_ASSERT_EQ(test, kapi_register_spec(spec1), 0);
+ KUNIT_ASSERT_EQ(test, kapi_register_spec(spec2), 0);
+
+ found = kapi_get_spec("multi_spec_1");
+ KUNIT_EXPECT_PTR_EQ(test, found, (const struct kernel_api_spec *)spec1);
+
+ found = kapi_get_spec("multi_spec_2");
+ KUNIT_EXPECT_PTR_EQ(test, found, (const struct kernel_api_spec *)spec2);
+
+ kapi_unregister_spec("multi_spec_1");
+ kapi_unregister_spec("multi_spec_2");
+ kfree(spec1);
+ kfree(spec2);
+}
+
+/* JSON export produces valid output */
+static void test_json_export(struct kunit *test)
+{
+ static const s64 enum_vals[] = { 3, -1 };
+ static const s64 err_vals[] = { -EINVAL, -EFAULT };
+ struct kernel_api_spec *spec;
+ char *buf;
+ int ret;
+
+ spec = kzalloc_obj(*spec, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+
+ buf = kzalloc(4096, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf);
+
+ init_test_spec(spec, "test_json");
+ spec->param_count = 1;
+ spec->params[0].name = "arg0";
+ spec->params[0].type_name = "int";
+ spec->params[0].constraint_type = KAPI_CONSTRAINT_ENUM;
+ spec->params[0].enum_values = enum_vals;
+ spec->params[0].enum_count = ARRAY_SIZE(enum_vals);
+ spec->params[0].valid_mask = 0xff;
+ spec->params[0].size_param_idx = 2;
+ spec->return_spec.check_type = KAPI_RETURN_ERROR_CHECK;
+ spec->return_spec.error_values = err_vals;
+ spec->return_spec.error_count = ARRAY_SIZE(err_vals);
+
+ ret = kapi_export_json(spec, buf, 4096);
+ KUNIT_EXPECT_GT(test, ret, 0);
+
+ /* Verify it starts with '{' and ends with '}' */
+ KUNIT_EXPECT_EQ(test, buf[0], '{');
+ KUNIT_ASSERT_GT(test, ret, 1);
+ /* Find last non-whitespace char */
+ while (ret > 0 && (buf[ret - 1] == '\n' || buf[ret - 1] == ' '))
+ ret--;
+ KUNIT_EXPECT_EQ(test, buf[ret - 1], '}');
+
+ /* Verify key fields are present */
+ KUNIT_EXPECT_NOT_NULL(test, strstr(buf, "\"name\""));
+ KUNIT_EXPECT_NOT_NULL(test, strstr(buf, "\"test_json\""));
+ KUNIT_EXPECT_NOT_NULL(test, strstr(buf, "\"parameters\""));
+
+ KUNIT_EXPECT_NOT_NULL(test, strstr(buf, "\"constraint_type\": \"enum\""));
+ KUNIT_EXPECT_NOT_NULL(test, strstr(buf, "\"enum_values\": [3, -1]"));
+ KUNIT_EXPECT_NOT_NULL(test, strstr(buf, "\"valid_mask\": \"0xff\""));
+ KUNIT_EXPECT_NOT_NULL(test, strstr(buf, "\"size_param_idx\": 1"));
+ KUNIT_EXPECT_NOT_NULL(test, strstr(buf, "\"error_values\": [-22, -14]"));
+ KUNIT_EXPECT_NOT_NULL(test, strstr(buf, "\"state_transitions\""));
+ KUNIT_EXPECT_NOT_NULL(test, strstr(buf, "\"struct_specs\""));
+
+ kfree(buf);
+ kfree(spec);
+}
+
+/* JSON export with NULL args returns -EINVAL */
+static void test_json_export_null(struct kunit *test)
+{
+ struct kernel_api_spec *spec;
+ char buf[64];
+ int ret;
+
+ ret = kapi_export_json(NULL, buf, sizeof(buf));
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+
+ spec = kunit_kzalloc(test, sizeof(*spec), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+ init_test_spec(spec, "test");
+
+ ret = kapi_export_json(spec, NULL, 64);
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+
+ ret = kapi_export_json(spec, buf, 0);
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+}
+
+/* JSON export into a buffer that is too small reports -E2BIG */
+static void test_json_export_small_buffer(struct kunit *test)
+{
+ struct kernel_api_spec *spec;
+ char buf[64];
+ int ret;
+
+ spec = kunit_kzalloc(test, sizeof(*spec), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+ init_test_spec(spec, "test_small");
+
+ ret = kapi_export_json(spec, buf, sizeof(buf));
+
+ KUNIT_EXPECT_EQ(test, ret, -E2BIG);
+}
+
+static struct kunit_case kapi_test_cases[] = {
+ KUNIT_CASE(test_register_valid),
+ KUNIT_CASE(test_lookup_registered),
+ KUNIT_CASE(test_double_register),
+ KUNIT_CASE(test_unregister),
+ KUNIT_CASE(test_get_spec_null),
+ KUNIT_CASE(test_register_null),
+ KUNIT_CASE(test_register_null_name),
+#ifdef CONFIG_KAPI_RUNTIME_CHECKS
+ KUNIT_CASE(test_constraint_range_valid),
+ KUNIT_CASE(test_constraint_range_invalid),
+ KUNIT_CASE(test_constraint_mask_valid),
+ KUNIT_CASE(test_constraint_mask_invalid),
+ KUNIT_CASE(test_constraint_power_of_two),
+ KUNIT_CASE(test_constraint_page_aligned),
+ KUNIT_CASE(test_constraint_nonzero),
+ KUNIT_CASE(test_return_validation),
+ KUNIT_CASE(test_return_known_error),
+ KUNIT_CASE(test_return_unknown_error),
+ KUNIT_CASE(test_constraint_alignment),
+ KUNIT_CASE(test_fd_int_overflow),
+ KUNIT_CASE(test_constraint_enum),
+ KUNIT_CASE(test_constraint_buffer),
+ KUNIT_CASE(test_return_range),
+ KUNIT_CASE(test_dyn_buf_zero_count),
+ KUNIT_CASE(test_dyn_buf_null_nonzero_count),
+ KUNIT_CASE(test_dyn_buf_kernel_addr),
+ KUNIT_CASE(test_dyn_buf_default_multiplier),
+ KUNIT_CASE(test_dyn_buf_multiplier_and_bad_count),
+#endif
+ KUNIT_CASE(test_signal_mask_and_cap_alternative),
+ KUNIT_CASE(test_unregister_nonexistent),
+ KUNIT_CASE(test_multiple_specs),
+ KUNIT_CASE(test_json_export),
+ KUNIT_CASE(test_json_export_null),
+ KUNIT_CASE(test_json_export_small_buffer),
+ {}
+};
+
+static struct kunit_suite kapi_test_suite = {
+ .name = "kapi",
+ .test_cases = kapi_test_cases,
+};
+
+kunit_test_suite(kapi_test_suite);
+
+MODULE_DESCRIPTION("KUnit tests for Kernel API Specification Framework");
+MODULE_LICENSE("GPL");
diff --git a/kernel/api/kernel_api_spec.c b/kernel/api/kernel_api_spec.c
new file mode 100644
index 0000000000000..c3b220104c04b
--- /dev/null
+++ b/kernel/api/kernel_api_spec.c
@@ -0,0 +1,1415 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) "kapi: " fmt
+
+#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/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>
+#include <linux/mm.h>
+#include <linux/ratelimit.h>
+
+#include "internal.h"
+
+/* Dynamic API registration */
+static LIST_HEAD(dynamic_api_specs);
+static DEFINE_MUTEX(api_spec_mutex);
+
+struct dynamic_api_spec {
+ struct list_head list;
+ const struct kernel_api_spec *spec;
+};
+
+/*
+ * __kapi_find_spec_locked - Internal lookup, caller must hold api_spec_mutex
+ */
+static const struct kernel_api_spec *__kapi_find_spec_locked(const char *name)
+{
+ const struct kernel_api_spec * const *pp;
+ struct dynamic_api_spec *dyn_spec;
+
+ for (pp = __start_kapi_specs; pp < __stop_kapi_specs; pp++) {
+ const struct kernel_api_spec *spec = *pp;
+
+ if (spec && spec->name && strcmp(spec->name, name) == 0)
+ return spec;
+ }
+
+ list_for_each_entry(dyn_spec, &dynamic_api_specs, list) {
+ if (dyn_spec->spec->name &&
+ strcmp(dyn_spec->spec->name, name) == 0)
+ return dyn_spec->spec;
+ }
+
+ return NULL;
+}
+
+/**
+ * kapi_get_spec - Get API specification by name
+ * @name: Function name to look up
+ *
+ * Return: Pointer to the API specification, or NULL if not found. The
+ * pointer stays valid for specifications in the ``.kapi_specs`` ELF section
+ * (built-in, statically defined). A dynamically registered spec stays valid
+ * only until kapi_unregister_spec() is called for it, so the caller must
+ * serialize against unregistration.
+ *
+ * Context: May sleep. Do not call under spinlock or in IRQ context.
+ */
+const struct kernel_api_spec *kapi_get_spec(const char *name)
+{
+ const struct kernel_api_spec *spec;
+
+ if (!name)
+ return NULL;
+
+ mutex_lock(&api_spec_mutex);
+ spec = __kapi_find_spec_locked(name);
+ mutex_unlock(&api_spec_mutex);
+
+ return spec;
+}
+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(const struct kernel_api_spec *spec)
+{
+ struct dynamic_api_spec *dyn_spec;
+ int ret = 0;
+
+ if (!spec || !spec->name || !spec->name[0])
+ return -EINVAL;
+
+ dyn_spec = kzalloc_obj(*dyn_spec, GFP_KERNEL);
+ if (!dyn_spec)
+ return -ENOMEM;
+
+ dyn_spec->spec = spec;
+
+ mutex_lock(&api_spec_mutex);
+
+ /* Check if already exists while holding lock to prevent races */
+ if (__kapi_find_spec_locked(spec->name)) {
+ ret = -EEXIST;
+ kfree(dyn_spec);
+ } else {
+ list_add_tail(&dyn_spec->list, &dynamic_api_specs);
+ }
+
+ mutex_unlock(&api_spec_mutex);
+
+ return ret;
+}
+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;
+
+ if (!name)
+ return;
+
+ mutex_lock(&api_spec_mutex);
+ list_for_each_entry_safe(dyn_spec, tmp, &dynamic_api_specs, list) {
+ if (dyn_spec->spec->name &&
+ 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);
+
+/**
+ * kapi_param_type_to_string - Convert parameter type to string
+ * @type: Parameter type
+ *
+ * Return: String representation of type
+ */
+const char *kapi_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];
+}
+
+/**
+ * kapi_lock_type_to_string - Convert lock type to string
+ * @type: Lock type
+ *
+ * Return: String representation of lock type
+ */
+const char *kapi_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];
+}
+
+/**
+ * kapi_lock_scope_to_string - Convert lock scope to string
+ * @scope: Lock scope
+ *
+ * Return: String representation of lock scope
+ */
+const char *kapi_lock_scope_to_string(enum kapi_lock_scope scope)
+{
+ static const char * const scope_names[] = {
+ [KAPI_LOCK_INTERNAL] = "internal",
+ [KAPI_LOCK_ACQUIRES] = "acquires",
+ [KAPI_LOCK_RELEASES] = "releases",
+ [KAPI_LOCK_CALLER_HELD] = "caller_held",
+ };
+
+ if (scope >= ARRAY_SIZE(scope_names))
+ return "unknown";
+
+ return scope_names[scope];
+}
+
+/**
+ * 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",
+ [KAPI_RETURN_NO_RETURN] = "no_return",
+ };
+
+ 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];
+}
+
+/**
+ * constraint_type_to_string - Convert constraint type to string
+ * @type: Constraint type
+ *
+ * Return: String representation of constraint type
+ */
+static const char *constraint_type_to_string(enum kapi_constraint_type type)
+{
+ static const char * const constraint_names[] = {
+ [KAPI_CONSTRAINT_NONE] = "none",
+ [KAPI_CONSTRAINT_RANGE] = "range",
+ [KAPI_CONSTRAINT_MASK] = "mask",
+ [KAPI_CONSTRAINT_ENUM] = "enum",
+ [KAPI_CONSTRAINT_ALIGNMENT] = "alignment",
+ [KAPI_CONSTRAINT_POWER_OF_TWO] = "power_of_two",
+ [KAPI_CONSTRAINT_PAGE_ALIGNED] = "page_aligned",
+ [KAPI_CONSTRAINT_NONZERO] = "nonzero",
+ [KAPI_CONSTRAINT_USER_STRING] = "user_string",
+ [KAPI_CONSTRAINT_USER_PATH] = "user_path",
+ [KAPI_CONSTRAINT_USER_PTR] = "user_ptr",
+ [KAPI_CONSTRAINT_BUFFER] = "buffer",
+ [KAPI_CONSTRAINT_CUSTOM] = "custom",
+ };
+
+ if (type >= ARRAY_SIZE(constraint_names))
+ return "unknown";
+
+ return constraint_names[type];
+}
+
+/*
+ * kapi_json_escape - Write a JSON-escaped string into a buffer
+ * @buf: Output buffer
+ * @size: Remaining space in buffer
+ * @str: Input string to escape
+ *
+ * Escapes backslash, double-quote, and control characters for JSON output.
+ * Return: Number of bytes written (via scnprintf semantics)
+ */
+static int kapi_json_escape(char *buf, size_t size, const char *str)
+{
+ int ret = 0;
+ const char *p;
+
+ if (!str || size == 0)
+ return 0;
+
+ for (p = str; *p && ret < size - 1; p++) {
+ switch (*p) {
+ case '\\':
+ ret += scnprintf(buf + ret, size - ret, "\\\\");
+ break;
+ case '"':
+ ret += scnprintf(buf + ret, size - ret, "\\\"");
+ break;
+ case '\n':
+ ret += scnprintf(buf + ret, size - ret, "\\n");
+ break;
+ case '\r':
+ ret += scnprintf(buf + ret, size - ret, "\\r");
+ break;
+ case '\t':
+ ret += scnprintf(buf + ret, size - ret, "\\t");
+ break;
+ default:
+ if ((unsigned char)*p < 0x20) {
+ ret += scnprintf(buf + ret, size - ret,
+ "\\u%04x", (unsigned char)*p);
+ } else {
+ ret += scnprintf(buf + ret, size - ret,
+ "%c", *p);
+ }
+ break;
+ }
+ }
+
+ if (ret < size)
+ buf[ret] = '\0';
+
+ return ret;
+}
+
+static int kapi_json_str(char *buf, size_t size, const char *str)
+{
+ int ret = 0;
+
+ ret += scnprintf(buf, size, "\"");
+ ret += kapi_json_escape(buf + ret, size - ret, str);
+ ret += scnprintf(buf + ret, size - ret, "\"");
+ return ret;
+}
+
+static int kapi_json_s64_list(char *buf, size_t size, const s64 *vals, u32 count)
+{
+ int ret = scnprintf(buf, size, "[");
+ u32 i;
+
+ for (i = 0; vals && i < count; i++)
+ ret += scnprintf(buf + ret, size - ret, "%s%lld",
+ i ? ", " : "", vals[i]);
+
+ ret += scnprintf(buf + ret, size - ret, "]");
+ return ret;
+}
+
+static int kapi_json_struct_spec(char *buf, size_t size,
+ const struct kapi_struct_spec *st)
+{
+ int ret;
+ u32 i;
+
+ ret = scnprintf(buf, size, " {\n \"name\": ");
+ ret += kapi_json_str(buf + ret, size - ret, st->name);
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n \"size\": %zu,\n \"alignment\": %zu,\n \"description\": ",
+ st->size, st->alignment);
+ ret += kapi_json_str(buf + ret, size - ret, st->description);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"fields\": [\n");
+
+ for (i = 0; i < st->field_count && i < KAPI_MAX_PARAMS; i++) {
+ const struct kapi_struct_field *field = &st->fields[i];
+
+ ret += scnprintf(buf + ret, size - ret,
+ " {\n \"name\": ");
+ ret += kapi_json_str(buf + ret, size - ret, field->name);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"type\": ");
+ ret += kapi_json_str(buf + ret, size - ret, field->type_name);
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n"
+ " \"type_class\": \"%s\",\n"
+ " \"offset\": %zu,\n"
+ " \"size\": %zu,\n"
+ " \"flags\": \"0x%x\",\n"
+ " \"constraint_type\": \"%s\",\n"
+ " \"min_value\": %lld,\n"
+ " \"max_value\": %lld,\n"
+ " \"valid_mask\": \"0x%llx\",\n"
+ " \"enum_values\": ",
+ kapi_param_type_to_string(field->type),
+ field->offset, field->size, field->flags,
+ constraint_type_to_string(field->constraint_type),
+ field->min_value, field->max_value, field->valid_mask);
+ ret += kapi_json_str(buf + ret, size - ret, field->enum_values);
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n \"description\": ");
+ ret += kapi_json_str(buf + ret, size - ret, field->description);
+ ret += scnprintf(buf + ret, size - ret,
+ "\n }%s\n",
+ (i < st->field_count - 1) ? "," : "");
+ }
+
+ ret += scnprintf(buf + ret, size - ret, " ]\n }");
+ return ret;
+}
+
+/**
+ * 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, -EINVAL on bad arguments, or -E2BIG
+ * if the output did not fit in @buf (the contents are then incomplete)
+ */
+int kapi_export_json(const struct kernel_api_spec *spec, char *buf, size_t size)
+{
+ int ret = 0;
+ int i, j;
+
+ if (!spec || !buf || size == 0)
+ return -EINVAL;
+
+ ret = scnprintf(buf, size, "{\n \"name\": ");
+ ret += kapi_json_str(buf + ret, size - ret, spec->name);
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n \"version\": %u,\n \"description\": ",
+ spec->version);
+ ret += kapi_json_str(buf + ret, size - ret, spec->description);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"long_description\": ");
+ ret += kapi_json_str(buf + ret, size - ret, spec->long_description);
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n \"context_flags\": \"0x%x\",\n",
+ 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\": ");
+ ret += kapi_json_str(buf + ret, size - ret, param->name);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"type\": ");
+ ret += kapi_json_str(buf + ret, size - ret, param->type_name);
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n \"type_class\": \"%s\",\n \"flags\": \"0x%x\",\n \"description\": ",
+ kapi_param_type_to_string(param->type),
+ param->flags);
+ ret += kapi_json_str(buf + ret, size - ret, param->description);
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n \"constraint_type\": \"%s\",\n \"constraint_desc\": ",
+ constraint_type_to_string(param->constraint_type));
+ ret += kapi_json_str(buf + ret, size - ret, param->constraints);
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n"
+ " \"min_value\": %lld,\n"
+ " \"max_value\": %lld,\n"
+ " \"valid_mask\": \"0x%llx\",\n"
+ " \"enum_values\": ",
+ param->min_value, param->max_value, param->valid_mask);
+ ret += kapi_json_s64_list(buf + ret, size - ret,
+ param->enum_values, param->enum_count);
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n \"size\": %zu,\n \"alignment\": %zu,\n \"size_param_idx\": ",
+ param->size, param->alignment);
+ if (param->size_param_idx > 0)
+ ret += scnprintf(buf + ret, size - ret, "%d",
+ param->size_param_idx - 1);
+ else
+ ret += scnprintf(buf + ret, size - ret, "null");
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n \"size_multiplier\": %zu\n }%s\n",
+ param->size_multiplier,
+ (i < spec->param_count - 1) ? "," : "");
+ }
+
+ ret += scnprintf(buf + ret, size - ret, " ],\n");
+
+ /* Return value */
+ ret += scnprintf(buf + ret, size - ret, " \"return\": {\n \"type\": ");
+ ret += kapi_json_str(buf + ret, size - ret, spec->return_spec.type_name);
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n"
+ " \"type_class\": \"%s\",\n"
+ " \"check_type\": \"%s\",\n"
+ " \"success_value\": %lld,\n"
+ " \"success_min\": %lld,\n"
+ " \"success_max\": %lld,\n"
+ " \"error_values\": ",
+ kapi_param_type_to_string(spec->return_spec.type),
+ return_check_type_to_string(spec->return_spec.check_type),
+ spec->return_spec.success_value,
+ spec->return_spec.success_min,
+ spec->return_spec.success_max);
+ ret += kapi_json_s64_list(buf + ret, size - ret,
+ spec->return_spec.error_values,
+ spec->return_spec.error_count);
+
+ ret += scnprintf(buf + ret, size - ret, ",\n \"description\": ");
+ ret += kapi_json_str(buf + ret, size - ret, spec->return_spec.description);
+ ret += scnprintf(buf + ret, size - ret, "\n },\n");
+
+ /* 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\": ",
+ error->error_code);
+ ret += kapi_json_str(buf + ret, size - ret, error->name);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"condition\": ");
+ ret += kapi_json_str(buf + ret, size - ret, error->condition);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"description\": ");
+ ret += kapi_json_str(buf + ret, size - ret, error->description);
+ ret += scnprintf(buf + ret, size - ret,
+ "\n }%s\n",
+ (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_LOCKS; i++) {
+ const struct kapi_lock_spec *lock = &spec->locks[i];
+
+ ret += scnprintf(buf + ret, size - ret, " {\n \"name\": ");
+ ret += kapi_json_str(buf + ret, size - ret, lock->lock_name);
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n \"type\": \"%s\",\n \"scope\": \"%s\",\n \"description\": ",
+ kapi_lock_type_to_string(lock->lock_type),
+ kapi_lock_scope_to_string(lock->scope));
+ ret += kapi_json_str(buf + ret, size - ret, lock->description);
+ ret += scnprintf(buf + ret, size - ret,
+ "\n }%s\n",
+ (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\": ",
+ cap->capability);
+ ret += kapi_json_str(buf + ret, size - ret, cap->cap_name);
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n \"action\": \"%s\",\n \"allows\": ",
+ capability_action_to_string(cap->action));
+ ret += kapi_json_str(buf + ret, size - ret, cap->allows);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"without_cap\": ");
+ ret += kapi_json_str(buf + ret, size - ret, cap->without_cap);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"check_condition\": ");
+ ret += kapi_json_str(buf + ret, size - ret, cap->check_condition);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"priority\": %u", cap->priority);
+
+ if (cap->alternative_count > 0) {
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n \"alternatives\": [");
+ for (j = 0; j < cap->alternative_count && j < KAPI_MAX_CAPABILITIES; j++) {
+ ret += scnprintf(buf + ret, size - ret,
+ "%s%d", j ? ", " : "",
+ cap->alternative[j]);
+ }
+ 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");
+
+ /* Constraints */
+ ret += scnprintf(buf + ret, size - ret, " \"constraints\": [\n");
+ for (i = 0; i < spec->constraint_count && i < KAPI_MAX_CONSTRAINTS; i++) {
+ const struct kapi_constraint_spec *con = &spec->constraints[i];
+
+ ret += scnprintf(buf + ret, size - ret, " {\n \"name\": ");
+ ret += kapi_json_str(buf + ret, size - ret, con->name);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"description\": ");
+ ret += kapi_json_str(buf + ret, size - ret, con->description);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"expression\": ");
+ ret += kapi_json_str(buf + ret, size - ret, con->expression);
+ ret += scnprintf(buf + ret, size - ret,
+ "\n }%s\n",
+ (i < spec->constraint_count - 1) ? "," : "");
+ }
+ ret += scnprintf(buf + ret, size - ret, " ],\n");
+
+ /* Signals */
+ ret += scnprintf(buf + ret, size - ret, " \"signals\": [\n");
+ for (i = 0; i < spec->signal_count && i < KAPI_MAX_SIGNALS; i++) {
+ const struct kapi_signal_spec *sig = &spec->signals[i];
+
+ ret += scnprintf(buf + ret, size - ret,
+ " {\n \"signal_num\": %d,\n \"signal_name\": ",
+ sig->signal_num);
+ ret += kapi_json_str(buf + ret, size - ret, sig->signal_name);
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n \"direction\": \"0x%x\",\n \"action\": %u,\n \"target\": ",
+ sig->direction, sig->action);
+ ret += kapi_json_str(buf + ret, size - ret, sig->target);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"condition\": ");
+ ret += kapi_json_str(buf + ret, size - ret, sig->condition);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"description\": ");
+ ret += kapi_json_str(buf + ret, size - ret, sig->description);
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n"
+ " \"restartable\": %s,\n"
+ " \"sa_flags_required\": \"0x%x\",\n"
+ " \"sa_flags_forbidden\": \"0x%x\",\n"
+ " \"error_on_signal\": %d,\n"
+ " \"transform_to\": %d,\n"
+ " \"timing\": ",
+ sig->restartable ? "true" : "false",
+ sig->sa_flags_required,
+ sig->sa_flags_forbidden,
+ sig->error_on_signal,
+ sig->transform_to);
+ ret += kapi_json_str(buf + ret, size - ret, sig->timing);
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n \"priority\": %u,\n \"interruptible\": %s,\n \"queue_behavior\": ",
+ sig->priority,
+ sig->interruptible ? "true" : "false");
+ ret += kapi_json_str(buf + ret, size - ret, sig->queue_behavior);
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n \"state_required\": \"0x%x\",\n \"state_forbidden\": \"0x%x\"\n }%s\n",
+ sig->state_required,
+ sig->state_forbidden,
+ (i < spec->signal_count - 1) ? "," : "");
+ }
+ ret += scnprintf(buf + ret, size - ret, " ],\n");
+
+ /* Side effects */
+ ret += scnprintf(buf + ret, size - ret, " \"side_effects\": [\n");
+ for (i = 0; i < spec->side_effect_count && i < KAPI_MAX_SIDE_EFFECTS; i++) {
+ const struct kapi_side_effect *eff = &spec->side_effects[i];
+
+ ret += scnprintf(buf + ret, size - ret,
+ " {\n \"type\": \"0x%x\",\n \"target\": ",
+ eff->type);
+ ret += kapi_json_str(buf + ret, size - ret, eff->target);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"condition\": ");
+ ret += kapi_json_str(buf + ret, size - ret, eff->condition);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"description\": ");
+ ret += kapi_json_str(buf + ret, size - ret, eff->description);
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n \"reversible\": %s\n }%s\n",
+ eff->reversible ? "true" : "false",
+ (i < spec->side_effect_count - 1) ? "," : "");
+ }
+ ret += scnprintf(buf + ret, size - ret, " ],\n");
+
+ /* State transitions */
+ ret += scnprintf(buf + ret, size - ret, " \"state_transitions\": [\n");
+ for (i = 0; i < spec->state_trans_count && i < KAPI_MAX_STATE_TRANS; i++) {
+ const struct kapi_state_transition *trans = &spec->state_transitions[i];
+
+ ret += scnprintf(buf + ret, size - ret, " {\n \"object\": ");
+ ret += kapi_json_str(buf + ret, size - ret, trans->object);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"from_state\": ");
+ ret += kapi_json_str(buf + ret, size - ret, trans->from_state);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"to_state\": ");
+ ret += kapi_json_str(buf + ret, size - ret, trans->to_state);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"condition\": ");
+ ret += kapi_json_str(buf + ret, size - ret, trans->condition);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"description\": ");
+ ret += kapi_json_str(buf + ret, size - ret, trans->description);
+ ret += scnprintf(buf + ret, size - ret,
+ "\n }%s\n",
+ (i < spec->state_trans_count - 1) ? "," : "");
+ }
+ ret += scnprintf(buf + ret, size - ret, " ],\n");
+
+ /* Signal masks */
+ ret += scnprintf(buf + ret, size - ret, " \"signal_masks\": [\n");
+ for (i = 0; i < spec->signal_mask_count && i < KAPI_MAX_SIGNALS; i++) {
+ const struct kapi_signal_mask_spec *mask = &spec->signal_masks[i];
+
+ ret += scnprintf(buf + ret, size - ret, " {\n \"name\": ");
+ ret += kapi_json_str(buf + ret, size - ret, mask->mask_name);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"description\": ");
+ ret += kapi_json_str(buf + ret, size - ret, mask->description);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"signals\": [");
+ for (j = 0; j < mask->signal_count && j < KAPI_MAX_SIGNALS; j++)
+ ret += scnprintf(buf + ret, size - ret, "%s%d",
+ j ? ", " : "", mask->signals[j]);
+ ret += scnprintf(buf + ret, size - ret,
+ "]\n }%s\n",
+ (i < spec->signal_mask_count - 1) ? "," : "");
+ }
+ ret += scnprintf(buf + ret, size - ret, " ],\n");
+
+ /* Structure specifications */
+ ret += scnprintf(buf + ret, size - ret, " \"struct_specs\": [\n");
+ for (i = 0; i < spec->struct_spec_count && i < KAPI_MAX_STRUCT_SPECS; i++) {
+ ret += kapi_json_struct_spec(buf + ret, size - ret,
+ &spec->struct_specs[i]);
+ ret += scnprintf(buf + ret, size - ret, "%s\n",
+ (i < spec->struct_spec_count - 1) ? "," : "");
+ }
+ ret += scnprintf(buf + ret, size - ret, " ],\n");
+
+ /* Additional info */
+ ret += scnprintf(buf + ret, size - ret, " \"examples\": ");
+ ret += kapi_json_str(buf + ret, size - ret, spec->examples);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"notes\": ");
+ ret += kapi_json_str(buf + ret, size - ret, spec->notes);
+ ret += scnprintf(buf + ret, size - ret, "\n}\n");
+
+ /* scnprintf() never writes past size - 1, so a full buffer means truncation */
+ if (ret >= size - 1)
+ return -E2BIG;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(kapi_export_json);
+
+#ifdef CONFIG_KAPI_RUNTIME_CHECKS
+
+/**
+ * kapi_validate_fd - Validate that a file descriptor value is in valid range
+ * @fd: File descriptor to validate
+ *
+ * Only the numeric range is checked: AT_FDCWD or a non-negative value.
+ * Openness is left to the syscall, since the fd can be closed between check
+ * and use. Other negative values are rejected here, so a syscall such as
+ * close() fails with EINVAL for them rather than EBADF.
+ *
+ * Return: true if fd is in valid range, false otherwise
+ */
+static bool kapi_validate_fd(int fd)
+{
+ return fd == AT_FDCWD || fd >= 0;
+}
+
+/**
+ * kapi_validate_user_ptr - Validate that a user pointer is accessible
+ * @ptr: User pointer to validate
+ * @size: Size in bytes to validate
+ *
+ * Return: true if user memory is accessible, false otherwise
+ */
+static bool kapi_validate_user_ptr(const void __user *ptr, size_t size)
+{
+ /* NULL pointers are not valid; caller handles optional case */
+ if (!ptr)
+ return false;
+
+ 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;
+
+ /* NULL is allowed for optional parameters */
+ if (!ptr && (param_spec->flags & KAPI_PARAM_OPTIONAL))
+ return true;
+
+ /*
+ * size_param_idx is stored 1-based (0 means "no dynamic sizing").
+ * Convert to a real index before looking into all_params.
+ */
+ if (param_spec->size_param_idx > 0 &&
+ param_spec->size_param_idx - 1 < param_count) {
+ s64 count = all_params[param_spec->size_param_idx - 1];
+ size_t unit = param_spec->size_multiplier ?: 1;
+
+ if (count < 0) {
+ pr_warn_ratelimited("Parameter %s: size determinant is negative (%lld)\n",
+ param_spec->name, count);
+ return false;
+ }
+
+ /* A zero-length access never dereferences the pointer */
+ if (count == 0)
+ return true;
+
+ if (count > SIZE_MAX / unit) {
+ pr_warn_ratelimited("Parameter %s: size calculation overflow\n",
+ param_spec->name);
+ return false;
+ }
+
+ actual_size = (size_t)count * unit;
+ } else {
+ actual_size = param_spec->size;
+ }
+
+ return kapi_validate_user_ptr(ptr, actual_size);
+}
+
+/**
+ * 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_ratelimited("Parameter %s: NULL path not allowed\n", param_spec->name);
+ return false;
+ }
+
+ if (!access_ok(path, 1)) {
+ pr_warn_ratelimited("Parameter %s: path pointer %p not accessible\n",
+ param_spec->name, path);
+ return false;
+ }
+
+ /*
+ * Use strnlen_user to check the path length and accessibility.
+ * Note: strnlen_user() is subject to TOCTOU -- the measured length
+ * may change if another thread modifies the user memory. This is
+ * acceptable since the kernel re-copies and re-validates the path
+ * later in the syscall path. This check is best-effort.
+ */
+ len = strnlen_user(path, PATH_MAX + 1);
+ if (len == 0) {
+ pr_warn_ratelimited("Parameter %s: invalid path pointer %p\n",
+ param_spec->name, path);
+ return false;
+ }
+
+ if (len > PATH_MAX) {
+ pr_warn_ratelimited("Parameter %s: path too long (exceeds PATH_MAX)\n",
+ param_spec->name);
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * kapi_validate_user_string - Validate a userspace null-terminated string
+ * @str: User pointer to string
+ * @param_spec: Parameter specification containing length constraints
+ *
+ * Validates that the userspace string pointer is accessible and that the
+ * string length (excluding null terminator) is within the range specified
+ * by min_value and max_value in the parameter specification.
+ *
+ * Return: true if string is valid, false otherwise
+ */
+static bool kapi_validate_user_string(const char __user *str,
+ const struct kapi_param_spec *param_spec)
+{
+ size_t len;
+ size_t max_check_len;
+
+ /* NULL is allowed for optional parameters */
+ if (!str && (param_spec->flags & KAPI_PARAM_OPTIONAL))
+ return true;
+
+ if (!str) {
+ pr_warn_ratelimited("Parameter %s: NULL string not allowed\n", param_spec->name);
+ return false;
+ }
+
+ if (!access_ok(str, 1)) {
+ pr_warn_ratelimited("Parameter %s: string pointer %p not accessible\n",
+ param_spec->name, str);
+ return false;
+ }
+
+ /*
+ * Use strnlen_user to check the string length and validate accessibility.
+ * Check up to max_value + 1 to detect strings that are too long.
+ * If max_value is 0 or unset, use PATH_MAX as a reasonable default.
+ *
+ * Note: strnlen_user() is subject to TOCTOU -- see comment in
+ * kapi_validate_path() above. This check is best-effort.
+ */
+ max_check_len = param_spec->max_value > 0 ?
+ (size_t)param_spec->max_value + 1 : PATH_MAX + 1;
+ len = strnlen_user(str, max_check_len);
+
+ if (len == 0) {
+ pr_warn_ratelimited("Parameter %s: invalid string pointer %p\n",
+ param_spec->name, str);
+ return false;
+ }
+
+ /*
+ * strnlen_user returns the length including the null terminator.
+ * Convert to string length (excluding terminator) for range check.
+ */
+ len--;
+
+ if (param_spec->min_value > 0 && len < (size_t)param_spec->min_value) {
+ pr_warn_ratelimited("Parameter %s: string too short (%zu < %lld)\n",
+ param_spec->name, len, param_spec->min_value);
+ return false;
+ }
+
+ if (param_spec->max_value > 0 && len > (size_t)param_spec->max_value) {
+ pr_warn_ratelimited("Parameter %s: string too long (%zu > %lld)\n",
+ param_spec->name, len, param_spec->max_value);
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * kapi_validate_user_ptr_constraint - Validate a userspace pointer with size
+ * @ptr: User pointer to validate
+ * @param_spec: Parameter specification containing size
+ *
+ * Validates that the userspace pointer is accessible and that the memory
+ * region of the specified size can be accessed. The size is taken from
+ * the param_spec->size field.
+ *
+ * Return: true if pointer is valid, false otherwise
+ */
+static bool kapi_validate_user_ptr_constraint(const void __user *ptr,
+ const struct kapi_param_spec *param_spec)
+{
+ /* NULL is allowed for optional parameters */
+ if (!ptr && (param_spec->flags & KAPI_PARAM_OPTIONAL))
+ return true;
+
+ if (!ptr) {
+ pr_warn_ratelimited("Parameter %s: NULL pointer not allowed\n", param_spec->name);
+ return false;
+ }
+
+ if (param_spec->size == 0) {
+ pr_warn_ratelimited("Parameter %s: size not specified for user pointer validation\n",
+ param_spec->name);
+ return false;
+ }
+
+ if (!access_ok(ptr, param_spec->size)) {
+ pr_warn_ratelimited("Parameter %s: user pointer %p not accessible for %zu bytes\n",
+ param_spec->name, ptr, param_spec->size);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * check_user_ptr is false once the pointer was validated against its dynamic
+ * size; the fixed-size check would reject NULL even when that size is 0.
+ */
+static bool kapi_validate_param_checks(const struct kapi_param_spec *param_spec,
+ s64 value, bool check_user_ptr)
+{
+ int i;
+
+ /* Special handling for file descriptor type */
+ if (param_spec->type == KAPI_TYPE_FD &&
+ !(param_spec->flags & KAPI_PARAM_OPTIONAL)) {
+ if (value < INT_MIN || value > INT_MAX) {
+ pr_warn_ratelimited("Parameter %s: file descriptor %lld out of int range\n",
+ param_spec->name, value);
+ return false;
+ }
+ if (!kapi_validate_fd((int)value)) {
+ pr_warn_ratelimited("Parameter %s: invalid file descriptor %lld\n",
+ param_spec->name, value);
+ return false;
+ }
+ }
+
+ /* Special handling for user pointer type */
+ if (check_user_ptr && param_spec->type == KAPI_TYPE_USER_PTR) {
+ const void __user *ptr = (const void __user *)(unsigned long)value;
+
+ /* 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)) {
+ pr_warn_ratelimited("Parameter %s: invalid user pointer %p (size: %zu)\n",
+ param_spec->name, ptr, param_spec->size);
+ return false;
+ }
+ }
+
+ /* Special handling for path type */
+ if (param_spec->type == KAPI_TYPE_PATH) {
+ const char __user *path = (const char __user *)(unsigned long)value;
+
+ if (!kapi_validate_path(path, param_spec))
+ return false;
+ }
+
+ switch (param_spec->constraint_type) {
+ case KAPI_CONSTRAINT_NONE:
+ case KAPI_CONSTRAINT_BUFFER:
+ return true;
+
+ case KAPI_CONSTRAINT_RANGE:
+ /*
+ * If max_value is below min_value, it was likely set from an
+ * unsigned constant (e.g. SIZE_MAX) that overflowed s64. Treat
+ * as no upper bound; only check the minimum.
+ */
+ if (param_spec->max_value >= param_spec->min_value) {
+ if (value < param_spec->min_value ||
+ value > param_spec->max_value) {
+ pr_warn_ratelimited("Parameter %s value %lld out of range [%lld, %lld]\n",
+ param_spec->name, value,
+ param_spec->min_value,
+ param_spec->max_value);
+ return false;
+ }
+ } else {
+ if (value < param_spec->min_value) {
+ pr_warn_ratelimited("Parameter %s value %lld below minimum %lld\n",
+ param_spec->name, value,
+ param_spec->min_value);
+ return false;
+ }
+ }
+ return true;
+
+ case KAPI_CONSTRAINT_MASK:
+ if (value & ~param_spec->valid_mask) {
+ pr_warn_ratelimited("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_ratelimited("Parameter %s value %lld not in valid enumeration\n",
+ param_spec->name, value);
+ return false;
+
+ case KAPI_CONSTRAINT_ALIGNMENT:
+ if (param_spec->alignment == 0) {
+ pr_warn_ratelimited("Parameter %s: alignment constraint specified but alignment is 0\n",
+ param_spec->name);
+ return false;
+ }
+ if (param_spec->alignment & (param_spec->alignment - 1)) {
+ pr_warn_ratelimited("Parameter %s: alignment %zu is not a power of two\n",
+ param_spec->name, param_spec->alignment);
+ return false;
+ }
+ if (value & (param_spec->alignment - 1)) {
+ pr_warn_ratelimited("Parameter %s value 0x%llx not aligned to %zu boundary\n",
+ param_spec->name, value, param_spec->alignment);
+ return false;
+ }
+ return true;
+
+ case KAPI_CONSTRAINT_POWER_OF_TWO:
+ if (value == 0 || (value & (value - 1))) {
+ pr_warn_ratelimited("Parameter %s value %lld is not a power of two\n",
+ param_spec->name, value);
+ return false;
+ }
+ return true;
+
+ case KAPI_CONSTRAINT_PAGE_ALIGNED:
+ if (value & (PAGE_SIZE - 1)) {
+ pr_warn_ratelimited("Parameter %s value 0x%llx not page-aligned (PAGE_SIZE=%ld)\n",
+ param_spec->name, value, PAGE_SIZE);
+ return false;
+ }
+ return true;
+
+ case KAPI_CONSTRAINT_NONZERO:
+ if (value == 0) {
+ pr_warn_ratelimited("Parameter %s must be non-zero\n", param_spec->name);
+ return false;
+ }
+ return true;
+
+ case KAPI_CONSTRAINT_USER_STRING:
+ return kapi_validate_user_string((const char __user *)(unsigned long)value,
+ param_spec);
+
+ case KAPI_CONSTRAINT_USER_PATH:
+ return kapi_validate_path((const char __user *)(unsigned long)value, param_spec);
+
+ case KAPI_CONSTRAINT_USER_PTR:
+ return kapi_validate_user_ptr_constraint((const void __user *)(unsigned long)value,
+ param_spec);
+
+ case KAPI_CONSTRAINT_CUSTOM:
+ if (param_spec->validate)
+ return param_spec->validate(value);
+ return true;
+
+ default:
+ 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)
+{
+ return kapi_validate_param_checks(param_spec, value, 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 *)(unsigned long)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_ratelimited("Parameter %s: invalid user pointer %p\n",
+ param_spec->name, ptr);
+ return false;
+ }
+ return kapi_validate_param_checks(param_spec, value, false);
+ }
+
+ /* 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_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_ratelimited("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
+ *
+ * Return: 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;
+
+ 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;
+ }
+ }
+ return true;
+
+ 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;
+ }
+}
+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: false if the spec is a KAPI_RETURN_FD check and a successful @retval
+ * is not a valid file descriptor, true otherwise.
+ *
+ * kapi_check_return_success() runs first. A value that does not satisfy it is
+ * treated as an error and is accepted. An error code that is not listed in the
+ * spec is only logged with pr_debug().
+ */
+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) {
+ /* Special validation for file descriptor returns */
+ if (spec->return_spec.check_type == KAPI_RETURN_FD) {
+ if (retval > INT_MAX || !kapi_validate_fd((int)retval)) {
+ pr_warn_ratelimited("API %s returned invalid file descriptor %lld\n",
+ spec->name, retval);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ if (spec->error_count == 0) {
+ pr_debug("API %s returned unspecified error %lld\n",
+ spec->name, retval);
+ return true;
+ }
+
+ 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 - log at debug level since filesystem-specific and
+ * device-specific error codes may not be exhaustively listed.
+ */
+ pr_debug("API %s returned error code %lld not listed in spec\n",
+ spec->name, retval);
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(kapi_validate_return_value);
+
+/**
+ * kapi_validate_syscall_return - Validate syscall return value
+ * @spec: API specification
+ * @retval: Return value
+ *
+ * Return: always 0. A return value that does not match the spec is only
+ * logged and the syscall result is left unchanged.
+ */
+int kapi_validate_syscall_return(const struct kernel_api_spec *spec, s64 retval)
+{
+ if (!spec)
+ return 0;
+
+ /* Skip return validation if return spec was not defined */
+ if (spec->return_magic != KAPI_MAGIC_RETURN)
+ return 0;
+
+ if (!kapi_validate_return_value(spec, retval)) {
+ /* Log the violation but don't change the return value */
+ pr_warn_ratelimited("KAPI: Syscall %s returned unspecified value %lld\n",
+ spec->name, retval);
+ }
+
+ 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)
+{
+ bool valid = false;
+ u32 ctx;
+
+ if (!spec)
+ return;
+
+ ctx = spec->context_flags;
+
+ 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 */
--
2.53.0
next prev parent reply other threads:[~2026-10-08 8:50 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-08 8:49 [PATCH v5 00/11] Kernel API Specification Framework Sasha Levin
2026-10-08 8:49 ` Sasha Levin [this message]
2026-10-08 8:49 ` [PATCH v5 02/11] kernel/api: enable kerneldoc-based API specifications Sasha Levin
2026-10-08 8:49 ` [PATCH v5 03/11] kernel/api: add debugfs interface for kernel " Sasha Levin
2026-10-08 8:49 ` [PATCH v5 04/11] tools/kapi: add kernel API specification extraction tool Sasha Levin
2026-10-08 8:49 ` [PATCH v5 05/11] kernel/api: add API specification for sys_open Sasha Levin
2026-10-08 12:49 ` Serge E. Hallyn
2026-10-08 13:13 ` Gregory Price
2026-10-08 14:20 ` Serge E. Hallyn
2026-10-08 14:37 ` Sasha Levin
2026-10-08 14:46 ` Serge E. Hallyn
2026-10-08 15:23 ` Sasha Levin
2026-10-08 16:12 ` David Laight
2026-10-08 16:16 ` Serge E. Hallyn
2026-10-08 22:47 ` Sasha Levin
2026-10-08 8:49 ` [PATCH v5 06/11] kernel/api: add API specification for sys_close Sasha Levin
2026-10-08 8:49 ` [PATCH v5 07/11] kernel/api: add API specification for sys_read Sasha Levin
2026-10-08 8:49 ` [PATCH v5 08/11] kernel/api: add API specification for sys_write Sasha Levin
2026-10-08 8:49 ` [PATCH v5 09/11] kernel/api: add runtime verification selftest Sasha Levin
2026-10-08 8:49 ` [PATCH v5 10/11] kernel/api: add API specification for sys_madvise Sasha Levin
2026-10-08 8:49 ` [PATCH v5 11/11] kernel/api: add syscall enter/exit tracepoints Sasha Levin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261008084956.2911790-2-sashal@kernel.org \
--to=sashal@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=arnd@arndb.de \
--cc=brauner@kernel.org \
--cc=chrubis@suse.cz \
--cc=corbet@lwn.net \
--cc=david.laight.linux@gmail.com \
--cc=dvyukov@google.com \
--cc=gpaoloni@redhat.com \
--cc=gregkh@linuxfoundation.org \
--cc=jake@lwn.net \
--cc=kees@kernel.org \
--cc=linux-api@vger.kernel.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kbuild@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=masahiroy@kernel.org \
--cc=mathieu.desnoyers@efficios.com \
--cc=mchehab@kernel.org \
--cc=mhiramat@kernel.org \
--cc=nathan@kernel.org \
--cc=paulmck@kernel.org \
--cc=rdunlap@infradead.org \
--cc=rostedt@goodmis.org \
--cc=skhan@linuxfoundation.org \
--cc=tglx@kernel.org \
--cc=tools@kernel.org \
--cc=viro@zeniv.linux.org.uk \
--cc=workflows@vger.kernel.org \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.