]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
bpf: Treat KF_RELEASE kfuncs as KF_TRUSTED_ARGS
authorDavid Vernet <void@manifault.com>
Sat, 25 Mar 2023 21:31:46 +0000 (16:31 -0500)
committerAlexei Starovoitov <ast@kernel.org>
Sat, 25 Mar 2023 23:56:22 +0000 (16:56 -0700)
KF_RELEASE kfuncs are not currently treated as having KF_TRUSTED_ARGS,
even though they have a superset of the requirements of KF_TRUSTED_ARGS.
Like KF_TRUSTED_ARGS, KF_RELEASE kfuncs require a 0-offset argument, and
don't allow NULL-able arguments. Unlike KF_TRUSTED_ARGS which require
_either_ an argument with ref_obj_id > 0, _or_ (ref->type &
BPF_REG_TRUSTED_MODIFIERS) (and no unsafe modifiers allowed), KF_RELEASE
only allows for ref_obj_id > 0.  Because KF_RELEASE today doesn't
automatically imply KF_TRUSTED_ARGS, some of these requirements are
enforced in different ways that can make the behavior of the verifier
feel unpredictable. For example, a KF_RELEASE kfunc with a NULL-able
argument will currently fail in the verifier with a message like, "arg#0
is ptr_or_null_ expected ptr_ or socket" rather than "Possibly NULL
pointer passed to trusted arg0". Our intention is the same, but the
semantics are different due to implemenetation details that kfunc authors
and BPF program writers should not need to care about.

Let's make the behavior of the verifier more consistent and intuitive by
having KF_RELEASE kfuncs imply the presence of KF_TRUSTED_ARGS. Our
eventual goal is to have all kfuncs assume KF_TRUSTED_ARGS by default
anyways, so this takes us a step in that direction.

Note that it does not make sense to assume KF_TRUSTED_ARGS for all
KF_ACQUIRE kfuncs. KF_ACQUIRE kfuncs can have looser semantics than
KF_RELEASE, with e.g. KF_RCU | KF_RET_NULL. We may want to have
KF_ACQUIRE imply KF_TRUSTED_ARGS _unless_ KF_RCU is specified, but that
can be left to another patch set, and there are no such subtleties to
address for KF_RELEASE.

Signed-off-by: David Vernet <void@manifault.com>
Link: https://lore.kernel.org/r/20230325213144.486885-4-void@manifault.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Documentation/bpf/kfuncs.rst
kernel/bpf/cpumask.c
kernel/bpf/verifier.c
net/bpf/test_run.c
tools/testing/selftests/bpf/progs/cgrp_kfunc_failure.c
tools/testing/selftests/bpf/progs/task_kfunc_failure.c
tools/testing/selftests/bpf/verifier/calls.c
tools/testing/selftests/bpf/verifier/ref_tracking.c

index 69eccf6f98ef4a608992ff55a43cff833a7a9682..bf1b859414527a754a7bbda9600b6adf31a054ef 100644 (file)
@@ -179,9 +179,10 @@ both are orthogonal to each other.
 ---------------------
 
 The KF_RELEASE flag is used to indicate that the kfunc releases the pointer
-passed in to it. There can be only one referenced pointer that can be passed in.
-All copies of the pointer being released are invalidated as a result of invoking
-kfunc with this flag.
+passed in to it. There can be only one referenced pointer that can be passed
+in. All copies of the pointer being released are invalidated as a result of
+invoking kfunc with this flag. KF_RELEASE kfuncs automatically receive the
+protection afforded by the KF_TRUSTED_ARGS flag described below.
 
 2.4.4 KF_KPTR_GET flag
 ----------------------
index e991af7dc13cc5685c1148ac4d19d1feab3cbb1e..7efdf5d770caa4343e80c0c48a62f37bc345429c 100644 (file)
@@ -402,7 +402,7 @@ __diag_pop();
 
 BTF_SET8_START(cpumask_kfunc_btf_ids)
 BTF_ID_FLAGS(func, bpf_cpumask_create, KF_ACQUIRE | KF_RET_NULL)
-BTF_ID_FLAGS(func, bpf_cpumask_release, KF_RELEASE | KF_TRUSTED_ARGS)
+BTF_ID_FLAGS(func, bpf_cpumask_release, KF_RELEASE)
 BTF_ID_FLAGS(func, bpf_cpumask_acquire, KF_ACQUIRE | KF_TRUSTED_ARGS)
 BTF_ID_FLAGS(func, bpf_cpumask_first, KF_RCU)
 BTF_ID_FLAGS(func, bpf_cpumask_first_zero, KF_RCU)
index 64f06f6e16bf6569fc4fbb437bd75843136c0cff..20eb2015842fca8c3f1cf8311dda0420aa8e6434 100644 (file)
@@ -9307,7 +9307,7 @@ static bool is_kfunc_release(struct bpf_kfunc_call_arg_meta *meta)
 
 static bool is_kfunc_trusted_args(struct bpf_kfunc_call_arg_meta *meta)
 {
-       return meta->kfunc_flags & KF_TRUSTED_ARGS;
+       return (meta->kfunc_flags & KF_TRUSTED_ARGS) || is_kfunc_release(meta);
 }
 
 static bool is_kfunc_sleepable(struct bpf_kfunc_call_arg_meta *meta)
index 27587f1c5f367314c290d20f3df2d23b480bd877..f1652f5fbd2e0f26ffde2fb1954a89163e1b84d7 100644 (file)
@@ -606,6 +606,11 @@ bpf_kfunc_call_test_acquire(unsigned long *scalar_ptr)
        return &prog_test_struct;
 }
 
+__bpf_kfunc void bpf_kfunc_call_test_offset(struct prog_test_ref_kfunc *p)
+{
+       WARN_ON_ONCE(1);
+}
+
 __bpf_kfunc struct prog_test_member *
 bpf_kfunc_call_memb_acquire(void)
 {
@@ -800,6 +805,7 @@ BTF_ID_FLAGS(func, bpf_kfunc_call_test_mem_len_fail2)
 BTF_ID_FLAGS(func, bpf_kfunc_call_test_ref, KF_TRUSTED_ARGS | KF_RCU)
 BTF_ID_FLAGS(func, bpf_kfunc_call_test_destructive, KF_DESTRUCTIVE)
 BTF_ID_FLAGS(func, bpf_kfunc_call_test_static_unused_arg)
+BTF_ID_FLAGS(func, bpf_kfunc_call_test_offset)
 BTF_SET8_END(test_sk_check_kfunc_ids)
 
 static void *bpf_test_init(const union bpf_attr *kattr, u32 user_size,
index 807fb0ac41e9c5d920484374a43d6c5018d579c6..48b2034cadb33fa9b759a1150d1a9862cac3c59e 100644 (file)
@@ -206,7 +206,7 @@ int BPF_PROG(cgrp_kfunc_get_unreleased, struct cgroup *cgrp, const char *path)
 }
 
 SEC("tp_btf/cgroup_mkdir")
-__failure __msg("expects refcounted")
+__failure __msg("Possibly NULL pointer passed to trusted arg0")
 int BPF_PROG(cgrp_kfunc_release_untrusted, struct cgroup *cgrp, const char *path)
 {
        struct __cgrps_kfunc_map_value *v;
@@ -234,7 +234,7 @@ int BPF_PROG(cgrp_kfunc_release_fp, struct cgroup *cgrp, const char *path)
 }
 
 SEC("tp_btf/cgroup_mkdir")
-__failure __msg("arg#0 is ptr_or_null_ expected ptr_ or socket")
+__failure __msg("Possibly NULL pointer passed to trusted arg0")
 int BPF_PROG(cgrp_kfunc_release_null, struct cgroup *cgrp, const char *path)
 {
        struct __cgrps_kfunc_map_value local, *v;
index 27994d6b291427088891198fa42dbf5d9f53a850..2c374a7ffece8ef759d058fab5bc4c1f76503a4a 100644 (file)
@@ -206,7 +206,7 @@ int BPF_PROG(task_kfunc_get_unreleased, struct task_struct *task, u64 clone_flag
 }
 
 SEC("tp_btf/task_newtask")
-__failure __msg("arg#0 is untrusted_ptr_or_null_ expected ptr_ or socket")
+__failure __msg("Possibly NULL pointer passed to trusted arg0")
 int BPF_PROG(task_kfunc_release_untrusted, struct task_struct *task, u64 clone_flags)
 {
        struct __tasks_kfunc_map_value *v;
@@ -234,7 +234,7 @@ int BPF_PROG(task_kfunc_release_fp, struct task_struct *task, u64 clone_flags)
 }
 
 SEC("tp_btf/task_newtask")
-__failure __msg("arg#0 is ptr_or_null_ expected ptr_ or socket")
+__failure __msg("Possibly NULL pointer passed to trusted arg0")
 int BPF_PROG(task_kfunc_release_null, struct task_struct *task, u64 clone_flags)
 {
        struct __tasks_kfunc_map_value local, *v;
@@ -277,7 +277,7 @@ int BPF_PROG(task_kfunc_release_unacquired, struct task_struct *task, u64 clone_
 }
 
 SEC("tp_btf/task_newtask")
-__failure __msg("arg#0 is ptr_or_null_ expected ptr_ or socket")
+__failure __msg("Possibly NULL pointer passed to trusted arg0")
 int BPF_PROG(task_kfunc_from_pid_no_null_check, struct task_struct *task, u64 clone_flags)
 {
        struct task_struct *acquired;
index 5702fc9761ef910133cdce04dca3277b472bf61d..1bdf2b43e49eaf31395ab93177e77b71be36ec2d 100644 (file)
        },
        .prog_type = BPF_PROG_TYPE_SCHED_CLS,
        .result = REJECT,
-       .errstr = "arg#0 is ptr_or_null_ expected ptr_ or socket",
+       .errstr = "Possibly NULL pointer passed to trusted arg0",
        .fixup_kfunc_btf_id = {
                { "bpf_kfunc_call_test_acquire", 3 },
                { "bpf_kfunc_call_test_release", 5 },
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8),
        BPF_ST_MEM(BPF_DW, BPF_REG_1, 0, 0),
        BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0),
+       BPF_MOV64_REG(BPF_REG_2, BPF_REG_0),
        BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
        BPF_EXIT_INSN(),
        BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
-       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, 16),
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -4),
        BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0),
        BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_2),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
        BPF_EXIT_INSN(),
        },
        .prog_type = BPF_PROG_TYPE_SCHED_CLS,
        .fixup_kfunc_btf_id = {
                { "bpf_kfunc_call_test_acquire", 3 },
-               { "bpf_kfunc_call_test_release", 9 },
+               { "bpf_kfunc_call_test_offset", 9 },
+               { "bpf_kfunc_call_test_release", 12 },
        },
        .result_unpriv = REJECT,
        .result = REJECT,
index 9540164712b736237f6431f963910e9c74b14cfe..5a2e154dd1e0d78529fd4ab9fc4e9d5d2f19bfa3 100644 (file)
        .kfunc = "bpf",
        .expected_attach_type = BPF_LSM_MAC,
        .flags = BPF_F_SLEEPABLE,
-       .errstr = "arg#0 is ptr_or_null_ expected ptr_ or socket",
+       .errstr = "Possibly NULL pointer passed to trusted arg0",
        .fixup_kfunc_btf_id = {
                { "bpf_lookup_user_key", 2 },
                { "bpf_key_put", 4 },
        .kfunc = "bpf",
        .expected_attach_type = BPF_LSM_MAC,
        .flags = BPF_F_SLEEPABLE,
-       .errstr = "arg#0 is ptr_or_null_ expected ptr_ or socket",
+       .errstr = "Possibly NULL pointer passed to trusted arg0",
        .fixup_kfunc_btf_id = {
                { "bpf_lookup_system_key", 1 },
                { "bpf_key_put", 3 },
        .kfunc = "bpf",
        .expected_attach_type = BPF_LSM_MAC,
        .flags = BPF_F_SLEEPABLE,
-       .errstr = "arg#0 pointer type STRUCT bpf_key must point to scalar, or struct with scalar",
+       .errstr = "Possibly NULL pointer passed to trusted arg0",
        .fixup_kfunc_btf_id = {
                { "bpf_key_put", 1 },
        },