All of lore.kernel.org
 help / color / mirror / Atom feed
From: Leon Hwang <leon.hwang@linux.dev>
To: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Cc: bpf@vger.kernel.org, ast@kernel.org, andrii@kernel.org,
	daniel@iogearbox.net, menglong8.dong@gmail.com
Subject: Re: [RFC PATCH bpf-next v2 4/6] bpf: Add common attr support for map_create
Date: Wed, 24 Sep 2025 00:27:26 +0800	[thread overview]
Message-ID: <da547182-563c-463b-8ac5-ac4b9064cb6f@linux.dev> (raw)
In-Reply-To: <CAEf4BzbX_j5guUYuNNgR4dANR11tzLriDGOCOfxS9zRFmQdi7g@mail.gmail.com>



On 2025/9/18 05:39, Andrii Nakryiko wrote:
> On Thu, Sep 11, 2025 at 9:33 AM Leon Hwang <leon.hwang@linux.dev> wrote:
>>
>> Currently, many 'BPF_MAP_CREATE' failures return '-EINVAL' without
>> providing any explanation to user space.
>>
>> With the extended BPF syscall support introduced in the previous patch,
>> detailed error messages can now be reported. This allows users to
>> understand the specific reason for a failed map creation, rather than
>> just receiving a generic '-EINVAL'.
>>
>> Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
>> ---
>>  kernel/bpf/syscall.c | 82 ++++++++++++++++++++++++++++++++++----------
>>  1 file changed, 63 insertions(+), 19 deletions(-)
>>
>> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
>> index 5e5cf0262a14e..2f5e6005671b5 100644
>> --- a/kernel/bpf/syscall.c
>> +++ b/kernel/bpf/syscall.c
>> @@ -1340,12 +1340,13 @@ static bool bpf_net_capable(void)
>>
>>  #define BPF_MAP_CREATE_LAST_FIELD map_token_fd
>>  /* called via syscall */
>> -static int map_create(union bpf_attr *attr, bool kernel)
>> +static int map_create(union bpf_attr *attr, bool kernel, struct bpf_common_attr *common_attrs)
>>  {
>> +       u32 map_type = attr->map_type, log_true_size;
>> +       struct bpf_verifier_log *log = NULL;
>>         const struct bpf_map_ops *ops;
>>         struct bpf_token *token = NULL;
>>         int numa_node = bpf_map_attr_numa_node(attr);
>> -       u32 map_type = attr->map_type;
>>         struct bpf_map *map;
>>         bool token_flag;
>>         int f_flags;
>> @@ -1355,6 +1356,18 @@ static int map_create(union bpf_attr *attr, bool kernel)
>>         if (err)
>>                 return -EINVAL;
>>
>> +       if (common_attrs->log_buf) {
>> +               log = kvzalloc(sizeof(*log), GFP_KERNEL);
>> +               if (!log)
>> +                       return -ENOMEM;
>> +               err = bpf_vlog_init(log, BPF_LOG_FIXED, u64_to_user_ptr(common_attrs->log_buf),
>> +                                   common_attrs->log_size, NULL);
>> +               if (err) {
>> +                       kvfree(log);
>> +                       return err;
>> +               }
>> +       }
>
> what if we keep bpf_verifier_log on stack? It's 1KB, should be still
> fine to be on kernel stack, no?
>

I'm going to follow Alexei's suggestion.

>
>> +
>>         /* check BPF_F_TOKEN_FD flag, remember if it's set, and then clear it
>>          * to avoid per-map type checks tripping on unknown flag
>>          */
>> @@ -1363,16 +1376,24 @@ static int map_create(union bpf_attr *attr, bool kernel)
>>
>>         if (attr->btf_vmlinux_value_type_id) {
>>                 if (attr->map_type != BPF_MAP_TYPE_STRUCT_OPS ||
>> -                   attr->btf_key_type_id || attr->btf_value_type_id)
>> -                       return -EINVAL;
>> +                   attr->btf_key_type_id || attr->btf_value_type_id) {
>> +                       bpf_log(log, "Invalid use of btf_vmlinux_value_type_id.\n");
>
> I don't know how far we want to go here, but I'd split the original
> check into map type check and key_type/value_type check, and log a bit
> more meaningful error:
>
> a) btf_vmlinux_value_type_id can only be used with struct_ops maps.
>
> and
>
> b) btf_vmlinux_value_type_id is mutually exclusive with
> btf_key_type_id and btf_value_type_id
>

Sure.

It would be better to separate it like this.

>> +                       err = -EINVAL;
>> +                       goto put_token;
>
> there is no token just yet, add new label for finalizing log?
>

Ack.

>> +               }
>>         } else if (attr->btf_key_type_id && !attr->btf_value_type_id) {
>> -               return -EINVAL;
>> +               bpf_log(log, "Invalid btf_value_type_id.\n");
>> +               err = -EINVAL;
>> +               goto put_token;
>
> ditto about token
>

Ack.

>>         }
>>
>>         if (attr->map_type != BPF_MAP_TYPE_BLOOM_FILTER &&
>>             attr->map_type != BPF_MAP_TYPE_ARENA &&
>> -           attr->map_extra != 0)
>> -               return -EINVAL;
>> +           attr->map_extra != 0) {
>> +               bpf_log(log, "Invalid map_extra.\n");
>> +               err = -EINVAL;
>> +               goto put_token;

It'll be changed to the new label.

>> +       }
>>
>>         f_flags = bpf_get_file_flag(attr->map_flags);
>>         if (f_flags < 0)
>> @@ -1380,17 +1401,26 @@ static int map_create(union bpf_attr *attr, bool kernel)
>>
>>         if (numa_node != NUMA_NO_NODE &&
>>             ((unsigned int)numa_node >= nr_node_ids ||
>> -            !node_online(numa_node)))
>> -               return -EINVAL;
>> +            !node_online(numa_node))) {
>> +               bpf_log(log, "Invalid or offline numa_node.\n");
>
>
> nit: just "invalid numa_node" ?
>

Ack.

>> +               err = -EINVAL;
>> +               goto put_token;

It'll be changed to the new label.

>> +       }
>>
>>         /* find map type and init map: hashtable vs rbtree vs bloom vs ... */
>>         map_type = attr->map_type;
>> -       if (map_type >= ARRAY_SIZE(bpf_map_types))
>> -               return -EINVAL;
>> +       if (map_type >= ARRAY_SIZE(bpf_map_types)) {
>> +               bpf_log(log, "Invalid map_type.\n");
>> +               err = -EINVAL;
>> +               goto put_token;

It'll be changed to the new label.

>> +       }
>>         map_type = array_index_nospec(map_type, ARRAY_SIZE(bpf_map_types));
>>         ops = bpf_map_types[map_type];
>> -       if (!ops)
>> -               return -EINVAL;
>> +       if (!ops) {
>> +               bpf_log(log, "Invalid map_type.\n");
>> +               err = -EINVAL;
>> +               goto put_token;

It'll be changed to the new label.

>> +       }
>>
>>         if (ops->map_alloc_check) {
>>                 err = ops->map_alloc_check(attr);
>> @@ -1399,13 +1429,20 @@ static int map_create(union bpf_attr *attr, bool kernel)
>>         }
>>         if (attr->map_ifindex)
>>                 ops = &bpf_map_offload_ops;
>> -       if (!ops->map_mem_usage)
>> -               return -EINVAL;
>> +       if (!ops->map_mem_usage) {
>> +               bpf_log(log, "map_mem_usage is required.\n");
>
>
> this is kernel bug, actually, so let's log "bug: " prefix? same above
> with the second "Invalid map_type." message?
>
> and actually, given these are kernel bugs and shouldn't happen, I
> wonder if we should log anything here at all?
>

As it's a kernel bug if no map_mem_usage, would it be better to
WARN_ONCE here instead of reporting a log?

>> +               err = -EINVAL;
>> +               goto put_token;

It'll be changed to the new label.

>> +       }
>>
>>         if (token_flag) {
>>                 token = bpf_token_get_from_fd(attr->map_token_fd);
>> -               if (IS_ERR(token))
>> -                       return PTR_ERR(token);
>> +               if (IS_ERR(token)) {
>> +                       bpf_log(log, "Invalid map_token_fd.\n");
>> +                       err = PTR_ERR(token);
>> +                       token = NULL;
>> +                       goto put_token;
>
> ditto, no token
>

It'll be changed to the new label.

>> +               }
>>
>>                 /* if current token doesn't grant map creation permissions,
>>                  * then we can't use this token, so ignore it and rely on
>> @@ -1487,8 +1524,10 @@ static int map_create(union bpf_attr *attr, bool kernel)
>>
>>         err = bpf_obj_name_cpy(map->name, attr->map_name,
>>                                sizeof(attr->map_name));
>> -       if (err < 0)
>> +       if (err < 0) {
>> +               bpf_log(log, "Invalid map_name.\n");
>>                 goto free_map;
>> +       }
>>
>>         preempt_disable();
>>         map->cookie = gen_cookie_next(&bpf_map_cookie);
>> @@ -1511,6 +1550,7 @@ static int map_create(union bpf_attr *attr, bool kernel)
>>
>>                 btf = btf_get_by_fd(attr->btf_fd);
>>                 if (IS_ERR(btf)) {
>> +                       bpf_log(log, "Invalid btf_fd.\n");
>>                         err = PTR_ERR(btf);
>>                         goto free_map;
>>                 }
>> @@ -1565,6 +1605,10 @@ static int map_create(union bpf_attr *attr, bool kernel)
>>         bpf_map_free(map);
>>  put_token:
>>         bpf_token_put(token);
>> +       if (err && log)
>> +               (void) bpf_vlog_finalize(log, &log_true_size);
>
> so we'll just drop this size on the floor and never report it to the user?
>
>
> a) let's either teach bpf_vlog_finalize that log_true_size pointer can
> be NULL (and optional),
> or b) let's report it back to user through that same commont_attrs
> struct, just like we do it for verifier and btf logs?
>

Reporting it back to user looks better to me.

>
> Also, what if complicating error handling with this goto jumping, can
> we make use of __cleanup() attribute to do this automatically? Then
> we'd just allocate (or see above, maybe just init on the stack) log
> struct, and declare it with cleanup callback that will do this
> vlog_finalize and, maybe, report back the log actual size?
>

It does seem feasible to simplify error handling with __cleanup().

Instead of initializing log directly on the stack, we could introduce a
small wrapper:

struct bpf_log_wrapper {
    struct bpf_verifier_log *log;
};

with a destructor:

static void bpf_log_wrapper_destructor(struct bpf_log_wrapper *w)
{
    u32 log_true_size;

    if (w->log) {
        (void) bpf_vlog_finalize(w->log, &log_true_size);
        kfree(w->log);
    }
}

In this demo snippet I skipped handling log_true_size, but in practice
it should be dealt with properly.

Then, for example, in map_create():

struct bpf_log_wrapper log_wrapper __cleanup(bpf_log_wrapper_destructor)
= {};

if (common_attrs->log_buf) {
    log_wrapper.log = kzalloc(...);
}

I’ll give this approach a try in the next revision.

Thanks,
Leon

  parent reply	other threads:[~2025-09-23 16:27 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-09-11 16:33 [RFC PATCH bpf-next v2 0/6] bpf: Extend bpf syscall with common attributes support Leon Hwang
2025-09-11 16:33 ` [RFC PATCH bpf-next v2 1/6] " Leon Hwang
2025-09-17  0:06   ` Andrii Nakryiko
2025-09-23 15:23     ` Leon Hwang
2025-09-11 16:33 ` [RFC PATCH bpf-next v2 2/6] libbpf: Add support for extended bpf syscall Leon Hwang
2025-09-17  0:06   ` Andrii Nakryiko
2025-09-23 15:36     ` Leon Hwang
2025-09-24 23:57       ` Andrii Nakryiko
2025-09-11 16:33 ` [RFC PATCH bpf-next v2 3/6] bpf: Add common attr support for prog_load and btf_load Leon Hwang
2025-09-17 21:12   ` Andrii Nakryiko
2025-09-23 15:50     ` Leon Hwang
2025-09-25  0:00       ` Andrii Nakryiko
2025-09-11 16:33 ` [RFC PATCH bpf-next v2 4/6] bpf: Add common attr support for map_create Leon Hwang
2025-09-17 21:39   ` Andrii Nakryiko
2025-09-17 21:49     ` Alexei Starovoitov
2025-09-23 15:52       ` Leon Hwang
2025-09-23 16:27     ` Leon Hwang [this message]
2025-09-18 23:29   ` Eduard Zingerman
2025-09-23 16:31     ` Leon Hwang
2025-09-11 16:33 ` [RFC PATCH bpf-next v2 5/6] libbpf: " Leon Hwang
2025-09-17 21:45   ` Andrii Nakryiko
2025-09-17 21:46     ` Andrii Nakryiko
2025-09-23 16:40       ` Leon Hwang
2025-09-25  0:02         ` Andrii Nakryiko
2025-09-11 16:33 ` [RFC PATCH bpf-next v2 6/6] selftests/bpf: Add cases to test map create failure log Leon Hwang

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=da547182-563c-463b-8ac5-ac4b9064cb6f@linux.dev \
    --to=leon.hwang@linux.dev \
    --cc=andrii.nakryiko@gmail.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=menglong8.dong@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.