[PATCH bpf-next 4/4] bpf: Fix double-free RO header in bpf_jit_free

From: Pu Lehui

Date: Wed Jul 22 2026 - 07:33:34 EST


From: Pu Lehui <pulehui@xxxxxxxxxx>

When bpf_jit_binary_pack_finalize fails in bpf_jit_free, it implicitly
frees ro_header. However, JITs are unaware of this. They extract and
free it again, leading to double-free issue.

Fix this by dropping the implicit free in bpf_jit_binary_pack_finalize.
Accordingly, adapt the JITs to explicitly handle freeing ro_header in
their error paths. This also fixes the missing memory uncharge upon
failure, as bpf_prog_pack_free does not uncharge.

Fixes: 1d5f82d9dd47 ("bpf, x86: fix freeing of not-finalized bpf_prog_pack")
Reported-by: Sashiko <sashiko-bot@xxxxxxxxxx>
Signed-off-by: Pu Lehui <pulehui@xxxxxxxxxx>
---
arch/arm64/net/bpf_jit_comp.c | 2 +-
arch/loongarch/net/bpf_jit.c | 2 +-
arch/powerpc/net/bpf_jit_comp.c | 4 +++-
arch/riscv/net/bpf_jit_core.c | 2 +-
arch/x86/net/bpf_jit_comp.c | 2 +-
kernel/bpf/core.c | 5 ++---
6 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c
index f4e4d4578e38..e200b0207f7a 100644
--- a/arch/arm64/net/bpf_jit_comp.c
+++ b/arch/arm64/net/bpf_jit_comp.c
@@ -2258,7 +2258,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
goto out_free_hdr;
}
if (WARN_ON(bpf_jit_binary_pack_finalize(ro_header, header))) {
- /* ro_header and header has been freed */
+ bpf_jit_binary_pack_free(ro_header, NULL);
ro_header = NULL;
header = NULL;
goto out_free_hdr;
diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c
index 3d3ed1677d01..c8c8664dccaf 100644
--- a/arch/loongarch/net/bpf_jit.c
+++ b/arch/loongarch/net/bpf_jit.c
@@ -2298,7 +2298,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
goto out_free;
}
if (WARN_ON(bpf_jit_binary_pack_finalize(ro_header, header))) {
- /* ro_header and header have been freed */
+ bpf_jit_binary_pack_free(ro_header, NULL);
ro_header = NULL;
header = NULL;
goto out_free;
diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c
index 7b07b43575f1..0ca14b7d0b0e 100644
--- a/arch/powerpc/net/bpf_jit_comp.c
+++ b/arch/powerpc/net/bpf_jit_comp.c
@@ -339,8 +339,10 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
#endif

if (!fp->is_func || extra_pass) {
- if (bpf_jit_binary_pack_finalize(fhdr, hdr))
+ if (bpf_jit_binary_pack_finalize(fhdr, hdr)) {
+ bpf_jit_binary_pack_free(fhdr, NULL);
goto out_err;
+ }
}

fp->bpf_func = (void *)fimage;
diff --git a/arch/riscv/net/bpf_jit_core.c b/arch/riscv/net/bpf_jit_core.c
index 059db1adeaf8..440d5b965e60 100644
--- a/arch/riscv/net/bpf_jit_core.c
+++ b/arch/riscv/net/bpf_jit_core.c
@@ -154,7 +154,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr

if (!prog->is_func || extra_pass) {
if (WARN_ON(bpf_jit_binary_pack_finalize(jit_data->ro_header, jit_data->header))) {
- /* ro_header has been freed */
+ bpf_jit_binary_pack_free(jit_data->ro_header, NULL);
jit_data->ro_header = NULL;
jit_data->header = NULL;
goto out_free_hdr;
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index de7515ea1bea..d017bb0397b8 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -3988,7 +3988,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
* Both cases are serious bugs and justify WARN_ON.
*/
if (WARN_ON(bpf_jit_binary_pack_finalize(header, rw_header))) {
- /* header has been freed */
+ bpf_jit_binary_pack_free(header, NULL);
header = NULL;
goto out_image;
}
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 47fe047ad30b..77195911a3b3 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -1237,10 +1237,9 @@ int bpf_jit_binary_pack_finalize(struct bpf_binary_header *ro_header,

kvfree(rw_header);

- if (IS_ERR(ptr)) {
- bpf_prog_pack_free(ro_header, ro_header->size);
+ if (IS_ERR(ptr))
return PTR_ERR(ptr);
- }
+
return 0;
}

--
2.34.1