All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH bpf] bpf, x86: fix freeing of not-finalized bpf_prog_pack
@ 2022-07-06  0:26 Song Liu
  2022-07-12 22:09 ` Alexei Starovoitov
  2022-07-13  0:40 ` patchwork-bot+netdevbpf
  0 siblings, 2 replies; 5+ messages in thread
From: Song Liu @ 2022-07-06  0:26 UTC (permalink / raw)
  To: bpf
  Cc: daniel, kernel-team, ast, andrii, Song Liu,
	syzbot+2f649ec6d2eea1495a8f, syzbot+87f65c75f4a72db05445

syzbot reported a few issues with bpf_prog_pack [1], [2]. These are
triggered when the program passed initial JIT in jit_subprogs(), but
failed final pass of JIT. At this point, bpf_jit_binary_pack_free() is
called before bpf_jit_binary_pack_finalize(), and the whole 2MB page is
freed.

Fix this with a custom bpf_jit_free() for x86_64, which calls
bpf_jit_binary_pack_finalize() if necessary. Also, with custom
bpf_jit_free(), bpf_prog_aux->use_bpf_prog_pack is not needed any more,
remove it.

Fixes: 1022a5498f6f ("bpf, x86_64: Use bpf_jit_binary_pack_alloc")
[1] https://syzkaller.appspot.com/bug?extid=2f649ec6d2eea1495a8f
[2] https://syzkaller.appspot.com/bug?extid=87f65c75f4a72db05445
Reported-by: syzbot+2f649ec6d2eea1495a8f@syzkaller.appspotmail.com
Reported-by: syzbot+87f65c75f4a72db05445@syzkaller.appspotmail.com
Signed-off-by: Song Liu <song@kernel.org>
---
 arch/x86/net/bpf_jit_comp.c | 25 +++++++++++++++++++++++++
 include/linux/bpf.h         |  1 -
 include/linux/filter.h      |  8 ++++++++
 kernel/bpf/core.c           | 29 ++++++++++++-----------------
 4 files changed, 45 insertions(+), 18 deletions(-)

diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index c98b8c0ed3b8..c3dca4c97e48 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -2492,3 +2492,28 @@ void *bpf_arch_text_copy(void *dst, void *src, size_t len)
 		return ERR_PTR(-EINVAL);
 	return dst;
 }
+
+void bpf_jit_free(struct bpf_prog *prog)
+{
+	if (prog->jited) {
+		struct x64_jit_data *jit_data = prog->aux->jit_data;
+		struct bpf_binary_header *hdr;
+
+		/*
+		 * If we fail the final pass of JIT (from jit_subprogs),
+		 * the program may not be finalized yet. Call finalize here
+		 * before freeing it.
+		 */
+		if (jit_data) {
+			bpf_jit_binary_pack_finalize(prog, jit_data->header,
+						     jit_data->rw_header);
+			kvfree(jit_data->addrs);
+			kfree(jit_data);
+		}
+		hdr = bpf_jit_binary_pack_hdr(prog);
+		bpf_jit_binary_pack_free(hdr, NULL);
+		WARN_ON_ONCE(!bpf_prog_kallsyms_verify_off(prog));
+	}
+
+	bpf_prog_unlock_free(prog);
+}
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 2b914a56a2c5..7424cf234ae0 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1025,7 +1025,6 @@ struct bpf_prog_aux {
 	bool sleepable;
 	bool tail_call_reachable;
 	bool xdp_has_frags;
-	bool use_bpf_prog_pack;
 	/* BTF_KIND_FUNC_PROTO for valid attach_btf_id */
 	const struct btf_type *attach_func_proto;
 	/* function name for valid attach_btf_id */
diff --git a/include/linux/filter.h b/include/linux/filter.h
index ed0c0ff42ad5..5005bf2d30bd 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -1060,6 +1060,14 @@ u64 bpf_jit_alloc_exec_limit(void);
 void *bpf_jit_alloc_exec(unsigned long size);
 void bpf_jit_free_exec(void *addr);
 void bpf_jit_free(struct bpf_prog *fp);
+struct bpf_binary_header *
+bpf_jit_binary_pack_hdr(const struct bpf_prog *fp);
+
+static inline bool bpf_prog_kallsyms_verify_off(const struct bpf_prog *fp)
+{
+	return list_empty(&fp->aux->ksym.lnode) ||
+	       fp->aux->ksym.lnode.prev == LIST_POISON2;
+}
 
 struct bpf_binary_header *
 bpf_jit_binary_pack_alloc(unsigned int proglen, u8 **ro_image,
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 5f6f3f829b36..360ceb817639 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -647,12 +647,6 @@ static bool bpf_prog_kallsyms_candidate(const struct bpf_prog *fp)
 	return fp->jited && !bpf_prog_was_classic(fp);
 }
 
-static bool bpf_prog_kallsyms_verify_off(const struct bpf_prog *fp)
-{
-	return list_empty(&fp->aux->ksym.lnode) ||
-	       fp->aux->ksym.lnode.prev == LIST_POISON2;
-}
-
 void bpf_prog_kallsyms_add(struct bpf_prog *fp)
 {
 	if (!bpf_prog_kallsyms_candidate(fp) ||
@@ -1150,7 +1144,6 @@ int bpf_jit_binary_pack_finalize(struct bpf_prog *prog,
 		bpf_prog_pack_free(ro_header);
 		return PTR_ERR(ptr);
 	}
-	prog->aux->use_bpf_prog_pack = true;
 	return 0;
 }
 
@@ -1174,17 +1167,23 @@ void bpf_jit_binary_pack_free(struct bpf_binary_header *ro_header,
 	bpf_jit_uncharge_modmem(size);
 }
 
+struct bpf_binary_header *
+bpf_jit_binary_pack_hdr(const struct bpf_prog *fp)
+{
+	unsigned long real_start = (unsigned long)fp->bpf_func;
+	unsigned long addr;
+
+	addr = real_start & BPF_PROG_CHUNK_MASK;
+	return (void *)addr;
+}
+
 static inline struct bpf_binary_header *
 bpf_jit_binary_hdr(const struct bpf_prog *fp)
 {
 	unsigned long real_start = (unsigned long)fp->bpf_func;
 	unsigned long addr;
 
-	if (fp->aux->use_bpf_prog_pack)
-		addr = real_start & BPF_PROG_CHUNK_MASK;
-	else
-		addr = real_start & PAGE_MASK;
-
+	addr = real_start & PAGE_MASK;
 	return (void *)addr;
 }
 
@@ -1197,11 +1196,7 @@ void __weak bpf_jit_free(struct bpf_prog *fp)
 	if (fp->jited) {
 		struct bpf_binary_header *hdr = bpf_jit_binary_hdr(fp);
 
-		if (fp->aux->use_bpf_prog_pack)
-			bpf_jit_binary_pack_free(hdr, NULL /* rw_buffer */);
-		else
-			bpf_jit_binary_free(hdr);
-
+		bpf_jit_binary_free(hdr);
 		WARN_ON_ONCE(!bpf_prog_kallsyms_verify_off(fp));
 	}
 
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH bpf] bpf, x86: fix freeing of not-finalized bpf_prog_pack
  2022-07-06  0:26 [PATCH bpf] bpf, x86: fix freeing of not-finalized bpf_prog_pack Song Liu
@ 2022-07-12 22:09 ` Alexei Starovoitov
  2022-07-12 23:01   ` Song Liu
  2022-07-13  0:40 ` patchwork-bot+netdevbpf
  1 sibling, 1 reply; 5+ messages in thread
From: Alexei Starovoitov @ 2022-07-12 22:09 UTC (permalink / raw)
  To: Song Liu
  Cc: bpf, Daniel Borkmann, Kernel Team, Alexei Starovoitov,
	Andrii Nakryiko, syzbot+2f649ec6d2eea1495a8f,
	syzbot+87f65c75f4a72db05445

On Tue, Jul 5, 2022 at 5:26 PM Song Liu <song@kernel.org> wrote:
>
> syzbot reported a few issues with bpf_prog_pack [1], [2]. These are
> triggered when the program passed initial JIT in jit_subprogs(), but
> failed final pass of JIT. At this point, bpf_jit_binary_pack_free() is
> called before bpf_jit_binary_pack_finalize(), and the whole 2MB page is
> freed.
>
> Fix this with a custom bpf_jit_free() for x86_64, which calls
> bpf_jit_binary_pack_finalize() if necessary. Also, with custom
> bpf_jit_free(), bpf_prog_aux->use_bpf_prog_pack is not needed any more,
> remove it.
>
> Fixes: 1022a5498f6f ("bpf, x86_64: Use bpf_jit_binary_pack_alloc")
> [1] https://syzkaller.appspot.com/bug?extid=2f649ec6d2eea1495a8f
> [2] https://syzkaller.appspot.com/bug?extid=87f65c75f4a72db05445
> Reported-by: syzbot+2f649ec6d2eea1495a8f@syzkaller.appspotmail.com
> Reported-by: syzbot+87f65c75f4a72db05445@syzkaller.appspotmail.com
> Signed-off-by: Song Liu <song@kernel.org>
> ---
>  arch/x86/net/bpf_jit_comp.c | 25 +++++++++++++++++++++++++
>  include/linux/bpf.h         |  1 -
>  include/linux/filter.h      |  8 ++++++++
>  kernel/bpf/core.c           | 29 ++++++++++++-----------------
>  4 files changed, 45 insertions(+), 18 deletions(-)
>
> diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
> index c98b8c0ed3b8..c3dca4c97e48 100644
> --- a/arch/x86/net/bpf_jit_comp.c
> +++ b/arch/x86/net/bpf_jit_comp.c
> @@ -2492,3 +2492,28 @@ void *bpf_arch_text_copy(void *dst, void *src, size_t len)
>                 return ERR_PTR(-EINVAL);
>         return dst;
>  }
> +
> +void bpf_jit_free(struct bpf_prog *prog)
> +{
> +       if (prog->jited) {
> +               struct x64_jit_data *jit_data = prog->aux->jit_data;
> +               struct bpf_binary_header *hdr;
> +
> +               /*
> +                * If we fail the final pass of JIT (from jit_subprogs),
> +                * the program may not be finalized yet. Call finalize here
> +                * before freeing it.
> +                */
> +               if (jit_data) {
> +                       bpf_jit_binary_pack_finalize(prog, jit_data->header,
> +                                                    jit_data->rw_header);
> +                       kvfree(jit_data->addrs);
> +                       kfree(jit_data);
> +               }

It looks like a workaround for missed cleanup on the JIT side.
When bpf_int_jit_compile() fails it is supposed to free jit_data
immediately.

> passed initial JIT in jit_subprogs(), but
> failed final pass of JIT. At this point, bpf_jit_binary_pack_free() is
> called before bpf_jit_binary_pack_finalize()

It feels that bpf_int_jit_compile() should call
bpf_jit_binary_pack_finalize() instead in the path where
it's failing.
I could be missing details on what exactly
"failed final pass of JIT" means.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH bpf] bpf, x86: fix freeing of not-finalized bpf_prog_pack
  2022-07-12 22:09 ` Alexei Starovoitov
@ 2022-07-12 23:01   ` Song Liu
  2022-07-13  0:37     ` Alexei Starovoitov
  0 siblings, 1 reply; 5+ messages in thread
From: Song Liu @ 2022-07-12 23:01 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: bpf, Daniel Borkmann, Kernel Team, Alexei Starovoitov,
	Andrii Nakryiko, syzbot, syzbot

On Tue, Jul 12, 2022 at 3:09 PM Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
>
> On Tue, Jul 5, 2022 at 5:26 PM Song Liu <song@kernel.org> wrote:
> >
> > syzbot reported a few issues with bpf_prog_pack [1], [2]. These are
> > triggered when the program passed initial JIT in jit_subprogs(), but
> > failed final pass of JIT. At this point, bpf_jit_binary_pack_free() is
> > called before bpf_jit_binary_pack_finalize(), and the whole 2MB page is
> > freed.
> >
> > Fix this with a custom bpf_jit_free() for x86_64, which calls
> > bpf_jit_binary_pack_finalize() if necessary. Also, with custom
> > bpf_jit_free(), bpf_prog_aux->use_bpf_prog_pack is not needed any more,
> > remove it.
> >
> > Fixes: 1022a5498f6f ("bpf, x86_64: Use bpf_jit_binary_pack_alloc")
> > [1] https://syzkaller.appspot.com/bug?extid=2f649ec6d2eea1495a8f
> > [2] https://syzkaller.appspot.com/bug?extid=87f65c75f4a72db05445
> > Reported-by: syzbot+2f649ec6d2eea1495a8f@syzkaller.appspotmail.com
> > Reported-by: syzbot+87f65c75f4a72db05445@syzkaller.appspotmail.com
> > Signed-off-by: Song Liu <song@kernel.org>
> > ---
> >  arch/x86/net/bpf_jit_comp.c | 25 +++++++++++++++++++++++++
> >  include/linux/bpf.h         |  1 -
> >  include/linux/filter.h      |  8 ++++++++
> >  kernel/bpf/core.c           | 29 ++++++++++++-----------------
> >  4 files changed, 45 insertions(+), 18 deletions(-)
> >
> > diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
> > index c98b8c0ed3b8..c3dca4c97e48 100644
> > --- a/arch/x86/net/bpf_jit_comp.c
> > +++ b/arch/x86/net/bpf_jit_comp.c
> > @@ -2492,3 +2492,28 @@ void *bpf_arch_text_copy(void *dst, void *src, size_t len)
> >                 return ERR_PTR(-EINVAL);
> >         return dst;
> >  }
> > +
> > +void bpf_jit_free(struct bpf_prog *prog)
> > +{
> > +       if (prog->jited) {
> > +               struct x64_jit_data *jit_data = prog->aux->jit_data;
> > +               struct bpf_binary_header *hdr;
> > +
> > +               /*
> > +                * If we fail the final pass of JIT (from jit_subprogs),
> > +                * the program may not be finalized yet. Call finalize here
> > +                * before freeing it.
> > +                */
> > +               if (jit_data) {
> > +                       bpf_jit_binary_pack_finalize(prog, jit_data->header,
> > +                                                    jit_data->rw_header);
> > +                       kvfree(jit_data->addrs);
> > +                       kfree(jit_data);
> > +               }
>
> It looks like a workaround for missed cleanup on the JIT side.
> When bpf_int_jit_compile() fails it is supposed to free jit_data
> immediately.
>
> > passed initial JIT in jit_subprogs(), but
> > failed final pass of JIT. At this point, bpf_jit_binary_pack_free() is
> > called before bpf_jit_binary_pack_finalize()
>
> It feels that bpf_int_jit_compile() should call
> bpf_jit_binary_pack_finalize() instead in the path where
> it's failing.
> I could be missing details on what exactly
> "failed final pass of JIT" means.

This only happens with multiple subprogs. In jit_subprogs(), we
first call bpf_int_jit_compile() on each sub program. And then,
we call it on each sub program again. jit_data is not freed in the
first call of bpf_int_jit_compile(). Similarly we don't call
bpf_jit_binary_pack_finalize() in the first call of bpf_int_jit_compile().

If bpf_int_jit_compile() failed for one sub program, we will call
bpf_jit_binary_pack_finalize() for this sub program. However,
we don't have a chance to call it for other sub programs. Then
we will hit "goto out_free" in jit_subprogs(), and call bpf_jit_free
on some subprograms that haven't got bpf_jit_binary_pack_finalize()
yet. So, I think bpf_jit_free is the best place we can add the extra
check and call bpf_jit_binary_pack_finalize().

Does this make sense?

Thanks,
Song

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH bpf] bpf, x86: fix freeing of not-finalized bpf_prog_pack
  2022-07-12 23:01   ` Song Liu
@ 2022-07-13  0:37     ` Alexei Starovoitov
  0 siblings, 0 replies; 5+ messages in thread
From: Alexei Starovoitov @ 2022-07-13  0:37 UTC (permalink / raw)
  To: Song Liu
  Cc: bpf, Daniel Borkmann, Kernel Team, Alexei Starovoitov,
	Andrii Nakryiko, syzbot, syzbot

On Tue, Jul 12, 2022 at 4:02 PM Song Liu <song@kernel.org> wrote:
>
> On Tue, Jul 12, 2022 at 3:09 PM Alexei Starovoitov
> <alexei.starovoitov@gmail.com> wrote:
> >
> > On Tue, Jul 5, 2022 at 5:26 PM Song Liu <song@kernel.org> wrote:
> > >
> > > syzbot reported a few issues with bpf_prog_pack [1], [2]. These are
> > > triggered when the program passed initial JIT in jit_subprogs(), but
> > > failed final pass of JIT. At this point, bpf_jit_binary_pack_free() is
> > > called before bpf_jit_binary_pack_finalize(), and the whole 2MB page is
> > > freed.
> > >
> > > Fix this with a custom bpf_jit_free() for x86_64, which calls
> > > bpf_jit_binary_pack_finalize() if necessary. Also, with custom
> > > bpf_jit_free(), bpf_prog_aux->use_bpf_prog_pack is not needed any more,
> > > remove it.
> > >
> > > Fixes: 1022a5498f6f ("bpf, x86_64: Use bpf_jit_binary_pack_alloc")
> > > [1] https://syzkaller.appspot.com/bug?extid=2f649ec6d2eea1495a8f
> > > [2] https://syzkaller.appspot.com/bug?extid=87f65c75f4a72db05445
> > > Reported-by: syzbot+2f649ec6d2eea1495a8f@syzkaller.appspotmail.com
> > > Reported-by: syzbot+87f65c75f4a72db05445@syzkaller.appspotmail.com
> > > Signed-off-by: Song Liu <song@kernel.org>
> > > ---
> > >  arch/x86/net/bpf_jit_comp.c | 25 +++++++++++++++++++++++++
> > >  include/linux/bpf.h         |  1 -
> > >  include/linux/filter.h      |  8 ++++++++
> > >  kernel/bpf/core.c           | 29 ++++++++++++-----------------
> > >  4 files changed, 45 insertions(+), 18 deletions(-)
> > >
> > > diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
> > > index c98b8c0ed3b8..c3dca4c97e48 100644
> > > --- a/arch/x86/net/bpf_jit_comp.c
> > > +++ b/arch/x86/net/bpf_jit_comp.c
> > > @@ -2492,3 +2492,28 @@ void *bpf_arch_text_copy(void *dst, void *src, size_t len)
> > >                 return ERR_PTR(-EINVAL);
> > >         return dst;
> > >  }
> > > +
> > > +void bpf_jit_free(struct bpf_prog *prog)
> > > +{
> > > +       if (prog->jited) {
> > > +               struct x64_jit_data *jit_data = prog->aux->jit_data;
> > > +               struct bpf_binary_header *hdr;
> > > +
> > > +               /*
> > > +                * If we fail the final pass of JIT (from jit_subprogs),
> > > +                * the program may not be finalized yet. Call finalize here
> > > +                * before freeing it.
> > > +                */
> > > +               if (jit_data) {
> > > +                       bpf_jit_binary_pack_finalize(prog, jit_data->header,
> > > +                                                    jit_data->rw_header);
> > > +                       kvfree(jit_data->addrs);
> > > +                       kfree(jit_data);
> > > +               }
> >
> > It looks like a workaround for missed cleanup on the JIT side.
> > When bpf_int_jit_compile() fails it is supposed to free jit_data
> > immediately.
> >
> > > passed initial JIT in jit_subprogs(), but
> > > failed final pass of JIT. At this point, bpf_jit_binary_pack_free() is
> > > called before bpf_jit_binary_pack_finalize()
> >
> > It feels that bpf_int_jit_compile() should call
> > bpf_jit_binary_pack_finalize() instead in the path where
> > it's failing.
> > I could be missing details on what exactly
> > "failed final pass of JIT" means.
>
> This only happens with multiple subprogs. In jit_subprogs(), we
> first call bpf_int_jit_compile() on each sub program. And then,
> we call it on each sub program again. jit_data is not freed in the
> first call of bpf_int_jit_compile(). Similarly we don't call
> bpf_jit_binary_pack_finalize() in the first call of bpf_int_jit_compile().
>
> If bpf_int_jit_compile() failed for one sub program, we will call
> bpf_jit_binary_pack_finalize() for this sub program. However,
> we don't have a chance to call it for other sub programs. Then
> we will hit "goto out_free" in jit_subprogs(), and call bpf_jit_free
> on some subprograms that haven't got bpf_jit_binary_pack_finalize()
> yet. So, I think bpf_jit_free is the best place we can add the extra
> check and call bpf_jit_binary_pack_finalize().
>
> Does this make sense?

Got it. I've amended the commit log with the above details.
Considering that we're at rc6 and the amount of conflicts
this patch would cause between bpf and bpf-next trees
I pushed it to bpf-next after applying manually.
Thanks.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH bpf] bpf, x86: fix freeing of not-finalized bpf_prog_pack
  2022-07-06  0:26 [PATCH bpf] bpf, x86: fix freeing of not-finalized bpf_prog_pack Song Liu
  2022-07-12 22:09 ` Alexei Starovoitov
@ 2022-07-13  0:40 ` patchwork-bot+netdevbpf
  1 sibling, 0 replies; 5+ messages in thread
From: patchwork-bot+netdevbpf @ 2022-07-13  0:40 UTC (permalink / raw)
  To: Song Liu
  Cc: bpf, daniel, kernel-team, ast, andrii,
	syzbot+2f649ec6d2eea1495a8f, syzbot+87f65c75f4a72db05445

Hello:

This patch was applied to bpf/bpf-next.git (master)
by Alexei Starovoitov <ast@kernel.org>:

On Tue, 5 Jul 2022 17:26:12 -0700 you wrote:
> syzbot reported a few issues with bpf_prog_pack [1], [2]. These are
> triggered when the program passed initial JIT in jit_subprogs(), but
> failed final pass of JIT. At this point, bpf_jit_binary_pack_free() is
> called before bpf_jit_binary_pack_finalize(), and the whole 2MB page is
> freed.
> 
> Fix this with a custom bpf_jit_free() for x86_64, which calls
> bpf_jit_binary_pack_finalize() if necessary. Also, with custom
> bpf_jit_free(), bpf_prog_aux->use_bpf_prog_pack is not needed any more,
> remove it.
> 
> [...]

Here is the summary with links:
  - [bpf] bpf, x86: fix freeing of not-finalized bpf_prog_pack
    https://git.kernel.org/bpf/bpf-next/c/1d5f82d9dd47

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2022-07-13  0:40 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-07-06  0:26 [PATCH bpf] bpf, x86: fix freeing of not-finalized bpf_prog_pack Song Liu
2022-07-12 22:09 ` Alexei Starovoitov
2022-07-12 23:01   ` Song Liu
2022-07-13  0:37     ` Alexei Starovoitov
2022-07-13  0:40 ` patchwork-bot+netdevbpf

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.