The next commit will add support for reporting logs via extended common
attributes, including 'log_true_size'.
To prepare for that, refactor the 'log_true_size' reporting logic by
introducing a new struct bpf_log_attr to encapsulate log-related behavior:
* bpf_log_attr_init(): initialize log fields, which will support
extended common attributes in the next commit.
* bpf_log_attr_finalize(): handle log finalization and write back
'log_true_size' to userspace.
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
include/linux/bpf.h | 4 +++-
include/linux/bpf_verifier.h | 11 +++++++++++
kernel/bpf/log.c | 25 +++++++++++++++++++++++++
kernel/bpf/syscall.c | 13 ++++++++++---
kernel/bpf/verifier.c | 17 ++++-------------
5 files changed, 53 insertions(+), 17 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index cd9b96434904..d4dbcc7ad156 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -2913,7 +2913,9 @@ int bpf_check_uarg_tail_zero(bpfptr_t uaddr, size_t expected_size,
size_t actual_size);
/* verify correctness of eBPF program */
-int bpf_check(struct bpf_prog **fp, union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size);
+struct bpf_log_attr;
+int bpf_check(struct bpf_prog **fp, union bpf_attr *attr, bpfptr_t uattr,
+ struct bpf_log_attr *attr_log);
#ifndef CONFIG_BPF_JIT_ALWAYS_ON
void bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth);
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index ef8e45a362d9..dbd9bdb955b3 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -635,6 +635,17 @@ static inline bool bpf_verifier_log_needed(const struct bpf_verifier_log *log)
return log && log->level;
}
+struct bpf_log_attr {
+ char __user *log_buf;
+ u32 log_size;
+ u32 log_level;
+ u32 __user *log_true_size;
+};
+
+int bpf_log_attr_init(struct bpf_log_attr *log, u64 log_buf, u32 log_size, u32 log_level,
+ u32 __user *log_true_size);
+int bpf_log_attr_finalize(struct bpf_log_attr *attr, struct bpf_verifier_log *log);
+
#define BPF_MAX_SUBPROGS 256
struct bpf_subprog_arg_info {
diff --git a/kernel/bpf/log.c b/kernel/bpf/log.c
index a0c3b35de2ce..e31747b84fe2 100644
--- a/kernel/bpf/log.c
+++ b/kernel/bpf/log.c
@@ -863,3 +863,28 @@ void print_insn_state(struct bpf_verifier_env *env, const struct bpf_verifier_st
}
print_verifier_state(env, vstate, frameno, false);
}
+
+int bpf_log_attr_init(struct bpf_log_attr *log, u64 log_buf, u32 log_size, u32 log_level,
+ u32 __user *log_true_size)
+{
+ memset(log, 0, sizeof(*log));
+ log->log_buf = u64_to_user_ptr(log_buf);
+ log->log_size = log_size;
+ log->log_level = log_level;
+ log->log_true_size = log_true_size;
+ return 0;
+}
+
+int bpf_log_attr_finalize(struct bpf_log_attr *attr, struct bpf_verifier_log *log)
+{
+ u32 log_true_size;
+ int err;
+
+ err = bpf_vlog_finalize(log, &log_true_size);
+
+ if (attr->log_true_size && copy_to_user(attr->log_true_size, &log_true_size,
+ sizeof(log_true_size)))
+ return -EFAULT;
+
+ return err;
+}
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 0e231c0b1d04..e86674811996 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -2867,7 +2867,7 @@ static int bpf_prog_mark_insn_arrays_ready(struct bpf_prog *prog)
/* last field in 'union bpf_attr' used by this command */
#define BPF_PROG_LOAD_LAST_FIELD keyring_id
-static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
+static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, struct bpf_log_attr *attr_log)
{
enum bpf_prog_type type = attr->prog_type;
struct bpf_prog *prog, *dst_prog = NULL;
@@ -3085,7 +3085,7 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
goto free_prog_sec;
/* run eBPF verifier */
- err = bpf_check(&prog, attr, uattr, uattr_size);
+ err = bpf_check(&prog, attr, uattr, attr_log);
if (err < 0)
goto free_used_maps;
@@ -6189,7 +6189,10 @@ static int prog_assoc_struct_ops(union bpf_attr *attr)
static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size,
bpfptr_t uattr_common, unsigned int size_common)
{
+ bool from_user = !bpfptr_is_kernel(uattr);
struct bpf_common_attr attr_common;
+ u32 __user *log_true_size = NULL;
+ struct bpf_log_attr attr_log;
union bpf_attr attr;
int err;
@@ -6241,7 +6244,11 @@ static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size,
err = map_freeze(&attr);
break;
case BPF_PROG_LOAD:
- err = bpf_prog_load(&attr, uattr, size);
+ if (from_user && size >= offsetofend(union bpf_attr, log_true_size))
+ log_true_size = uattr.user + offsetof(union bpf_attr, log_true_size);
+ err = bpf_log_attr_init(&attr_log, attr.log_buf, attr.log_size, attr.log_level,
+ log_true_size);
+ err = err ?: bpf_prog_load(&attr, uattr, &attr_log);
break;
case BPF_OBJ_PIN:
err = bpf_obj_pin(&attr);
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index edf5342b982f..f1447b1878fd 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -25840,12 +25840,12 @@ static int compute_scc(struct bpf_verifier_env *env)
return err;
}
-int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u32 uattr_size)
+int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,
+ struct bpf_log_attr *attr_log)
{
u64 start_time = ktime_get_ns();
struct bpf_verifier_env *env;
int i, len, ret = -EINVAL, err;
- u32 log_true_size;
bool is_priv;
BTF_TYPE_EMIT(enum bpf_features);
@@ -25892,9 +25892,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u3
/* user could have requested verbose verifier output
* and supplied buffer to store the verification trace
*/
- ret = bpf_vlog_init(&env->log, attr->log_level,
- (char __user *) (unsigned long) attr->log_buf,
- attr->log_size);
+ ret = bpf_vlog_init(&env->log, attr_log->log_level, attr_log->log_buf, attr_log->log_size);
if (ret)
goto err_unlock;
@@ -26044,17 +26042,10 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u3
env->prog->aux->verified_insns = env->insn_processed;
/* preserve original error even if log finalization is successful */
- err = bpf_vlog_finalize(&env->log, &log_true_size);
+ err = bpf_log_attr_finalize(attr_log, &env->log);
if (err)
ret = err;
- if (uattr_size >= offsetofend(union bpf_attr, log_true_size) &&
- copy_to_bpfptr_offset(uattr, offsetof(union bpf_attr, log_true_size),
- &log_true_size, sizeof(log_true_size))) {
- ret = -EFAULT;
- goto err_release_maps;
- }
-
if (ret)
goto err_release_maps;
--
2.52.0
On Wed, Feb 11, 2026 at 7:13 AM Leon Hwang <leon.hwang@linux.dev> wrote:
>
> The next commit will add support for reporting logs via extended common
> attributes, including 'log_true_size'.
>
> To prepare for that, refactor the 'log_true_size' reporting logic by
> introducing a new struct bpf_log_attr to encapsulate log-related behavior:
>
> * bpf_log_attr_init(): initialize log fields, which will support
> extended common attributes in the next commit.
> * bpf_log_attr_finalize(): handle log finalization and write back
> 'log_true_size' to userspace.
>
> Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
> ---
> include/linux/bpf.h | 4 +++-
> include/linux/bpf_verifier.h | 11 +++++++++++
> kernel/bpf/log.c | 25 +++++++++++++++++++++++++
> kernel/bpf/syscall.c | 13 ++++++++++---
> kernel/bpf/verifier.c | 17 ++++-------------
> 5 files changed, 53 insertions(+), 17 deletions(-)
>
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index cd9b96434904..d4dbcc7ad156 100644
> --- a/include/linux/bpf.h
> +++ b/include/linux/bpf.h
> @@ -2913,7 +2913,9 @@ int bpf_check_uarg_tail_zero(bpfptr_t uaddr, size_t expected_size,
> size_t actual_size);
>
> /* verify correctness of eBPF program */
> -int bpf_check(struct bpf_prog **fp, union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size);
> +struct bpf_log_attr;
> +int bpf_check(struct bpf_prog **fp, union bpf_attr *attr, bpfptr_t uattr,
> + struct bpf_log_attr *attr_log);
>
> #ifndef CONFIG_BPF_JIT_ALWAYS_ON
> void bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth);
> diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
> index ef8e45a362d9..dbd9bdb955b3 100644
> --- a/include/linux/bpf_verifier.h
> +++ b/include/linux/bpf_verifier.h
> @@ -635,6 +635,17 @@ static inline bool bpf_verifier_log_needed(const struct bpf_verifier_log *log)
> return log && log->level;
> }
>
> +struct bpf_log_attr {
> + char __user *log_buf;
> + u32 log_size;
> + u32 log_level;
> + u32 __user *log_true_size;
> +};
> +
> +int bpf_log_attr_init(struct bpf_log_attr *log, u64 log_buf, u32 log_size, u32 log_level,
> + u32 __user *log_true_size);
> +int bpf_log_attr_finalize(struct bpf_log_attr *attr, struct bpf_verifier_log *log);
> +
> #define BPF_MAX_SUBPROGS 256
>
> struct bpf_subprog_arg_info {
> diff --git a/kernel/bpf/log.c b/kernel/bpf/log.c
> index a0c3b35de2ce..e31747b84fe2 100644
> --- a/kernel/bpf/log.c
> +++ b/kernel/bpf/log.c
> @@ -863,3 +863,28 @@ void print_insn_state(struct bpf_verifier_env *env, const struct bpf_verifier_st
> }
> print_verifier_state(env, vstate, frameno, false);
> }
> +
> +int bpf_log_attr_init(struct bpf_log_attr *log, u64 log_buf, u32 log_size, u32 log_level,
> + u32 __user *log_true_size)
> +{
> + memset(log, 0, sizeof(*log));
> + log->log_buf = u64_to_user_ptr(log_buf);
> + log->log_size = log_size;
> + log->log_level = log_level;
> + log->log_true_size = log_true_size;
> + return 0;
> +}
> +
> +int bpf_log_attr_finalize(struct bpf_log_attr *attr, struct bpf_verifier_log *log)
> +{
> + u32 log_true_size;
> + int err;
> +
> + err = bpf_vlog_finalize(log, &log_true_size);
> +
> + if (attr->log_true_size && copy_to_user(attr->log_true_size, &log_true_size,
> + sizeof(log_true_size)))
> + return -EFAULT;
> +
> + return err;
> +}
> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> index 0e231c0b1d04..e86674811996 100644
> --- a/kernel/bpf/syscall.c
> +++ b/kernel/bpf/syscall.c
> @@ -2867,7 +2867,7 @@ static int bpf_prog_mark_insn_arrays_ready(struct bpf_prog *prog)
> /* last field in 'union bpf_attr' used by this command */
> #define BPF_PROG_LOAD_LAST_FIELD keyring_id
>
> -static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
> +static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, struct bpf_log_attr *attr_log)
> {
> enum bpf_prog_type type = attr->prog_type;
> struct bpf_prog *prog, *dst_prog = NULL;
> @@ -3085,7 +3085,7 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
> goto free_prog_sec;
>
> /* run eBPF verifier */
> - err = bpf_check(&prog, attr, uattr, uattr_size);
> + err = bpf_check(&prog, attr, uattr, attr_log);
> if (err < 0)
> goto free_used_maps;
>
> @@ -6189,7 +6189,10 @@ static int prog_assoc_struct_ops(union bpf_attr *attr)
> static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size,
> bpfptr_t uattr_common, unsigned int size_common)
> {
> + bool from_user = !bpfptr_is_kernel(uattr);
> struct bpf_common_attr attr_common;
> + u32 __user *log_true_size = NULL;
> + struct bpf_log_attr attr_log;
> union bpf_attr attr;
> int err;
>
> @@ -6241,7 +6244,11 @@ static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size,
> err = map_freeze(&attr);
> break;
> case BPF_PROG_LOAD:
> - err = bpf_prog_load(&attr, uattr, size);
> + if (from_user && size >= offsetofend(union bpf_attr, log_true_size))
> + log_true_size = uattr.user + offsetof(union bpf_attr, log_true_size);
So you added 'from_user' gating because
you replaced copy_to_bpfptr_offset() with copy_to_user()?
This is a drastic change in behavior and you don't even talk about
it in the commit log.
You said "refactor". This is not a refactoring!
This is v10. The common_attr feature is useful, but
you really need to think harder about what your patches
are doing.
pw-bot: cr
On 4/3/26 00:32, Alexei Starovoitov wrote: > On Wed, Feb 11, 2026 at 7:13 AM Leon Hwang <leon.hwang@linux.dev> wrote: >> [...] >> @@ -6241,7 +6244,11 @@ static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size, >> err = map_freeze(&attr); >> break; >> case BPF_PROG_LOAD: >> - err = bpf_prog_load(&attr, uattr, size); >> + if (from_user && size >= offsetofend(union bpf_attr, log_true_size)) >> + log_true_size = uattr.user + offsetof(union bpf_attr, log_true_size); > > So you added 'from_user' gating because > you replaced copy_to_bpfptr_offset() with copy_to_user()? > This is a drastic change in behavior and you don't even talk about > it in the commit log. > You said "refactor". This is not a refactoring! > > This is v10. The common_attr feature is useful, but > you really need to think harder about what your patches > are doing. > Refactoring should not introduce any functional changes. If a functional change is involved, it should be factored out of the refactoring commit into a separate commit with an explanation in the commit log. I'll add this to my self-review checklist. The intention of 'from_user' was to replace copy_to_bpfptr_offset() with copy_to_user(), since the log is always copied to the user-space buffer when the log level is not BPF_LOG_KERNEL in kernel/bpf/log.c::bpf_verifier_vlog(). The 'from_user' gating will be dropped in v12 to keep this patch as pure refactoring. Thanks, Leon
On Tue, Mar 3, 2026 at 9:47 PM Leon Hwang <leon.hwang@linux.dev> wrote: > > On 4/3/26 00:32, Alexei Starovoitov wrote: > > On Wed, Feb 11, 2026 at 7:13 AM Leon Hwang <leon.hwang@linux.dev> wrote: > >> > > [...] > > >> @@ -6241,7 +6244,11 @@ static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size, > >> err = map_freeze(&attr); > >> break; > >> case BPF_PROG_LOAD: > >> - err = bpf_prog_load(&attr, uattr, size); > >> + if (from_user && size >= offsetofend(union bpf_attr, log_true_size)) > >> + log_true_size = uattr.user + offsetof(union bpf_attr, log_true_size); > > > > So you added 'from_user' gating because > > you replaced copy_to_bpfptr_offset() with copy_to_user()? > > This is a drastic change in behavior and you don't even talk about > > it in the commit log. > > You said "refactor". This is not a refactoring! > > > > This is v10. The common_attr feature is useful, but > > you really need to think harder about what your patches > > are doing. > > > > Refactoring should not introduce any functional changes. If a functional > change is involved, it should be factored out of the refactoring commit > into a separate commit with an explanation in the commit log. > > I'll add this to my self-review checklist. > > The intention of 'from_user' was to replace copy_to_bpfptr_offset() with > copy_to_user(), since the log is always copied to the user-space buffer > when the log level is not BPF_LOG_KERNEL in > kernel/bpf/log.c::bpf_verifier_vlog(). > > The 'from_user' gating will be dropped in v12 to keep this patch as pure > refactoring. You were told multiple times to avoid copy pasting AI into your emails. Sorry, but this crosses the line for me. Your patches will be ignored for 2 weeks.
On 4/3/26 13:58, Alexei Starovoitov wrote: > On Tue, Mar 3, 2026 at 9:47 PM Leon Hwang <leon.hwang@linux.dev> wrote: >> >> On 4/3/26 00:32, Alexei Starovoitov wrote: >>> On Wed, Feb 11, 2026 at 7:13 AM Leon Hwang <leon.hwang@linux.dev> wrote: >>>> >> >> [...] >> >>>> @@ -6241,7 +6244,11 @@ static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size, >>>> err = map_freeze(&attr); >>>> break; >>>> case BPF_PROG_LOAD: >>>> - err = bpf_prog_load(&attr, uattr, size); >>>> + if (from_user && size >= offsetofend(union bpf_attr, log_true_size)) >>>> + log_true_size = uattr.user + offsetof(union bpf_attr, log_true_size); >>> >>> So you added 'from_user' gating because >>> you replaced copy_to_bpfptr_offset() with copy_to_user()? >>> This is a drastic change in behavior and you don't even talk about >>> it in the commit log. >>> You said "refactor". This is not a refactoring! >>> >>> This is v10. The common_attr feature is useful, but >>> you really need to think harder about what your patches >>> are doing. >>> >> >> Refactoring should not introduce any functional changes. If a functional >> change is involved, it should be factored out of the refactoring commit >> into a separate commit with an explanation in the commit log. >> >> I'll add this to my self-review checklist. >> >> The intention of 'from_user' was to replace copy_to_bpfptr_offset() with >> copy_to_user(), since the log is always copied to the user-space buffer >> when the log level is not BPF_LOG_KERNEL in >> kernel/bpf/log.c::bpf_verifier_vlog(). >> >> The 'from_user' gating will be dropped in v12 to keep this patch as pure >> refactoring. > > You were told multiple times to avoid copy pasting AI into your emails. > Sorry, but this crosses the line for me. > Your patches will be ignored for 2 weeks. Oops. The above reply was written by my hand. Possibly, the reply carried LLM smell because I learnt LLM tongue recently. As you said, I won't send patches for 2 weeks. :-( Thanks, Leon
On Wed, Feb 11, 2026 at 7:13 AM Leon Hwang <leon.hwang@linux.dev> wrote:
>
> The next commit will add support for reporting logs via extended common
> attributes, including 'log_true_size'.
>
> To prepare for that, refactor the 'log_true_size' reporting logic by
> introducing a new struct bpf_log_attr to encapsulate log-related behavior:
>
> * bpf_log_attr_init(): initialize log fields, which will support
> extended common attributes in the next commit.
> * bpf_log_attr_finalize(): handle log finalization and write back
> 'log_true_size' to userspace.
>
> Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
> ---
> include/linux/bpf.h | 4 +++-
> include/linux/bpf_verifier.h | 11 +++++++++++
> kernel/bpf/log.c | 25 +++++++++++++++++++++++++
> kernel/bpf/syscall.c | 13 ++++++++++---
> kernel/bpf/verifier.c | 17 ++++-------------
> 5 files changed, 53 insertions(+), 17 deletions(-)
>
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index cd9b96434904..d4dbcc7ad156 100644
> --- a/include/linux/bpf.h
> +++ b/include/linux/bpf.h
> @@ -2913,7 +2913,9 @@ int bpf_check_uarg_tail_zero(bpfptr_t uaddr, size_t expected_size,
> size_t actual_size);
>
> /* verify correctness of eBPF program */
> -int bpf_check(struct bpf_prog **fp, union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size);
> +struct bpf_log_attr;
> +int bpf_check(struct bpf_prog **fp, union bpf_attr *attr, bpfptr_t uattr,
> + struct bpf_log_attr *attr_log);
>
> #ifndef CONFIG_BPF_JIT_ALWAYS_ON
> void bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth);
> diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
> index ef8e45a362d9..dbd9bdb955b3 100644
> --- a/include/linux/bpf_verifier.h
> +++ b/include/linux/bpf_verifier.h
> @@ -635,6 +635,17 @@ static inline bool bpf_verifier_log_needed(const struct bpf_verifier_log *log)
> return log && log->level;
> }
>
> +struct bpf_log_attr {
> + char __user *log_buf;
> + u32 log_size;
> + u32 log_level;
> + u32 __user *log_true_size;
> +};
> +
> +int bpf_log_attr_init(struct bpf_log_attr *log, u64 log_buf, u32 log_size, u32 log_level,
> + u32 __user *log_true_size);
> +int bpf_log_attr_finalize(struct bpf_log_attr *attr, struct bpf_verifier_log *log);
> +
> #define BPF_MAX_SUBPROGS 256
>
> struct bpf_subprog_arg_info {
> diff --git a/kernel/bpf/log.c b/kernel/bpf/log.c
> index a0c3b35de2ce..e31747b84fe2 100644
> --- a/kernel/bpf/log.c
> +++ b/kernel/bpf/log.c
> @@ -863,3 +863,28 @@ void print_insn_state(struct bpf_verifier_env *env, const struct bpf_verifier_st
> }
> print_verifier_state(env, vstate, frameno, false);
> }
> +
> +int bpf_log_attr_init(struct bpf_log_attr *log, u64 log_buf, u32 log_size, u32 log_level,
> + u32 __user *log_true_size)
> +{
> + memset(log, 0, sizeof(*log));
> + log->log_buf = u64_to_user_ptr(log_buf);
> + log->log_size = log_size;
> + log->log_level = log_level;
> + log->log_true_size = log_true_size;
> + return 0;
> +}
> +
> +int bpf_log_attr_finalize(struct bpf_log_attr *attr, struct bpf_verifier_log *log)
> +{
> + u32 log_true_size;
> + int err;
> +
> + err = bpf_vlog_finalize(log, &log_true_size);
> +
> + if (attr->log_true_size && copy_to_user(attr->log_true_size, &log_true_size,
> + sizeof(log_true_size)))
> + return -EFAULT;
> +
> + return err;
> +}
> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> index 0e231c0b1d04..e86674811996 100644
> --- a/kernel/bpf/syscall.c
> +++ b/kernel/bpf/syscall.c
> @@ -2867,7 +2867,7 @@ static int bpf_prog_mark_insn_arrays_ready(struct bpf_prog *prog)
> /* last field in 'union bpf_attr' used by this command */
> #define BPF_PROG_LOAD_LAST_FIELD keyring_id
>
> -static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
> +static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, struct bpf_log_attr *attr_log)
> {
> enum bpf_prog_type type = attr->prog_type;
> struct bpf_prog *prog, *dst_prog = NULL;
> @@ -3085,7 +3085,7 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
> goto free_prog_sec;
>
> /* run eBPF verifier */
> - err = bpf_check(&prog, attr, uattr, uattr_size);
> + err = bpf_check(&prog, attr, uattr, attr_log);
> if (err < 0)
> goto free_used_maps;
>
> @@ -6189,7 +6189,10 @@ static int prog_assoc_struct_ops(union bpf_attr *attr)
> static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size,
> bpfptr_t uattr_common, unsigned int size_common)
> {
> + bool from_user = !bpfptr_is_kernel(uattr);
> struct bpf_common_attr attr_common;
> + u32 __user *log_true_size = NULL;
> + struct bpf_log_attr attr_log;
> union bpf_attr attr;
> int err;
>
> @@ -6241,7 +6244,11 @@ static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size,
> err = map_freeze(&attr);
> break;
> case BPF_PROG_LOAD:
> - err = bpf_prog_load(&attr, uattr, size);
> + if (from_user && size >= offsetofend(union bpf_attr, log_true_size))
> + log_true_size = uattr.user + offsetof(union bpf_attr, log_true_size);
LGTM, so
Acked-by: Andrii Nakryiko <andrii@kernel.org>
but it caught my eye that we will never return log_true_size if
SYSCALL program tried to load another BPF program (light skeleton
case), which seems limiting, but we can perhaps address that
separately as a follow up
> + err = bpf_log_attr_init(&attr_log, attr.log_buf, attr.log_size, attr.log_level,
> + log_true_size);
> + err = err ?: bpf_prog_load(&attr, uattr, &attr_log);
> break;
> case BPF_OBJ_PIN:
> err = bpf_obj_pin(&attr);
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index edf5342b982f..f1447b1878fd 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -25840,12 +25840,12 @@ static int compute_scc(struct bpf_verifier_env *env)
> return err;
> }
>
> -int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u32 uattr_size)
> +int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,
> + struct bpf_log_attr *attr_log)
> {
> u64 start_time = ktime_get_ns();
> struct bpf_verifier_env *env;
> int i, len, ret = -EINVAL, err;
> - u32 log_true_size;
> bool is_priv;
>
> BTF_TYPE_EMIT(enum bpf_features);
> @@ -25892,9 +25892,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u3
> /* user could have requested verbose verifier output
> * and supplied buffer to store the verification trace
> */
> - ret = bpf_vlog_init(&env->log, attr->log_level,
> - (char __user *) (unsigned long) attr->log_buf,
> - attr->log_size);
> + ret = bpf_vlog_init(&env->log, attr_log->log_level, attr_log->log_buf, attr_log->log_size);
> if (ret)
> goto err_unlock;
>
> @@ -26044,17 +26042,10 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u3
> env->prog->aux->verified_insns = env->insn_processed;
>
> /* preserve original error even if log finalization is successful */
> - err = bpf_vlog_finalize(&env->log, &log_true_size);
> + err = bpf_log_attr_finalize(attr_log, &env->log);
> if (err)
> ret = err;
>
> - if (uattr_size >= offsetofend(union bpf_attr, log_true_size) &&
> - copy_to_bpfptr_offset(uattr, offsetof(union bpf_attr, log_true_size),
> - &log_true_size, sizeof(log_true_size))) {
> - ret = -EFAULT;
> - goto err_release_maps;
> - }
> -
> if (ret)
> goto err_release_maps;
>
> --
> 2.52.0
>
On 12/2/26 06:10, Andrii Nakryiko wrote: > On Wed, Feb 11, 2026 at 7:13 AM Leon Hwang <leon.hwang@linux.dev> wrote: >> [...] >> @@ -6241,7 +6244,11 @@ static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size, >> err = map_freeze(&attr); >> break; >> case BPF_PROG_LOAD: >> - err = bpf_prog_load(&attr, uattr, size); >> + if (from_user && size >= offsetofend(union bpf_attr, log_true_size)) >> + log_true_size = uattr.user + offsetof(union bpf_attr, log_true_size); > > LGTM, so > > Acked-by: Andrii Nakryiko <andrii@kernel.org> > Thanks for the review and the Ack! > but it caught my eye that we will never return log_true_size if > SYSCALL program tried to load another BPF program (light skeleton > case), which seems limiting, but we can perhaps address that > separately as a follow up > Correct. Currently, struct bpf_verifier_log and the log_true_size back-propagation mechanism are designed specifically for user space and are not compatible with in-kernel callers. I’ll look into whether this can be improved in a follow-up. Thanks, Leon [...]
© 2016 - 2026 Red Hat, Inc.