Skip to content
HN On Hacker News ↗

add per-function short-circuit mitigation primitive [LWN.net]

▲ 87 points 24 comments by signa11 4mo ago HN discussion ↗

Pangram verdict · v3.3

We believe that this document is primarily human-written, with a small amount of AI content detected

17 %

AI likelihood · overall

Human
95% human-written 5% AI-generated
SEGMENTS · HUMAN 3 of 7
SEGMENTS · AI 0 of 7
WORD COUNT 1,400
PEAK AI % 63% · §4
Analyzed
May 9
backend: pangram/v3.3
Segments scanned
7 windows
avg 200 words each
Distribution
95 / 5%
human / AI fraction
Verdict
Human
Pangram v3.3

Article text · 1,400 words · 7 segments analyzed

Human AI-generated
§1 Human · 2%

Thread information [Search the all archive] From: Sasha Levin <sashal-AT-kernel.org> To: corbet-AT-lwn.net, akpm-AT-linux-foundation.org Subject: [PATCH] killswitch: add per-function short-circuit mitigation primitive Date: Thu, 07 May 2026 03:05:45 -0400 Message-ID: <20260507070547.2268452-1-sashal@kernel.org> Cc: skhan-AT-linuxfoundation.org, linux-doc-AT-vger.kernel.org, linux-kernel-AT-vger.kernel.org, linux-kselftest-AT-vger.kernel.org, gregkh-AT-linuxfoundation.org, Sasha Levin <sashal-AT-kernel.org> When a (security) issue goes public, fleets stay exposed until a patched kernel is built, distributed, and rebooted into. For many such issues the simplest mitigation is to stop calling the buggy function. Killswitch provides that. An admin writes: echo "engage af_alg_sendmsg -1" \ > /sys/kernel/security/killswitch/control After this, af_alg_sendmsg() returns -EPERM on every call without running its body. The mitigation takes effect immediately, and is dropped on the next reboot. A lot of recent kernel issues sit in code paths most installs only have enabled to support a relative minority of users: AF_ALG, ksmbd, nf_tables, vsock, ax25, and friends. For most users, the cost of "this socket family stops working for the day" is much smaller than the cost of running a known vulnerable kernel until the fix land.

§2 Human · 5%

Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Sasha Levin <sashal@kernel.org> --- Documentation/admin-guide/index.rst | 1 + Documentation/admin-guide/killswitch.rst | 159 ++++ Documentation/admin-guide/tainted-kernels.rst | 8 + MAINTAINERS | 11 + include/linux/killswitch.h | 19 + include/linux/panic.h | 3 +- init/Kconfig | 2 + kernel/Kconfig.killswitch | 31 + kernel/Makefile | 1 + kernel/killswitch.c | 798 ++++++++++++++++++ kernel/panic.c | 1 + lib/Kconfig.debug | 13 + lib/Makefile | 1 + lib/test_killswitch.c | 85 ++ tools/testing/selftests/Makefile | 1 + tools/testing/selftests/killswitch/.gitignore | 1 + tools/testing/selftests/killswitch/Makefile | 8 + .../selftests/killswitch/cve_31431_test.c | 162 ++++ .../selftests/killswitch/killswitch_test.sh | 147 ++++ 19 files changed, 1451 insertions(+), 1 deletion(-) create mode 100644 Documentation/admin-guide/killswitch.rst create mode 100644 include/linux/killswitch.h create mode 100644 kernel/Kconfig.killswitch create mode 100644 kernel/killswitch.c create mode 100644 lib/test_killswitch.c create mode 100644 tools/testing/selftests/killswitch/.gitignore create mode 100644 tools/testing/selftests/killswitch/Makefile create

§3 Mixed · 42%

mode 100644 tools/testing/selftests/killswitch/cve_31431_test.c create mode 100755 tools/testing/selftests/killswitch/killswitch_test.sh diff --git a/Documentation/admin-guide/index.rst b/Documentation/admin-guide/index.rst index cd28dfe91b060..ca37dd70f108d 100644 --- a/Documentation/admin-guide/index.rst +++ b/Documentation/admin-guide/index.rst @@ -70,6 +70,7 @@ problems and bugs in particular. bug-hunting bug-bisect tainted-kernels + killswitch ramoops dynamic-debug-howto init diff --git a/Documentation/admin-guide/killswitch.rst b/Documentation/admin-guide/killswitch.rst new file mode 100644 index 0000000000000..cb967ec348fdc --- /dev/null +++ b/Documentation/admin-guide/killswitch.rst @@ -0,0 +1,159 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. +.. Copyright (C) 2026 Sasha Levin <sashal@kernel.org> + +============ +Killswitch +============ + +Killswitch lets a privileged operator make a chosen kernel function +return a fixed value without executing its body, as a temporary +mitigation for a security bug while a real fix is being prepared. + +The function returns the operator-supplied value and nothing else +runs in its place. There is no allowlist, no return-type check; if +the kprobe layer accepts the symbol, killswitch engages it. Once +engaged, the change is in effect on every CPU until ``disengage`` is +written or the system reboots. + +Configuration +============= + +``CONFIG_KILLSWITCH`` + Enables the feature. Depends on ``SECURITYFS``, ``KPROBES`` (with + ftrace support), and ``FUNCTION_ERROR_INJECTION``.

§4 Mixed · 63%

+ +The interface +============= + +:: + + /sys/kernel/security/killswitch/ + engaged RO currently-engaged functions + control WO command sink + taint RO 0 or 1 + fn/<name>/ per-function directory, created on engage + retval RW return value + hits RO per-cpu summed call count + +Three commands are accepted by ``control``:: + + engage <symbol> <retval> + disengage <symbol> + disengage_all + +Each engage and disengage emits a single ``KERN_WARNING`` line to +dmesg with the symbol, retval, hit count (on disengage), and the +operator's identity (uid/auid/sessionid/comm, or ``source=cmdline``). + +Engagement is rejected when: + +* the symbol is unknown, in a non-traceable section, on the kprobe + blacklist, or otherwise refused by ``register_kprobe`` (the error + from the kprobe layer is logged and returned to userspace); +* the symbol is already engaged (``-EBUSY``); +* the operator does not hold ``CAP_SYS_ADMIN``. + +Whatever value the operator writes is what the function returns. +Writing the wrong type or wrong value lands in the caller as-is. + +Boot parameter +============== + +``killswitch=fn1=<val>,fn2=<val>,...`` + +Parsed early; engagements are applied at the end of kernel init +once the kprobe subsystem is up. Parse failures emit a warning and +skip the offending entry; they never panic. + +Useful for fleet rollout: when an issue drops, ship the mitigation +in the bootloader / PXE config and roll the fleet through reboots +while the real fix is being prepared. + +Tainting +======== + +The first successful engagement (runtime or boot-time) sets +``TAINT_KILLSWITCH`` (bit 20, char ``H``). The taint persists across +``disengage`` until reboot, so an oops on a killswitch-modified +kernel is identifiable from the banner: ``Tainted: ... H`` tells a +maintainer to consult ``engaged`` before further triage.

§5 Mixed · 48%

+ +Module unload +============= + +If a module containing an engaged target is unloaded, killswitch +auto-disengages the entry and emits a ``KERN_WARNING`` so the loss +of mitigation is visible. Reloading the module does not silently +re-arm the killswitch; the operator re-engages explicitly. + +Choosing the right target +========================= + +A function that *looks* skippable may be relied on by callers for a +side effect (a lock the caller releases, a refcount the caller +drops, a scatterlist the caller consumes). The rule of thumb: + + Pick the **highest-level** entry point that contains the bug. + +That gives callers no chance to dereference half-initialised state +from a function whose body was skipped. Two illustrative examples +from ``crypto/af_alg.c``: + +Anti-pattern: ``af_alg_count_tsgl`` +----------------------------------- + +``af_alg_count_tsgl()`` returns ``unsigned int`` (the number of TX +SG entries). Engaging it with retval ``0`` causes the caller in +``algif_aead.c`` to allocate a 1-entry scatterlist (its +``if (!entries) entries = 1`` guard) and then walk the *real* TX +SGL into that undersized destination via ``af_alg_pull_tsgl``, +producing out-of-bounds writes. **Killswitching here introduces a +worse bug than the one being mitigated.** + +Anti-pattern: ``af_alg_pull_tsgl`` +---------------------------------- + +``af_alg_pull_tsgl()`` returns ``void``, so any retval is accepted. +But its caller depends on the per-request SGL being filled in. +Skipping the body leaves the per-request SGL with NULL pages; the +next-stage ``memcpy_sglist`` dereferences them and the kernel +oopses. + +Correct pattern: ``af_alg_sendmsg`` +----------------------------------- + +``af_alg_sendmsg()`` is the highest-level entry into the AF_ALG +send path.

§6 Mixed · 34%

Engaging it with retval ``-EPERM`` causes every send +attempt to return -EPERM to userspace; no caller ever sees +half-initialised state, and any AF_ALG-reachable bug downstream of +``sendmsg`` is unreachable until the killswitch is disengaged. + +The canonical pattern: pick a syscall-handler-shaped function whose +return value already encodes "this operation didn't happen", and +let userspace handle the error as it would any other failed +syscall. + +Safety notes +============ + +* In-flight calls during ``write()`` to ``control`` may run either + the original body or the override. The override is ``return X``, + which has no preconditions to violate. +* SMP visibility comes from ``text_poke_bp()``. ``write()`` to + ``control`` returns only after every CPU sees the new path. +* The ftrace ops unregister waits for in-flight pre-handlers, so + freeing the engagement attribute on disengage is safe. +* Inline functions, freed ``__init`` symbols, and anything compiled + away cannot be killswitched. ``register_kprobe`` rejects them + with whatever error the kprobe layer chooses. + +Diagnostics +=========== + +Per-call hits are aggregated in a per-cpu counter readable at +``/sys/kernel/security/killswitch/fn/<name>/hits``. Per-hit logging +is not provided to avoid log storms on hot paths. + +A ``KILLSWITCH`` entry appears in the kernel taint vector once any +engagement succeeds (also visible as ``H`` in the oops banner). diff --git a/Documentation/admin-guide/tainted-kernels.rst b/Documentation/admin-guide/tainted-kernels.rst index 9ead927a37c0f..71a6e3364eddc 100644 --- a/Documentation/admin-guide/tainted-kernels.rst +++

§7 Human · 10%

b/Documentation/admin-guide/tainted-kernels.rst @@ -102,6 +102,7 @@ Bit Log Number Reason that got the kernel tainted 17 _/T 131072 kernel was built with the struct randomization plugin 18 _/N 262144 an in-kernel test has been run 19 _/J 524288 userspace used a mutating debug operation in fwctl + 20 _/H 1048576 killswitch override engaged (function short-circuited) === === ====== ======================================================== Note: The character ``_`` is representing a blank in this table to make reading @@ -189,3 +190,10 @@ More detailed explanation for tainting 19) ``J`` if userspace opened /dev/fwctl/* and performed a FWTCL_RPC_DEBUG_WRITE to use the devices debugging features. Device debugging features could cause the device to malfunction in undefined ways. + + 20) ``H`` if the killswitch primitive (see + Documentation/admin-guide/killswitch.rst) has been engaged on at least + one function. The kernel is no longer running its source: at least one + function has been short-circuited to return a fixed value. The taint + persists across ``disengage`` until the next reboot — once the running + image has been modified, oops triage must reflect that. diff --git a/MAINTAINERS b/MAINTAINERS index 882214b0e7db5..61851ef1d9b1c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14347,6 +14347,17 @@ F: lib/Kconfig.kmsan F: mm/kmsan/ F: scripts/Makefile.kmsan +KILLSWITCH (function short-circuit mitigation) +M: Sasha Levin <sashal@kernel.org> +L: linux-kernel@vger.kernel.org