Appendix 2.B KMES ABI Notes

§2.A is generated from pkm/uapi/pkm/kmes.h and holds only what a compiler can measure. This appendix holds the rest.

The split is structural rather than editorial. gen-kmes-abi.py overwrites §2.A wholesale on every run, so anything written there is lost the next time the ABI changes.

Layouts that form part of the consumer contract — the event header, the producer and consumer metadata pages, and the mapped region — are specified normatively in the PSPK event stream specification. §2.A gives their offsets as the header defines them; the specification governs.

2.B.1 Syscall parameters #

SyscallParameterTypeMeaning
kmes_emitevent_typeconst char *Event type string.
event_type_lenu16Its length in bytes.
payloadconst void *MessagePack payload.
payload_lenu32Its length in bytes.
kmes_emit_batchentriesstruct kmes_emit_entry *Array of event descriptors.
countu32Number of entries, 1 to KMES_BATCH_MAX_ENTRIES.
emitted_outu32 *Receives the number of events emitted.
kmes_attachcpu_idunsigned intRing slot index, or KMES_ATTACH_QUERY_SLOTS to query the slot count.
capacityu64 *Receives the ring buffer capacity in bytes.

kmes_emit and kmes_emit_batch return 0 on success; kmes_attach returns a file descriptor. All three return -1 and set errno on failure.

The PKM syscall range is 1090–1099; KMES uses the first three.

2.B.2 Privilege requirements #

kmes.h gives these as bit masks so it can stand alone. The names belong to the KACS privilege catalogue, and the bit index is what the two agree on.

OperationRequired privilegeBit
kmes_emit, kmes_emit_batchSeAuditPrivilege21
Rate-limit exemption on bothSeTcbPrivilege7
kmes_attachSeSecurityPrivilege8

Holding a privilege is not enough: it must be enabled, and KMES marks it used before proceeding. A failure to record the used state is itself an EPERM, because an unrecorded privilege use is an audit gap.

SeTcbPrivilege is checked but not required — an emitter that holds it enabled is exempt from the per-process rate limit, and one that does not is throttled.

2.B.3 Implementation bounds #

These are properties of the implementation rather than of the ABI, so they are not in kmes.h and a program must not compile against them. They bound what the interface will accept.

QuantityValueWhere
Maximum event size, structural50% of ring capacitykmes/kmes.c
MessagePack validator nesting stack256kmes/kmes_validate.rs
Self-configuration payload buffer768 byteskmes/kmes.c
Self-configuration audit intents per read4kmes/kmes.h
Self-configuration parameter name64 byteskmes/kmes.h

The structural 50% bound is independent of MaxEventSize and applies to kernel emitters too. An event may satisfy the configured maximum and still be refused because the ring is small.

The validator's 256-frame stack is why MaxNestingDepth has a maximum of 256. The two bounds are enforced independently: the configuration range check refuses a larger value at apply time, and the validator refuses every event outright if it is somehow handed one, rather than silently accepting nesting it cannot track.

2.B.4 Configuration keys #

Registry path Machine\System\KMES\. The type codes, defaults and ranges are in §2.A; what each key does is here.

KeyEffect
BufferCapacityPer-CPU ring size in bytes. Must be a power of two. Changing it swaps every ring; see §2.6.
MaxEventSizeLargest event a syscall emitter may produce.
MaxNestingDepthDeepest MessagePack container nesting the validator will accept.
MaxEmitRatePerProcessToken-bucket rate, events per second, per process.

MaxEventSize, MaxNestingDepth and MaxEmitRatePerProcess apply only to syscall emitters. A kernel emitter is not rate-limited and its payload is not parsed as MessagePack, but it is still checked structurally — non-empty type string within PKM_KMES_MAX_KERNEL_TYPE_LEN, a payload pointer if the length is non-zero, no size arithmetic overflow, and the 50% bound. A kernel event that fails is dropped with KMES_DROP_VALIDATE, not emitted.

A key that is missing, of the wrong type, or out of range does not fail the read: the previous value is retained and the disagreement is recorded as an audit intent. At most four such intents are carried out of one read, so a configuration with five bad keys reports four.

2.B.5 Error codes #

2.B.5.1 kmes_emit #

ErrnoCondition
EPERMSeAuditPrivilege not held or not enabled, or recording its used state failed.
EAGAINPer-process rate limit exceeded.
EINVALZero event type length, event type not valid UTF-8, declared size arithmetic overflowed, payload not valid msgpack, or nesting depth over MaxNestingDepth.
EFAULTEvent type or payload pointer inaccessible.
ENOSPCEvent exceeds MaxEventSize or 50% of ring capacity.
ENOMEMStaging buffer allocation failed, or KMES not initialised.

2.B.5.2 kmes_emit_batch #

ErrnoCondition
EPERMAs kmes_emit.
EAGAINFewer than count tokens available.
EINVALcount is 0 or over KMES_BATCH_MAX_ENTRIES, or the failing entry hit one of kmes_emit's EINVAL conditions.
EFAULTemitted_out, the entry array, or the failing entry's type or payload pointer inaccessible.
ENOSPCThe failing entry exceeds MaxEventSize or 50% of ring capacity.
ENOMEMKernel allocation failed, or KMES not initialised.

2.B.5.3 kmes_attach #

ErrnoCondition
EPERMSeSecurityPrivilege not held or not enabled, or recording its used state failed.
EINVALcpu_id at or beyond the ring array size, or its slot holds no live ring.
EFAULTcapacity pointer inaccessible.
ENOMEMKernel allocation failed, or KMES not initialised.

A KMES_ATTACH_QUERY_SLOTS call takes the same EPERM, EFAULT and ENOMEM conditions and cannot return EINVAL.

The ring array is sized by nr_cpu_ids, not by the number of rings allocated. On a machine with a sparse possible-CPU mask the two differ, and a slot inside the array with no live ring returns EINVAL exactly as an index beyond the array does. A consumer therefore enumerates against the slot count from KMES_ATTACH_QUERY_SLOTS and skips the EINVAL slots rather than stopping at the first one; see §2.4.

2.B.6 Build configuration #

KMES is built by CONFIG_SECURITY_PKM, a boolean option, so it is linked into vmlinux rather than loaded. CONFIG_RUST=y is required: the MessagePack validator is Rust. The whole subsystem is staged into the kernel tree as security/pkm/kmes by pkm/kernel/stage-sources.sh, which also stages <trace/events/kmes.h> so the tracepoints resolve.

The three syscall numbers are added to the syscall table by kernel/patches/arch/syscall-table-pkm.patch, which patches both arch/x86/entry/syscalls/syscall_64.tbl and the copy of it that ships under tools/perf/. They are registered common, so they are reachable from the x32 ABI as well as from x86-64.

CONFIG_SECURITY_PKM_KUNIT compiles in the in-kernel test harness.

Edit this page