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_prog_load_log_attr_init(): initialize the 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 | 10 ++++++++++
kernel/bpf/log.c | 35 +++++++++++++++++++++++++++++++++++
kernel/bpf/syscall.c | 8 +++++---
kernel/bpf/verifier.c | 13 +++----------
5 files changed, 56 insertions(+), 14 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 8355b585cd18..c805b85b6f7a 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -631,6 +631,16 @@ static inline bool bpf_verifier_log_needed(const struct bpf_verifier_log *log)
return log && log->level;
}
+struct bpf_log_attr {
+ u32 offsetof_true_size;
+ u32 uattr_size;
+ bpfptr_t uattr;
+};
+
+int bpf_prog_load_log_attr_init(struct bpf_log_attr *attr_log, union bpf_attr *attr,
+ bpfptr_t uattr, u32 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..ff579fcba36f 100644
--- a/kernel/bpf/log.c
+++ b/kernel/bpf/log.c
@@ -863,3 +863,38 @@ void print_insn_state(struct bpf_verifier_env *env, const struct bpf_verifier_st
}
print_verifier_state(env, vstate, frameno, false);
}
+
+static void bpf_log_attr_init(struct bpf_log_attr *attr_log, int offsetof_true_size, bpfptr_t uattr,
+ u32 uattr_size)
+{
+ memset(attr_log, 0, sizeof(*attr_log));
+ attr_log->offsetof_true_size = offsetof_true_size;
+ attr_log->uattr_size = uattr_size;
+ attr_log->uattr = uattr;
+}
+
+int bpf_prog_load_log_attr_init(struct bpf_log_attr *attr_log, union bpf_attr *attr,
+ bpfptr_t uattr, u32 size)
+{
+ bpf_log_attr_init(attr_log, offsetof(union bpf_attr, log_true_size), uattr, size);
+ return 0;
+}
+
+int bpf_log_attr_finalize(struct bpf_log_attr *attr, struct bpf_verifier_log *log)
+{
+ u32 log_true_size;
+ size_t size;
+ int err;
+
+ if (!log)
+ return 0;
+
+ err = bpf_vlog_finalize(log, &log_true_size);
+
+ size = sizeof(log_true_size);
+ if (attr->uattr_size >= attr->offsetof_true_size + size &&
+ copy_to_bpfptr_offset(attr->uattr, attr->offsetof_true_size, &log_true_size, size))
+ err = -EFAULT;
+
+ return err;
+}
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index a58b16735e86..e81199361241 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -2860,7 +2860,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;
@@ -3078,7 +3078,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;
@@ -6180,6 +6180,7 @@ static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size,
bpfptr_t uattr_common, unsigned int size_common)
{
struct bpf_common_attr attr_common;
+ struct bpf_log_attr attr_log;
union bpf_attr attr;
int err;
@@ -6231,7 +6232,8 @@ 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);
+ err = bpf_prog_load_log_attr_init(&attr_log, &attr, uattr, 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 6b62b6d57175..1489867671e0 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -25604,12 +25604,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);
@@ -25808,17 +25808,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 Mon, Feb 2, 2026 at 6:42 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_prog_load_log_attr_init(): initialize the 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 | 10 ++++++++++
> kernel/bpf/log.c | 35 +++++++++++++++++++++++++++++++++++
> kernel/bpf/syscall.c | 8 +++++---
> kernel/bpf/verifier.c | 13 +++----------
> 5 files changed, 56 insertions(+), 14 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 8355b585cd18..c805b85b6f7a 100644
> --- a/include/linux/bpf_verifier.h
> +++ b/include/linux/bpf_verifier.h
> @@ -631,6 +631,16 @@ static inline bool bpf_verifier_log_needed(const struct bpf_verifier_log *log)
> return log && log->level;
> }
>
> +struct bpf_log_attr {
> + u32 offsetof_true_size;
> + u32 uattr_size;
> + bpfptr_t uattr;
> +};
> +
> +int bpf_prog_load_log_attr_init(struct bpf_log_attr *attr_log, union bpf_attr *attr,
> + bpfptr_t uattr, u32 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..ff579fcba36f 100644
> --- a/kernel/bpf/log.c
> +++ b/kernel/bpf/log.c
> @@ -863,3 +863,38 @@ void print_insn_state(struct bpf_verifier_env *env, const struct bpf_verifier_st
> }
> print_verifier_state(env, vstate, frameno, false);
> }
> +
> +static void bpf_log_attr_init(struct bpf_log_attr *attr_log, int offsetof_true_size, bpfptr_t uattr,
> + u32 uattr_size)
> +{
> + memset(attr_log, 0, sizeof(*attr_log));
> + attr_log->offsetof_true_size = offsetof_true_size;
> + attr_log->uattr_size = uattr_size;
> + attr_log->uattr = uattr;
> +}
> +
> +int bpf_prog_load_log_attr_init(struct bpf_log_attr *attr_log, union bpf_attr *attr,
> + bpfptr_t uattr, u32 size)
> +{
> + bpf_log_attr_init(attr_log, offsetof(union bpf_attr, log_true_size), uattr, size);
> + return 0;
> +}
> +
> +int bpf_log_attr_finalize(struct bpf_log_attr *attr, struct bpf_verifier_log *log)
> +{
> + u32 log_true_size;
> + size_t size;
> + int err;
> +
> + if (!log)
> + return 0;
can this ever happen? why guard against this?
> +
> + err = bpf_vlog_finalize(log, &log_true_size);
> +
> + size = sizeof(log_true_size);
> + if (attr->uattr_size >= attr->offsetof_true_size + size &&
> + copy_to_bpfptr_offset(attr->uattr, attr->offsetof_true_size, &log_true_size, size))
> + err = -EFAULT;
minor nit: return -EFAULT;
> +
> + return err;
> +}
[...]
On 5/2/26 03:48, Andrii Nakryiko wrote:
> On Mon, Feb 2, 2026 at 6:42 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_prog_load_log_attr_init(): initialize the 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 | 10 ++++++++++
>> kernel/bpf/log.c | 35 +++++++++++++++++++++++++++++++++++
>> kernel/bpf/syscall.c | 8 +++++---
>> kernel/bpf/verifier.c | 13 +++----------
>> 5 files changed, 56 insertions(+), 14 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 8355b585cd18..c805b85b6f7a 100644
>> --- a/include/linux/bpf_verifier.h
>> +++ b/include/linux/bpf_verifier.h
>> @@ -631,6 +631,16 @@ static inline bool bpf_verifier_log_needed(const struct bpf_verifier_log *log)
>> return log && log->level;
>> }
>>
>> +struct bpf_log_attr {
>> + u32 offsetof_true_size;
>> + u32 uattr_size;
>> + bpfptr_t uattr;
>> +};
>> +
>> +int bpf_prog_load_log_attr_init(struct bpf_log_attr *attr_log, union bpf_attr *attr,
>> + bpfptr_t uattr, u32 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..ff579fcba36f 100644
>> --- a/kernel/bpf/log.c
>> +++ b/kernel/bpf/log.c
>> @@ -863,3 +863,38 @@ void print_insn_state(struct bpf_verifier_env *env, const struct bpf_verifier_st
>> }
>> print_verifier_state(env, vstate, frameno, false);
>> }
>> +
>> +static void bpf_log_attr_init(struct bpf_log_attr *attr_log, int offsetof_true_size, bpfptr_t uattr,
>> + u32 uattr_size)
>> +{
>> + memset(attr_log, 0, sizeof(*attr_log));
>> + attr_log->offsetof_true_size = offsetof_true_size;
>> + attr_log->uattr_size = uattr_size;
>> + attr_log->uattr = uattr;
>> +}
>> +
>> +int bpf_prog_load_log_attr_init(struct bpf_log_attr *attr_log, union bpf_attr *attr,
>> + bpfptr_t uattr, u32 size)
>> +{
>> + bpf_log_attr_init(attr_log, offsetof(union bpf_attr, log_true_size), uattr, size);
>> + return 0;
>> +}
>> +
>> +int bpf_log_attr_finalize(struct bpf_log_attr *attr, struct bpf_verifier_log *log)
>> +{
>> + u32 log_true_size;
>> + size_t size;
>> + int err;
>> +
>> + if (!log)
>> + return 0;
>
> can this ever happen? why guard against this?
>
In patch #7, 'log' can be NULL when users do not provide 'log_buf'.
However, bpf_vlog_finalize() already guards against this case, so I'll
drop this check.
>> +
>> + err = bpf_vlog_finalize(log, &log_true_size);
>> +
>> + size = sizeof(log_true_size);
>> + if (attr->uattr_size >= attr->offsetof_true_size + size &&
>> + copy_to_bpfptr_offset(attr->uattr, attr->offsetof_true_size, &log_true_size, size))
>> + err = -EFAULT;
>
> minor nit: return -EFAULT;
>
Ack.
Thanks,
Leon
© 2016 - 2026 Red Hat, Inc.