In the next commit, it will be able to report logs via extended common
attributes, which will report 'log_true_size' via the extended common
attributes meanwhile.
Therefore, refactor the way of 'log_true_size' reporting in order to
report 'log_true_size' via the extended common attributes easily.
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
include/linux/bpf.h | 2 +-
kernel/bpf/syscall.c | 21 +++++++++++++++++----
kernel/bpf/verifier.c | 12 ++----------
3 files changed, 20 insertions(+), 15 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index a63e47d2109c..26fbc550e5aa 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -2868,7 +2868,7 @@ 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);
+int bpf_check(struct bpf_prog **fp, union bpf_attr *attr, bpfptr_t uattr);
#ifndef CONFIG_BPF_JIT_ALWAYS_ON
void bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth);
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 8f464b847405..1739601fb7bd 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -2862,7 +2862,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)
{
enum bpf_prog_type type = attr->prog_type;
struct bpf_prog *prog, *dst_prog = NULL;
@@ -3080,7 +3080,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);
if (err < 0)
goto free_used_maps;
@@ -6160,12 +6160,22 @@ static int prog_assoc_struct_ops(union bpf_attr *attr)
return ret;
}
+static int copy_prog_load_log_true_size(union bpf_attr *attr, bpfptr_t uattr, unsigned int size)
+{
+ if (size >= offsetofend(union bpf_attr, log_true_size) &&
+ copy_to_bpfptr_offset(uattr, offsetof(union bpf_attr, log_true_size),
+ &attr->log_true_size, sizeof(attr->log_true_size)))
+ return -EFAULT;
+
+ return 0;
+}
+
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 common_attrs;
union bpf_attr attr;
- int err;
+ int err, ret;
err = bpf_check_uarg_tail_zero(uattr, sizeof(attr), size);
if (err)
@@ -6215,7 +6225,10 @@ 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);
+ attr.log_true_size = 0;
+ err = bpf_prog_load(&attr, uattr);
+ ret = copy_prog_load_log_true_size(&attr, uattr, size);
+ err = ret ? ret : err;
break;
case BPF_OBJ_PIN:
err = bpf_obj_pin(&attr);
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 9394b0de2ef0..ab5eacdde92c 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -25096,12 +25096,11 @@ 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)
{
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);
@@ -25300,17 +25299,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_vlog_finalize(&env->log, &attr->log_true_size);
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
> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> index 8f464b847405..1739601fb7bd 100644
> --- a/kernel/bpf/syscall.c
> +++ b/kernel/bpf/syscall.c
[ ... ]
> @@ -6160,12 +6160,22 @@ static int prog_assoc_struct_ops(union bpf_attr *attr)
> return ret;
> }
>
> +static int copy_prog_load_log_true_size(union bpf_attr *attr, bpfptr_t uattr, unsigned int size)
> +{
> + if (size >= offsetofend(union bpf_attr, log_true_size) &&
> + copy_to_bpfptr_offset(uattr, offsetof(union bpf_attr, log_true_size),
> + &attr->log_true_size, sizeof(attr->log_true_size)))
> + return -EFAULT;
> +
> + return 0;
> +}
> +
> 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 common_attrs;
> union bpf_attr attr;
> - int err;
> + int err, ret;
[ ... ]
> @@ -6215,7 +6225,10 @@ 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);
> + attr.log_true_size = 0;
> + err = bpf_prog_load(&attr, uattr);
> + ret = copy_prog_load_log_true_size(&attr, uattr, size);
> + err = ret ? ret : err;
^^^^^^^^^^^^^^^^^^^^
If bpf_prog_load() succeeds, it returns a positive file descriptor.
Then if copy_prog_load_log_true_size() fails with -EFAULT, this line
overwrites err with -EFAULT, losing the FD value.
The BPF program has already been loaded and the FD allocated at this
point. Since the FD is never returned to userspace, the program
remains loaded but unreachable - could this leak the BPF program?
In the previous code, the copy_to_bpfptr_offset() happened inside
bpf_check() before the program was fully committed:
if (uattr_size >= offsetofend(...) &&
copy_to_bpfptr_offset(...)) {
ret = -EFAULT;
goto err_release_maps;
}
This goto caused bpf_check() to return an error, preventing the FD
allocation in bpf_prog_load(). Moving the copy outside changes the
semantics - the FD can now be allocated before a copy failure occurs.
> + break;
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/20756616585
On 7/1/26 01:44, bot+bpf-ci@kernel.org wrote:
>> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
>> index 8f464b847405..1739601fb7bd 100644
>> --- a/kernel/bpf/syscall.c
>> +++ b/kernel/bpf/syscall.c
>
> [ ... ]
>
>> @@ -6160,12 +6160,22 @@ static int prog_assoc_struct_ops(union bpf_attr *attr)
>> return ret;
>> }
>>
>> +static int copy_prog_load_log_true_size(union bpf_attr *attr, bpfptr_t uattr, unsigned int size)
>> +{
>> + if (size >= offsetofend(union bpf_attr, log_true_size) &&
>> + copy_to_bpfptr_offset(uattr, offsetof(union bpf_attr, log_true_size),
>> + &attr->log_true_size, sizeof(attr->log_true_size)))
>> + return -EFAULT;
>> +
>> + return 0;
>> +}
>> +
>> 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 common_attrs;
>> union bpf_attr attr;
>> - int err;
>> + int err, ret;
>
> [ ... ]
>
>> @@ -6215,7 +6225,10 @@ 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);
>> + attr.log_true_size = 0;
>> + err = bpf_prog_load(&attr, uattr);
>> + ret = copy_prog_load_log_true_size(&attr, uattr, size);
>> + err = ret ? ret : err;
> ^^^^^^^^^^^^^^^^^^^^
>
> If bpf_prog_load() succeeds, it returns a positive file descriptor.
> Then if copy_prog_load_log_true_size() fails with -EFAULT, this line
> overwrites err with -EFAULT, losing the FD value.
>
> The BPF program has already been loaded and the FD allocated at this
> point. Since the FD is never returned to userspace, the program
> remains loaded but unreachable - could this leak the BPF program?
>
Indeed, the normal FD could be leaked if 'ret == -EFAULT'.
I'll address it in the next revision.
Thanks,
Leon
> In the previous code, the copy_to_bpfptr_offset() happened inside
> bpf_check() before the program was fully committed:
>
> if (uattr_size >= offsetofend(...) &&
> copy_to_bpfptr_offset(...)) {
> ret = -EFAULT;
> goto err_release_maps;
> }
>
> This goto caused bpf_check() to return an error, preventing the FD
> allocation in bpf_prog_load(). Moving the copy outside changes the
> semantics - the FD can now be allocated before a copy failure occurs.
>
>> + break;
>
>
> ---
> AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
> See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
>
> CI run summary: https://github.com/kernel-patches/bpf/actions/runs/20756616585
© 2016 - 2026 Red Hat, Inc.