Merge branch 'bpf-enforce-trusted-btf-id-argument-provenance'

Yiyang Chen says:

====================
bpf: Enforce trusted BTF ID argument provenance

A global subprogram parameter tagged __arg_trusted is verified with
PTR_TRUSTED, but its call-site type check also accepts bare PTR_TO_BTF_ID and
MEM_RCU registers. This lets a callee use a pointer with weaker provenance
under the trusted-pointer contract, including passing it to a non-KF_RCU
kfunc.

Encode the trusted-pointer requirement in generated kfunc prototypes and
drive the shared provenance check from the argument contract. A KF_RCU
kfunc additionally permits MEM_RCU, while a global __arg_trusted argument
requires a referenced or trusted pointer. Normalize nullability when the
argument contract permits it.

Add rejection coverage for bare and RCU-protected global-subprogram arguments
and acceptance coverage for a nullable kfunc receiving a trusted nullable
pointer.

A separate scx_cake caller fix has been prepared but is not yet submitted.
The current veristat-scx pin still uses the old caller, which this series
rejects.

Changes in v4:
- Rebase onto bpf-next and use the shared argument checker.
- Reject MEM_RCU for global __arg_trusted arguments.
- Encode trusted and KF_RCU provenance in generated kfunc prototypes.
- Add nullable-kfunc coverage and use a 12-character Fixes SHA.
- Link to v3: https://lore.kernel.org/r/20261006-a3-arg-trusted-v4-v3-0-4619daf30e1c@mails.tsinghua.edu.cn

Changes in v3:
- Preserve RCU-protected arguments accepted by existing sched-ext programs.
- Make the MEM_RCU case a positive regression test.
- Use the preferred multi-line comment style.
- Link to v2: https://lore.kernel.org/r/20261005-a3-arg-trusted-v4-v2-0-319ce2936949@mails.tsinghua.edu.cn

Changes in v2:
- Retarget the fix to btf_check_func_arg_match(), where the subprogram
  argument check lives in this tree, instead of check_func_arg().
- Run the check after check_reg_type() and check_func_arg_reg_off() so type
  and offset diagnostics keep their wording.

v1: https://lore.kernel.org/bpf/20261005-a3-arg-trusted-v4-v1-0-50ee0268fd39@mails.tsinghua.edu.cn/
---
====================

Link: https://patch.msgid.link/20261007-a3-arg-trusted-v4-v4-0-253ef3a1a69a@mails.tsinghua.edu.cn
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
This commit is contained in:
Kumar Kartikeya Dwivedi 2026-10-19 03:51:41 +02:00
commit 44b6b1dfb2
No known key found for this signature in database
GPG Key ID: 472D377B63542F83
5 changed files with 79 additions and 7 deletions

View File

@ -5501,15 +5501,17 @@ static u32 *reg2btf_ids[__BPF_REG_TYPE_MAX] = {
[CONST_PTR_TO_MAP] = btf_bpf_map_id,
};
static bool is_trusted_reg(struct bpf_verifier_env *env, const struct bpf_reg_state *reg)
static bool is_trusted_reg_type(struct bpf_verifier_env *env,
const struct bpf_reg_state *reg,
enum bpf_reg_type type)
{
/* A referenced register is always trusted. */
if (reg_is_referenced(env, reg))
return true;
/* Types listed in the reg2btf_ids are always trusted */
if (reg2btf_ids[base_type(reg->type)] &&
!bpf_type_has_unsafe_modifiers(reg->type))
if (reg2btf_ids[base_type(type)] &&
!bpf_type_has_unsafe_modifiers(type))
return true;
/* If a register is not referenced, it is trusted if it has the
@ -5521,8 +5523,14 @@ static bool is_trusted_reg(struct bpf_verifier_env *env, const struct bpf_reg_st
* Eventually, we should make PTR_TRUSTED the single source of truth
* for whether a register is trusted.
*/
return type_flag(reg->type) & BPF_REG_TRUSTED_MODIFIERS &&
!bpf_type_has_unsafe_modifiers(reg->type);
return type_flag(type) & BPF_REG_TRUSTED_MODIFIERS &&
!bpf_type_has_unsafe_modifiers(type);
}
static bool is_trusted_reg(struct bpf_verifier_env *env,
const struct bpf_reg_state *reg)
{
return is_trusted_reg_type(env, reg, reg->type);
}
static bool is_rcu_reg(const struct bpf_reg_state *reg)
@ -9798,6 +9806,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg, u32 slot, u32 p
{
const u32 *arg_btf_id = fn->arg_btf_id[arg];
const struct btf *arg_btf = is_kfunc(meta) ? meta->btf : btf_vmlinux;
enum bpf_reg_type reg_type;
if (!meta->btf) {
const struct bpf_reg_types *compatible;
@ -9825,8 +9834,10 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg, u32 slot, u32 p
}
}
if (is_kfunc(meta) && (!is_trusted_reg(env, reg) ||
bpf_type_has_unsafe_modifiers(reg->type))) {
reg_type = reg->type & ~(arg_type & PTR_MAYBE_NULL);
if ((arg_type & PTR_TRUSTED) &&
(!is_trusted_reg_type(env, reg, reg_type) ||
bpf_type_has_unsafe_modifiers(reg_type))) {
if (!(arg_type & MEM_RCU)) {
const char *actual_type, *arg_name, *expected_type;
@ -14023,6 +14034,8 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
else
proto->arg_btf_id[arg] = ref_id_ptr;
arg_type |= PTR_TRUSTED;
/*
* A KF_RCU kfunc accepts an RCU-protected pointer where it would
* otherwise demand a referenced or trusted one. Other argument kinds

View File

@ -46,6 +46,17 @@ int iter_next_trusted_or_null(const void *ctx)
return 0;
}
SEC("syscall")
__success
int trusted_nullable_kfunc(const void *ctx)
{
struct task_struct *task;
task = bpf_kfunc_trusted_null_test();
bpf_kfunc_trusted_nullable_test(task);
return 0;
}
SEC("raw_tp/sys_enter")
__success
int iter_next_rcu(const void *ctx)

View File

@ -289,6 +289,41 @@ __weak int subprog_void_untrusted(void *p __arg_untrusted)
return *(int *)p;
}
__weak int subprog_trusted_bare(struct task_struct *task __arg_trusted)
{
return task->pid;
}
SEC("tp_btf/task_newtask")
__failure
__msg("R1 must be referenced or trusted")
__msg("Caller passes invalid args into func#{{.*}} ('subprog_trusted_bare')")
int bare_to_trusted(void *ctx)
{
struct task_struct *cur = bpf_get_current_task_btf();
struct task_struct *wakee;
if (!cur)
return 0;
wakee = cur->last_wakee;
if (!wakee)
return 0;
return subprog_trusted_bare(wakee);
}
SEC("tp_btf/task_newtask")
__failure
__msg("R1 must be referenced or trusted")
__msg("Caller passes invalid args into func#{{.*}}")
int memrcu_to_trusted(void *ctx)
{
struct task_struct *cur = bpf_get_current_task_btf();
if (!cur)
return 0;
return subprog_trusted_task_nullable(cur->real_parent);
}
__weak int subprog_char_untrusted(char *p __arg_untrusted)
{
return *(int *)p;

View File

@ -350,6 +350,15 @@ __bpf_kfunc void bpf_kfunc_trusted_task_test(struct task_struct *ptr)
{
}
__bpf_kfunc struct task_struct *bpf_kfunc_trusted_null_test(void)
{
return NULL;
}
__bpf_kfunc void bpf_kfunc_trusted_nullable_test(struct task_struct *ptr__nullable)
{
}
__bpf_kfunc void bpf_kfunc_trusted_num_test(int *ptr)
{
}
@ -957,6 +966,8 @@ BTF_ID_FLAGS(func, bpf_kfunc_nested_acquire_zero_offset_test, KF_ACQUIRE)
BTF_ID_FLAGS(func, bpf_kfunc_nested_release_test, KF_RELEASE)
BTF_ID_FLAGS(func, bpf_kfunc_trusted_vma_test)
BTF_ID_FLAGS(func, bpf_kfunc_trusted_task_test)
BTF_ID_FLAGS(func, bpf_kfunc_trusted_null_test, KF_RET_NULL)
BTF_ID_FLAGS(func, bpf_kfunc_trusted_nullable_test)
BTF_ID_FLAGS(func, bpf_kfunc_trusted_num_test)
BTF_ID_FLAGS(func, bpf_kfunc_rcu_task_test, KF_RCU)
BTF_ID_FLAGS(func, bpf_kfunc_ret_rcu_test, KF_RET_NULL | KF_RCU_PROTECTED)

View File

@ -353,6 +353,8 @@ int bpf_kfunc_st_ops_inc10(struct st_ops_args *args) __ksym;
void bpf_kfunc_trusted_vma_test(struct vm_area_struct *ptr) __ksym;
void bpf_kfunc_trusted_task_test(struct task_struct *ptr) __ksym;
struct task_struct *bpf_kfunc_trusted_null_test(void) __ksym;
void bpf_kfunc_trusted_nullable_test(struct task_struct *ptr__nullable) __ksym;
void bpf_kfunc_trusted_num_test(int *ptr) __ksym;
void bpf_kfunc_rcu_task_test(struct task_struct *ptr) __ksym;
struct task_struct *bpf_kfunc_ret_rcu_test(void) __ksym;