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
next prev 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox