From: Kui-Feng Lee <sinquersw@gmail.com>
To: Amery Hung <ameryhung@gmail.com>
Cc: netdev@vger.kernel.org, bpf@vger.kernel.org,
yangpeihao@sjtu.edu.cn, daniel@iogearbox.net, andrii@kernel.org,
alexei.starovoitov@gmail.com, martin.lau@kernel.org,
toke@redhat.com, jhs@mojatatu.com, jiri@resnulli.us,
sdf@google.com, xiyou.wangcong@gmail.com, yepeilin.cs@gmail.com
Subject: Re: [RFC PATCH v9 03/11] bpf: Allow struct_ops prog to return referenced kptr
Date: Fri, 26 Jul 2024 11:22:13 -0700 [thread overview]
Message-ID: <71fa8e2c-953d-447d-905a-e2c596839cea@gmail.com> (raw)
In-Reply-To: <CAMB2axOR-9_h2wCiibRD0bwoCntZi7h_g79E1naRLFOurWscTg@mail.gmail.com>
On 7/24/24 13:44, Amery Hung wrote:
> On Tue, Jul 23, 2024 at 10:36 PM Kui-Feng Lee <sinquersw@gmail.com> wrote:
>>
>>
>>
>> On 7/14/24 10:51, Amery Hung wrote:
>>> Allow a struct_ops program to return a referenced kptr if the struct_ops
>>> operator has pointer to struct as the return type. To make sure the
>>> returned pointer continues to be valid in the kernel, several
>>> constraints are required:
>>>
>>> 1) The type of the pointer must matches the return type
>>> 2) The pointer originally comes from the kernel (not locally allocated)
>>> 3) The pointer is in its unmodified form
>>>
>>> In addition, since the first user, Qdisc_ops::dequeue, allows a NULL
>>> pointer to be returned when there is no skb to be dequeued, we will allow
>>> a scalar value with value equals to NULL to be returned.
>>>
>>> In the future when there is a struct_ops user that always expects a valid
>>> pointer to be returned from an operator, we may extend tagging to the
>>> return value. We can tell the verifier to only allow NULL pointer return
>>> if the return value is tagged with MAY_BE_NULL.
>>>
>>> The check is split into two parts since check_reference_leak() happens
>>> before check_return_code(). We first allow a reference object to leak
>>> through return if it is in the return register and the type matches the
>>> return type. Then, we check whether the pointer to-be-returned is valid in
>>> check_return_code().
>>>
>>> Signed-off-by: Amery Hung <amery.hung@bytedance.com>
>>> ---
>>> kernel/bpf/verifier.c | 50 +++++++++++++++++++++++++++++++++++++++----
>>> 1 file changed, 46 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
>>> index f614ab283c37..e7f356098902 100644
>>> --- a/kernel/bpf/verifier.c
>>> +++ b/kernel/bpf/verifier.c
>>> @@ -10188,16 +10188,36 @@ record_func_key(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
>>>
>>> static int check_reference_leak(struct bpf_verifier_env *env, bool exception_exit)
>>> {
>>> + enum bpf_prog_type type = resolve_prog_type(env->prog);
>>> + u32 regno = exception_exit ? BPF_REG_1 : BPF_REG_0;
>>> + struct bpf_reg_state *reg = reg_state(env, regno);
>>> struct bpf_func_state *state = cur_func(env);
>>> + const struct bpf_prog *prog = env->prog;
>>> + const struct btf_type *ret_type = NULL;
>>> bool refs_lingering = false;
>>> + struct btf *btf;
>>> int i;
>>>
>>> if (!exception_exit && state->frameno && !state->in_callback_fn)
>>> return 0;
>>>
>>> + if (type == BPF_PROG_TYPE_STRUCT_OPS &&
>>> + reg->type & PTR_TO_BTF_ID && reg->ref_obj_id) {
>>> + btf = bpf_prog_get_target_btf(prog);
>>> + ret_type = btf_type_by_id(btf, prog->aux->attach_func_proto->type);
>>> + if (reg->btf_id != ret_type->type) {
>>> + verbose(env, "Return kptr type, struct %s, doesn't match function prototype, struct %s\n",
>>> + btf_type_name(reg->btf, reg->btf_id),
>>> + btf_type_name(btf, ret_type->type));
>>> + return -EINVAL;
>>> + }
>>> + }
>>> +
>>> for (i = 0; i < state->acquired_refs; i++) {
>>> if (!exception_exit && state->in_callback_fn && state->refs[i].callback_ref != state->frameno)
>>> continue;
>>> + if (ret_type && reg->ref_obj_id == state->refs[i].id)
>>> + continue;
>>
>> Is it possible having two kptrs that both are in the returned type
>> passing into a function?
>>
>
> Just to make sure I understand the question correctly: Are you asking
> what would happen here if a struct_ops operator has the following
> signature?
>
> struct *foo xxx_ops__dummy_op(struct foo *foo_a__ref, struct foo *foo_b__ref)
Right! What would happen to this case? Could one of them leak without
being detected?
>
>>
>>> verbose(env, "Unreleased reference id=%d alloc_insn=%d\n",
>>> state->refs[i].id, state->refs[i].insn_idx);
>>> refs_lingering = true;
>>> @@ -15677,12 +15697,15 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char
>>> const char *exit_ctx = "At program exit";
>>> struct tnum enforce_attach_type_range = tnum_unknown;
>>> const struct bpf_prog *prog = env->prog;
>>> - struct bpf_reg_state *reg;
>>> + struct bpf_reg_state *reg = reg_state(env, regno);
>>> struct bpf_retval_range range = retval_range(0, 1);
>>> enum bpf_prog_type prog_type = resolve_prog_type(env->prog);
>>> int err;
>>> struct bpf_func_state *frame = env->cur_state->frame[0];
>>> const bool is_subprog = frame->subprogno;
>>> + struct btf *btf = bpf_prog_get_target_btf(prog);
>>> + bool st_ops_ret_is_kptr = false;
>>> + const struct btf_type *t;
>>>
>>> /* LSM and struct_ops func-ptr's return type could be "void" */
>>> if (!is_subprog || frame->in_exception_callback_fn) {
>>> @@ -15691,10 +15714,26 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char
>>> if (prog->expected_attach_type == BPF_LSM_CGROUP)
>>> /* See below, can be 0 or 0-1 depending on hook. */
>>> break;
>>> - fallthrough;
>>> + if (!prog->aux->attach_func_proto->type)
>>> + return 0;
>>> + break;
>>> case BPF_PROG_TYPE_STRUCT_OPS:
>>> if (!prog->aux->attach_func_proto->type)
>>> return 0;
>>> +
>>> + t = btf_type_by_id(btf, prog->aux->attach_func_proto->type);
>>> + if (btf_type_is_ptr(t)) {
>>> + /* Allow struct_ops programs to return kptr or null if
>>> + * the return type is a pointer type.
>>> + * check_reference_leak has ensured the returning kptr
>>> + * matches the type of the function prototype and is
>>> + * the only leaking reference. Thus, we can safely return
>>> + * if the pointer is in its unmodified form
>>> + */
>>> + if (reg->type & PTR_TO_BTF_ID)
>>> + return __check_ptr_off_reg(env, reg, regno, false);
>>> + st_ops_ret_is_kptr = true;
>>> + }
>>> break;
>>> default:
>>> break;
>>> @@ -15716,8 +15755,6 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char
>>> return -EACCES;
>>> }
>>>
>>> - reg = cur_regs(env) + regno;
>>> -
>>> if (frame->in_async_callback_fn) {
>>> /* enforce return zero from async callbacks like timer */
>>> exit_ctx = "At async callback return";
>>> @@ -15804,6 +15841,11 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char
>>> case BPF_PROG_TYPE_NETFILTER:
>>> range = retval_range(NF_DROP, NF_ACCEPT);
>>> break;
>>> + case BPF_PROG_TYPE_STRUCT_OPS:
>>> + if (!st_ops_ret_is_kptr)
>>> + return 0;
>>> + range = retval_range(0, 0);
>>> + break;
>>> case BPF_PROG_TYPE_EXT:
>>> /* freplace program can return anything as its return value
>>> * depends on the to-be-replaced kernel func or bpf program.
next prev parent reply other threads:[~2024-07-26 18:22 UTC|newest]
Thread overview: 43+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-07-14 17:51 [RFC PATCH v9 00/11] bpf qdisc Amery Hung
2024-07-14 17:51 ` [RFC PATCH v9 01/11] bpf: Support getting referenced kptr from struct_ops argument Amery Hung
2024-07-24 0:32 ` Martin KaFai Lau
2024-07-24 17:00 ` Amery Hung
2024-07-25 1:28 ` Martin KaFai Lau
2024-07-14 17:51 ` [RFC PATCH v9 02/11] selftests/bpf: Test referenced kptr arguments of struct_ops programs Amery Hung
2024-07-14 17:51 ` [RFC PATCH v9 03/11] bpf: Allow struct_ops prog to return referenced kptr Amery Hung
2024-07-24 5:36 ` Kui-Feng Lee
2024-07-24 18:27 ` Kui-Feng Lee
2024-07-24 20:44 ` Amery Hung
2024-07-26 18:22 ` Kui-Feng Lee [this message]
2024-07-26 22:45 ` Amery Hung
2024-07-24 23:57 ` Martin KaFai Lau
2024-07-14 17:51 ` [RFC PATCH v9 04/11] selftests/bpf: Test returning referenced kptr from struct_ops programs Amery Hung
2024-07-14 17:51 ` [RFC PATCH v9 05/11] bpf: net_sched: Support implementation of Qdisc_ops in bpf Amery Hung
2024-07-15 5:55 ` kernel test robot
2024-07-18 0:00 ` Amery Hung
2024-07-25 21:24 ` Martin KaFai Lau
2024-07-31 4:09 ` Amery Hung
2024-07-14 17:51 ` [RFC PATCH v9 06/11] bpf: net_sched: Add bpf qdisc kfuncs Amery Hung
2024-07-25 22:38 ` Martin KaFai Lau
2024-07-31 4:08 ` Amery Hung
2024-07-14 17:51 ` [RFC PATCH v9 07/11] bpf: net_sched: Allow more optional operators in Qdisc_ops Amery Hung
2024-07-18 0:01 ` Amery Hung
2024-07-26 1:15 ` Martin KaFai Lau
2024-07-26 18:30 ` Martin KaFai Lau
2024-07-26 22:30 ` Amery Hung
2024-07-30 0:20 ` Martin KaFai Lau
2024-07-14 17:51 ` [RFC PATCH v9 08/11] libbpf: Support creating and destroying qdisc Amery Hung
2024-07-14 17:51 ` [RFC PATCH v9 09/11] selftests: Add a basic fifo qdisc test Amery Hung
2024-07-14 17:51 ` [RFC PATCH v9 10/11] selftests: Add a bpf fq qdisc to selftest Amery Hung
2024-07-19 1:54 ` Martin KaFai Lau
2024-07-19 18:20 ` Amery Hung
2024-07-14 17:51 ` [RFC PATCH v9 11/11] selftests: Add a bpf netem " Amery Hung
2024-07-17 10:13 ` [RFC PATCH v9 00/11] bpf qdisc Donald Hunter
2024-07-17 22:04 ` Amery Hung
2024-07-19 17:21 ` [OFFLIST RFC 1/4] bpf: Search for kptrs in prog BTF structs Amery Hung
2024-07-19 17:21 ` [OFFLIST RFC 2/4] bpf: Rename ARG_PTR_TO_KPTR -> ARG_KPTR_XCHG_DEST Amery Hung
2024-07-19 17:21 ` [OFFLIST RFC 3/4] bpf: Support bpf_kptr_xchg into local kptr Amery Hung
2024-07-23 0:18 ` Alexei Starovoitov
2024-07-24 0:08 ` Amery Hung
2024-07-19 17:21 ` [OFFLIST RFC 4/4] selftests/bpf: Test bpf_kptr_xchg stashing " Amery Hung
2024-07-23 0:19 ` [RFC PATCH v9 00/11] bpf qdisc Alexei Starovoitov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=71fa8e2c-953d-447d-905a-e2c596839cea@gmail.com \
--to=sinquersw@gmail.com \
--cc=alexei.starovoitov@gmail.com \
--cc=ameryhung@gmail.com \
--cc=andrii@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=jhs@mojatatu.com \
--cc=jiri@resnulli.us \
--cc=martin.lau@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=sdf@google.com \
--cc=toke@redhat.com \
--cc=xiyou.wangcong@gmail.com \
--cc=yangpeihao@sjtu.edu.cn \
--cc=yepeilin.cs@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.