Kernel API Specification Framework¶
- Author:
Sasha Levin <sashal@kernel.org>
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:
Improve API Documentation: Provide structured, inline documentation that lives alongside the code and is maintained as part of the development process.
Enable Runtime Validation: Optionally validate API usage at runtime to catch common programming errors during development and testing.
Support Tooling: Export API specifications in machine-readable formats for use by static analyzers, documentation generators, and development tools. See The kapi Tool.
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:
Core Framework (
kernel/api/kernel_api_spec.c)API specification registration and storage
Runtime validation engine
Specification lookup and querying
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
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:
/**
* 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|-joinedKAPI_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(oruptr),struct,union,enum,func_ptr,array,custom. Flag aliases:input,output,inout,user,optional,const,volatile,dma,aligned.constraint-type:— aKAPI_CONSTRAINT_*enum tokenor 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_stringtakes its length limits fromrange:. The function-call form populates the matching aux fields (range:/valid-mask:/size-param:).arch-mask:— extends amask(...)constraint with bits that are valid only on a named architecture. The form isarch-mask: <arch> = <bits-expr>and may appear multiple times. The named arch must be one of the known short names (alpha,arc,arm,arm64,csky,hexagon,loongarch,m68k,microblaze,mips,nios2,openrisc,parisc,powerpc,riscv,s390,sh,sparc,um,x86,xtensa); the generator translates it to the matchingCONFIG_*symbol and emits the bits inside an#ifdefso a single generated apispec.h compiles on every architecture and folds in the arch-specific bits at compile time. Use this for PROT/MAP bits whose UAPI symbols are defined only on the matching arch.lock: … type:acceptsmutex,spinlock,rwlock,seqlock,rcu,semaphore,customorKAPI_LOCK_*.signal: … direction:acceptsreceive,send,handle,block,ignore(bitmask, joinable with|or,).signal: … action:acceptsdefault,terminate,coredump,stop,continue,custom,return,restart,queue,discard,transform.signal: … timing:acceptsbefore,during,after.capability: … type:acceptsbypass_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 exampleside-effect: resource_create | alloc_memory.return: … type:reuses thetype:aliases above.return: … check-type:acceptsexact,range,fd,no_return.success:gives the success value forexact(an integer, optionally written= N) and the lower bound forrange(>= N); the other check types do not use it. Thetype:of a return block is also kept as written (for exampleint) in the human-readabletype_nameofstruct kapi_return_spec.error:takes aNAME, one-line summaryheader followed by optional indenteddesc:/condition:subfields.lock:andsignal:take an indenteddesc:subfield for the long-form description;signal:also acceptsnumber:(the signal constant, for exampleSIGPIPE),errno:,priority:,restartable:,interruptible:, andqueue:subfields.state-trans:takesfrom:,to:,object:, optionalcondition:, anddesc:subfields. The condition is stored in its own field, apart from the description.long-desc:is a free-form multi-line prose block that populateslong_descriptionin 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\nin 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 countsparam: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:
Enable
CONFIG_KAPI_SPECto build the frameworkEnable
CONFIG_KAPI_RUNTIME_CHECKSfor runtime validationOptionally enable
CONFIG_KAPI_SPEC_DEBUGFSfor debugfs interface
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().
Warning
Userspace errno is affected when this option is on. For syscalls that
violate their parameter specification, KAPI short-circuits the call and
returns -EINVAL from the validator before the real handler runs.
That errno can differ from what the real handler would have produced for
the same condition (for example, -ENOMEM from an allocation path or
-EFAULT from a deeper copy-in). CONFIG_KAPI_RUNTIME_CHECKS is a
debug-only option; do not enable it on production kernels or in
userspace-visible test environments where error-code fidelity matters.
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:
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,
Tracepoints¶
When CONFIG_KAPI_RUNTIME_CHECKS is enabled, the syscall validation path emits
two ftrace tracepoints (in the kapi trace system) for every syscall that has a
specification:
kapi_syscall_enter-- fired before parameter validation, recording the spec name, the raw syscall argument values, and -- when the spec provides parameter metadata -- a renderedname=valuelist: signed integer and file descriptor values are shown in decimal, all other values in hex, and an unnamed parameter asarg.kapi_syscall_exit-- fired after the handler returns, or in place of the handler when parameter validation rejects the call (the handler is skipped and-EINVALis returned). Records the spec name, the return value, andspec_match: 0 when the parameters were rejected or a successfulfdreturn was not a valid file descriptor, and 1 otherwise. Error codes missing from the spec do not clear it.
Unlike the pr_warn_ratelimited violation reports, the tracepoints capture every
spec’d call rather than only violations, and can be filtered with the usual ftrace
facilities. They require CONFIG_TRACEPOINTS and stay dormant
until enabled:
# echo 1 > /sys/kernel/tracing/events/kapi/enable
# cat /sys/kernel/tracing/trace
... kapi_syscall_enter: sys_read(fd=3, buf=0x7ffd46780b58, count=0x340)
... kapi_syscall_exit: sys_read = 832 spec_match=1
... kapi_syscall_enter: sys_open(filename=0x480300, flags=268435456, mode=0x0)
... kapi_syscall_exit: sys_open = -22 spec_match=0
DebugFS Interface¶
The debugfs interface provides runtime access to API specifications:
Directory Structure¶
/sys/kernel/debug/kapi/
├── list # Overview of all registered API specs
├── specs/ # Per-API specification files
│ ├── sys_open # Human-readable spec for sys_open
│ ├── sys_close # Human-readable spec for sys_close
│ ├── sys_read # Human-readable spec for sys_read
│ ├── sys_write # Human-readable spec for sys_write
│ └── sys_madvise # Human-readable spec for sys_madvise
└── specs-json/ # Machine-readable counterpart of specs/
├── sys_open # JSON spec for sys_open
└── ...
Usage Examples¶
List all available API specifications:
$ cat /sys/kernel/debug/kapi/list
Available Kernel API Specifications
===================================
sys_open - Open or create a file
sys_close - Close a file descriptor
sys_read - Read data from a file descriptor
sys_write - Write data to a file descriptor
sys_madvise - Give advice about use of memory
Total: 5 specifications
Query specific API:
$ cat /sys/kernel/debug/kapi/specs/sys_open
Kernel API Specification
========================
Name: sys_open
Version: 1
Description: Open or create a file
...
The specs-json/ files carry the complete specification: for each
parameter the type class, flags and the constraint (type, range, valid
mask, enumerated values, alignment, size and the index of the parameter
holding a buffer’s size), the return check, errors, locks, signals,
signal masks, side effects, state transitions, capabilities, additional
constraints and structure specifications. Masks and flag words are
hex strings, enumerations are lower-case tokens such as
"constraint_type": "buffer", and size_param_idx is a 0-based
index into parameters (null when unused). The kapi tool
reads these files with --debugfs and reports the same data as
--vmlinux.
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:
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 commentVmlinux: Reads the
.kapi_specsELF section from compiled kernelsDebugfs: 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)
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¶
Add specifications to the same file as the API implementation
Follow existing patterns and naming conventions
Test with CONFIG_KAPI_RUNTIME_CHECKS enabled
Verify debugfs output is correct
Run scripts/checkpatch.pl on your changes