[PATCH bpf-next v10 3/8] bpf: Refactor reporting log_true_size for prog_load

Leon Hwang posted 8 patches 1 month, 2 weeks ago
There is a newer version of this series
[PATCH bpf-next v10 3/8] bpf: Refactor reporting log_true_size for prog_load
Posted by Leon Hwang 1 month, 2 weeks ago
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
Re: [PATCH bpf-next v10 3/8] bpf: Refactor reporting log_true_size for prog_load
Posted by Alexei Starovoitov 1 month ago
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
Re: [PATCH bpf-next v10 3/8] bpf: Refactor reporting log_true_size for prog_load
Posted by Leon Hwang 4 weeks, 1 day ago
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

Re: [PATCH bpf-next v10 3/8] bpf: Refactor reporting log_true_size for prog_load
Posted by Alexei Starovoitov 4 weeks, 1 day ago
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.
Re: [PATCH bpf-next v10 3/8] bpf: Refactor reporting log_true_size for prog_load
Posted by Leon Hwang 4 weeks, 1 day ago

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

Re: [PATCH bpf-next v10 3/8] bpf: Refactor reporting log_true_size for prog_load
Posted by Andrii Nakryiko 1 month, 2 weeks ago
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
>
Re: [PATCH bpf-next v10 3/8] bpf: Refactor reporting log_true_size for prog_load
Posted by Leon Hwang 1 month, 2 weeks ago

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

[...]