* [RFC PATCH v2] bpf: introduce TAINT_UNSAFE_BPF for mutating helpers
@ 2026-05-03 15:37 Aaron Tomlin
2026-05-03 16:13 ` bot+bpf-ci
0 siblings, 1 reply; 3+ messages in thread
From: Aaron Tomlin @ 2026-05-03 15:37 UTC (permalink / raw)
To: corbet, song, kpsingh, mattbobrowski, ast, daniel, andrii,
eddyz87, memxor, rostedt, mhiramat
Cc: skhan, jolsa, martin.lau, yonghong.song, mathieu.desnoyers,
rdunlap, atomlin, neelx, sean, chjohnst, steve, mproche,
nick.lange, linux-doc, linux-kernel, bpf, linux-trace-kernel
The primary remit of the eBPF verifier is to ensure that eBPF programs
can neither crash the kernel nor corrupt memory. Nevertheless,
administrative utilities such as "bpftrace --unsafe" permit the loading
of programs that employ destructive or mutating helpers, most notably
bpf_probe_write_user() and bpf_override_return().
Since commit b28573ebfabe ("bpf: Remove bpf_probe_write_user() warning
message"), the kernel no longer issues a warning when an attempt is made to
invoke such destructive helpers.
Consequently, this patch introduces a novel kernel taint flag,
TAINT_UNSAFE_BPF ("V"). Tainting the kernel establishes a permanent and
readily auditable indicator (i.e., /proc/sys/kernel/tainted) to alert
maintainers that the kernel's execution flow or user memory may have been
compromised by an eBPF program.
Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
---
Changes since v1 [1]:
- Moved the taint from run-time execution to load-time verification
- Added "V" flag decoding to tools/debugging/kernel-chktaint
(Randy Dunlap)
- Updated the seq command in tainted-kernels.rst to check all 21 bits
(Randy Dunlap)
- Fixed a Sphinx "Malformed table" warning by expanding the number
column boundaries in tainted-kernels.rst
[1]: https://lore.kernel.org/lkml/20260503035220.520479-1-atomlin@atomlin.com/
---
Documentation/admin-guide/tainted-kernels.rst | 56 ++++++++++---------
include/linux/panic.h | 3 +-
kernel/bpf/verifier.c | 8 +++
kernel/panic.c | 1 +
tools/debugging/kernel-chktaint | 8 +++
5 files changed, 50 insertions(+), 26 deletions(-)
diff --git a/Documentation/admin-guide/tainted-kernels.rst b/Documentation/admin-guide/tainted-kernels.rst
index 9ead927a37c0..d26a8d29808c 100644
--- a/Documentation/admin-guide/tainted-kernels.rst
+++ b/Documentation/admin-guide/tainted-kernels.rst
@@ -74,35 +74,36 @@ a particular type of taint. It's best to leave that to the aforementioned
script, but if you need something quick you can use this shell command to check
which bits are set::
- $ for i in $(seq 20); do echo $(($i-1)) $(($(cat /proc/sys/kernel/tainted)>>($i-1)&1));done
+ $ for i in $(seq 21); do echo $(($i-1)) $(($(cat /proc/sys/kernel/tainted)>>($i-1)&1));done
Table for decoding tainted state
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-=== === ====== ========================================================
-Bit Log Number Reason that got the kernel tainted
-=== === ====== ========================================================
- 0 G/P 1 proprietary module was loaded
- 1 _/F 2 module was force loaded
- 2 _/S 4 kernel running on an out of specification system
- 3 _/R 8 module was force unloaded
- 4 _/M 16 processor reported a Machine Check Exception (MCE)
- 5 _/B 32 bad page referenced or some unexpected page flags
- 6 _/U 64 taint requested by userspace application
- 7 _/D 128 kernel died recently, i.e. there was an OOPS or BUG
- 8 _/A 256 ACPI table overridden by user
- 9 _/W 512 kernel issued warning
- 10 _/C 1024 staging driver was loaded
- 11 _/I 2048 workaround for bug in platform firmware applied
- 12 _/O 4096 externally-built ("out-of-tree") module was loaded
- 13 _/E 8192 unsigned module was loaded
- 14 _/L 16384 soft lockup occurred
- 15 _/K 32768 kernel has been live patched
- 16 _/X 65536 auxiliary taint, defined for and used by distros
- 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
-=== === ====== ========================================================
+=== === ======= ========================================================
+Bit Log Number Reason that got the kernel tainted
+=== === ======= ========================================================
+ 0 G/P 1 proprietary module was loaded
+ 1 _/F 2 module was force loaded
+ 2 _/S 4 kernel running on an out of specification system
+ 3 _/R 8 module was force unloaded
+ 4 _/M 16 processor reported a Machine Check Exception (MCE)
+ 5 _/B 32 bad page referenced or some unexpected page flags
+ 6 _/U 64 taint requested by userspace application
+ 7 _/D 128 kernel died recently, i.e. there was an OOPS or BUG
+ 8 _/A 256 ACPI table overridden by user
+ 9 _/W 512 kernel issued warning
+ 10 _/C 1024 staging driver was loaded
+ 11 _/I 2048 workaround for bug in platform firmware applied
+ 12 _/O 4096 externally-built ("out-of-tree") module was loaded
+ 13 _/E 8192 unsigned module was loaded
+ 14 _/L 16384 soft lockup occurred
+ 15 _/K 32768 kernel has been live patched
+ 16 _/X 65536 auxiliary taint, defined for and used by distros
+ 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 _/V 1048576 an unsafe eBPF program (mutating helper) was loaded
+=== === ======= ========================================================
Note: The character ``_`` is representing a blank in this table to make reading
easier.
@@ -189,3 +190,8 @@ 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) ``V`` if an eBPF program utilising unsafe, mutating helpers (such as
+ bpf_probe_write_user() or bpf_override_return()) was loaded. These helpers
+ bypass standard eBPF safety guarantees and can alter execution flow or
+ corrupt memory.
diff --git a/include/linux/panic.h b/include/linux/panic.h
index f1dd417e54b2..8622c02c2c24 100644
--- a/include/linux/panic.h
+++ b/include/linux/panic.h
@@ -88,7 +88,8 @@ static inline void set_arch_panic_timeout(int timeout, int arch_default_timeout)
#define TAINT_RANDSTRUCT 17
#define TAINT_TEST 18
#define TAINT_FWCTL 19
-#define TAINT_FLAGS_COUNT 20
+#define TAINT_UNSAFE_BPF 20
+#define TAINT_FLAGS_COUNT 21
#define TAINT_FLAGS_MAX ((1UL << TAINT_FLAGS_COUNT) - 1)
struct taint_flag {
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 69d75515ed3f..cf0634d8901f 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -10287,6 +10287,14 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
return err;
}
+ /*
+ * Taint the kernel if the program attempts to use mutating
+ * helpers.
+ */
+ if (func_id == BPF_FUNC_probe_write_user ||
+ func_id == BPF_FUNC_override_return)
+ add_taint(TAINT_UNSAFE_BPF, LOCKDEP_STILL_OK);
+
/* eBPF programs must be GPL compatible to use GPL-ed functions */
if (!env->prog->gpl_compatible && fn->gpl_only) {
verbose(env, "cannot call GPL-restricted function from non-GPL compatible program\n");
diff --git a/kernel/panic.c b/kernel/panic.c
index 20feada5319d..1ae19bd8fc1d 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -825,6 +825,7 @@ const struct taint_flag taint_flags[TAINT_FLAGS_COUNT] = {
TAINT_FLAG(RANDSTRUCT, 'T', ' '),
TAINT_FLAG(TEST, 'N', ' '),
TAINT_FLAG(FWCTL, 'J', ' '),
+ TAINT_FLAG(UNSAFE_BPF, 'V', ' '),
};
#undef TAINT_FLAG
diff --git a/tools/debugging/kernel-chktaint b/tools/debugging/kernel-chktaint
index e1571c04afb5..c0fbd7bcfcfd 100755
--- a/tools/debugging/kernel-chktaint
+++ b/tools/debugging/kernel-chktaint
@@ -211,6 +211,14 @@ else
addout "J"
echo " * fwctl's mutating debug interface was used (#19)"
fi
+
+T=`expr $T / 2`
+if [ `expr $T % 2` -eq 0 ]; then
+ addout " "
+else
+ addout "V"
+ echo " * an unsafe eBPF program (mutating helper) was loaded (#20)"
+fi
echo "Raw taint value as int/string: $taint/'$out'"
# report on any tainted loadable modules
--
2.51.0
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [RFC PATCH v2] bpf: introduce TAINT_UNSAFE_BPF for mutating helpers
2026-05-03 15:37 [RFC PATCH v2] bpf: introduce TAINT_UNSAFE_BPF for mutating helpers Aaron Tomlin
@ 2026-05-03 16:13 ` bot+bpf-ci
2026-05-03 16:40 ` Aaron Tomlin
0 siblings, 1 reply; 3+ messages in thread
From: bot+bpf-ci @ 2026-05-03 16:13 UTC (permalink / raw)
To: atomlin, corbet, song, kpsingh, mattbobrowski, ast, daniel,
andrii, eddyz87, memxor, rostedt, mhiramat
Cc: skhan, jolsa, martin.lau, yonghong.song, mathieu.desnoyers,
rdunlap, atomlin, neelx, sean, chjohnst, steve, mproche,
nick.lange, linux-doc, linux-kernel, bpf, linux-trace-kernel, ast,
andrii, daniel, martin.lau, eddyz87, yonghong.song, clm,
ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 2066 bytes --]
> diff --git a/Documentation/admin-guide/tainted-kernels.rst b/Documentation/admin-guide/tainted-kernels.rst
[ ... ]
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 11054ad89c14..e2060c6f3e13 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -9906,6 +9906,14 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
> return err;
> }
>
> + /*
> + * Taint the kernel if the program attempts to use mutating
> + * helpers.
> + */
> + if (func_id == BPF_FUNC_probe_write_user ||
> + func_id == BPF_FUNC_override_return)
> + add_taint(TAINT_UNSAFE_BPF, LOCKDEP_STILL_OK);
> +
Is it possible for the kernel to be tainted here even if the program is
ultimately rejected by the verifier? check_helper_call() is executed during
the verifier's static analysis phase. If the program fails verification later
(for example, failing the GPL-compatibility check immediately following this
code, or encountering an invalid memory access), the program is safely
rejected and never loaded.
Because add_taint() synchronously modifies global state, wouldn't the kernel
remain permanently tainted in this scenario, falsely indicating a compromised
state?
Furthermore, if panic_on_taint is enabled, could a user trigger a panic merely
by attempting to load a malformed program that contains these helpers?
Could this be addressed by recording the intent to taint within the program's
auxiliary data during verification, and only invoking add_taint() once the
program has successfully passed all checks and is definitively loaded?
> /* eBPF programs must be GPL compatible to use GPL-ed functions */
> if (!env->prog->gpl_compatible && fn->gpl_only) {
> verbose(env, "cannot call GPL-restricted function from non-GPL compatible program\n");
[ ... ]
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/25283723455
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [RFC PATCH v2] bpf: introduce TAINT_UNSAFE_BPF for mutating helpers
2026-05-03 16:13 ` bot+bpf-ci
@ 2026-05-03 16:40 ` Aaron Tomlin
0 siblings, 0 replies; 3+ messages in thread
From: Aaron Tomlin @ 2026-05-03 16:40 UTC (permalink / raw)
To: bot+bpf-ci
Cc: corbet, song, kpsingh, mattbobrowski, ast, daniel, andrii,
eddyz87, memxor, rostedt, mhiramat, skhan, jolsa, martin.lau,
yonghong.song, mathieu.desnoyers, rdunlap, neelx, sean, chjohnst,
steve, mproche, nick.lange, linux-doc, linux-kernel, bpf,
linux-trace-kernel, martin.lau, clm, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 1373 bytes --]
On Sun, May 03, 2026 at 04:13:01PM +0000, bot+bpf-ci@kernel.org wrote:
> > diff --git a/Documentation/admin-guide/tainted-kernels.rst b/Documentation/admin-guide/tainted-kernels.rst
>
> [ ... ]
>
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index 11054ad89c14..e2060c6f3e13 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -9906,6 +9906,14 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
> > return err;
> > }
> >
> > + /*
> > + * Taint the kernel if the program attempts to use mutating
> > + * helpers.
> > + */
> > + if (func_id == BPF_FUNC_probe_write_user ||
> > + func_id == BPF_FUNC_override_return)
> > + add_taint(TAINT_UNSAFE_BPF, LOCKDEP_STILL_OK);
> > +
>
> Is it possible for the kernel to be tainted here even if the program is
> ultimately rejected by the verifier? check_helper_call() is executed during
> the verifier's static analysis phase. If the program fails verification later
> (for example, failing the GPL-compatibility check immediately following this
> code, or encountering an invalid memory access), the program is safely
> rejected and never loaded.
Yes, the above is true. The check should occur in bpf_prog_load(),
immediately after bpf_check() successfully returns.
Kind regards,
--
Aaron Tomlin
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-05-03 16:40 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-03 15:37 [RFC PATCH v2] bpf: introduce TAINT_UNSAFE_BPF for mutating helpers Aaron Tomlin
2026-05-03 16:13 ` bot+bpf-ci
2026-05-03 16:40 ` Aaron Tomlin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox