diff mbox series

libbpf: Fix integer overflow issue

Message ID 20241007164648.20926-1-richard120310@gmail.com (mailing list archive)
State Changes Requested
Delegated to: BPF
Headers show
Series libbpf: Fix integer overflow issue | expand

Checks

Context Check Description
netdev/tree_selection success Not a local patch
bpf/vmtest-bpf-next-PR success PR summary
bpf/vmtest-bpf-next-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-2 success Logs for Unittests
bpf/vmtest-bpf-next-VM_Test-3 success Logs for Validate matrix.py
bpf/vmtest-bpf-next-VM_Test-9 success Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-35 success Logs for x86_64-llvm-18 / build-release / build for x86_64 with llvm-18-O2
bpf/vmtest-bpf-next-VM_Test-12 success Logs for s390x-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-19 success Logs for x86_64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-41 success Logs for x86_64-llvm-18 / veristat
bpf/vmtest-bpf-next-VM_Test-11 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-16 success Logs for s390x-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-18 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-34 success Logs for x86_64-llvm-18 / build / build for x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-32 success Logs for x86_64-llvm-17 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-27 success Logs for x86_64-llvm-17 / build / build for x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-28 success Logs for x86_64-llvm-17 / build-release / build for x86_64 with llvm-17-O2
bpf/vmtest-bpf-next-VM_Test-4 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-5 success Logs for aarch64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-15 success Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-25 success Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-33 success Logs for x86_64-llvm-17 / veristat
bpf/vmtest-bpf-next-VM_Test-10 success Logs for aarch64-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-17 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-8 success Logs for aarch64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-7 success Logs for aarch64-gcc / test (test_progs, false, 360) / test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-13 success Logs for s390x-gcc / test (test_progs, false, 360) / test_progs on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-6 success Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-14 success Logs for s390x-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-22 success Logs for x86_64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 success Logs for x86_64-gcc / test (test_progs_no_alu32_parallel, true, 30) / test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-20 success Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-24 success Logs for x86_64-gcc / test (test_progs_parallel, true, 30) / test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-21 success Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-26 success Logs for x86_64-gcc / veristat / veristat on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-29 success Logs for x86_64-llvm-17 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-30 success Logs for x86_64-llvm-17 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-31 success Logs for x86_64-llvm-17 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-40 success Logs for x86_64-llvm-18 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-37 success Logs for x86_64-llvm-18 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-36 success Logs for x86_64-llvm-18 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-39 success Logs for x86_64-llvm-18 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-38 success Logs for x86_64-llvm-18 / test (test_progs_cpuv4, false, 360) / test_progs_cpuv4 on x86_64 with llvm-18

Commit Message

I Hsin Cheng Oct. 7, 2024, 4:46 p.m. UTC
Fix integer overflow issue discovered by coverity scan, where
"bpf_program_fd()" might return a value less than zero. Assignment of
"prog_fd" to "kern_data" will result in integer overflow in that case.

Do a pre-check after the program fd is returned, if it's negative we
should ignore this program and move on, or maybe add some error handling
mechanism here.

Signed-off-by: I Hsin Cheng <richard120310@gmail.com>
---
 tools/lib/bpf/libbpf.c | 3 +++
 1 file changed, 3 insertions(+)

Comments

Al Viro Oct. 7, 2024, 5:22 p.m. UTC | #1
On Tue, Oct 08, 2024 at 12:46:48AM +0800, I Hsin Cheng wrote:
> Fix integer overflow issue discovered by coverity scan, where
> "bpf_program_fd()" might return a value less than zero. Assignment of
> "prog_fd" to "kern_data" will result in integer overflow in that case.
> 
> Do a pre-check after the program fd is returned, if it's negative we
> should ignore this program and move on, or maybe add some error handling
> mechanism here.

We already had a mechanism there - the one you'd just disabled.
Namely, storing an unsigned long value with MSB set at given
offset.
Song Liu Oct. 7, 2024, 5:36 p.m. UTC | #2
On Mon, Oct 7, 2024 at 9:46 AM I Hsin Cheng <richard120310@gmail.com> wrote:
>
> Fix integer overflow issue discovered by coverity scan, where
> "bpf_program_fd()" might return a value less than zero. Assignment of
> "prog_fd" to "kern_data" will result in integer overflow in that case.

Has this been a real issue other than coverity scan? If so, we will need
a Fix tag.

Also, some logistics. Please be clear which tree this patch targets,
and tag the patches with "[PATCH bpf]" or "[PATCH bpf-next]".

> Do a pre-check after the program fd is returned, if it's negative we
> should ignore this program and move on, or maybe add some error handling
> mechanism here.
>
> Signed-off-by: I Hsin Cheng <richard120310@gmail.com>
> ---
>  tools/lib/bpf/libbpf.c | 3 +++
>  1 file changed, 3 insertions(+)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index a3be6f8fac09..95fb5e48e79e 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -8458,6 +8458,9 @@ static void bpf_map_prepare_vdata(const struct bpf_map *map)
>                         continue;
>
>                 prog_fd = bpf_program__fd(prog);
> +               if (prog_fd < 0)
> +                       continue;
> +

AFAICT, this only happens with non-NULL obj->gen_loader. So we can
achieve the same with something like:

diff --git i/tools/lib/bpf/libbpf.c w/tools/lib/bpf/libbpf.c
index 712b95e8891b..6756199a942f 100644
--- i/tools/lib/bpf/libbpf.c
+++ w/tools/lib/bpf/libbpf.c
@@ -8502,6 +8502,8 @@ static int bpf_object_prepare_struct_ops(struct
bpf_object *obj)
        struct bpf_map *map;
        int i;

+       if (obj->gen_loader)
+               return 0;
        for (i = 0; i < obj->nr_maps; i++) {
                map = &obj->maps[i];


Thanks,
Song
Eduard Zingerman Oct. 7, 2024, 7:13 p.m. UTC | #3
On Tue, 2024-10-08 at 00:46 +0800, I Hsin Cheng wrote:
> Fix integer overflow issue discovered by coverity scan, where
> "bpf_program_fd()" might return a value less than zero. Assignment of
> "prog_fd" to "kern_data" will result in integer overflow in that case.
> 
> Do a pre-check after the program fd is returned, if it's negative we
> should ignore this program and move on, or maybe add some error handling
> mechanism here.
> 
> Signed-off-by: I Hsin Cheng <richard120310@gmail.com>
> ---
>  tools/lib/bpf/libbpf.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index a3be6f8fac09..95fb5e48e79e 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -8458,6 +8458,9 @@ static void bpf_map_prepare_vdata(const struct bpf_map *map)
>  			continue;
>  
>  		prog_fd = bpf_program__fd(prog);
> +		if (prog_fd < 0)
> +			continue;
> +

The 'progs' variable comes from 'st_ops->progs' array.
Elements of this array are set in two places:
a. bpf_object__collect_st_ops_relos() called from
   bpf_object__collect_relos() called from
   bpf_object_open().
   This handles relocations pointing to programs in global struct ops
   maps definitions, e.g.:

    SEC(".struct_ops.link")
    struct bpf_testmod_ops testmod_1 = {
           .test_1 = (void *)test_1,     // <--- this one
           ...
    };

b. bpf_map__init_kern_struct_ops() called from
   bpf_object__init_kern_struct_ops_maps() called from
   bpf_object_load().
   This copies values set from "shadow types", e.g.:
  
    skel->struct_ops.testmod_1->test_1 = skel->some_other_prog

The bpf_map_prepare_vdata() itself is called from
bpf_object_prepare_struct_ops() called from
bpf_object_load().

The call to bpf_object_prepare_struct_ops() is preceded by a call to
bpf_object_adjust_struct_ops_autoload() (c), which in turn is preceded
by both (a) and (b). Meaning that autoload decisions are final at the
time of bpf_map_prepare_vdata() call. The (c) enables autoload for
programs referenced from any struct ops map.

Hence, I think that situation when 'prog_fd < 0' should not be
possible here => we need an error log before skipping prog_fd
(or aborting?).

(Also, please double-check what Song Liu suggests about
 obj->gen_loader, I am not familiar with that part).

>  		kern_data = st_ops->kern_vdata + st_ops->kern_func_off[i];
>  		*(unsigned long *)kern_data = prog_fd;
>  	}
Andrii Nakryiko Oct. 8, 2024, 3:42 a.m. UTC | #4
On Mon, Oct 7, 2024 at 12:13 PM Eduard Zingerman <eddyz87@gmail.com> wrote:
>
> On Tue, 2024-10-08 at 00:46 +0800, I Hsin Cheng wrote:
> > Fix integer overflow issue discovered by coverity scan, where
> > "bpf_program_fd()" might return a value less than zero. Assignment of
> > "prog_fd" to "kern_data" will result in integer overflow in that case.
> >
> > Do a pre-check after the program fd is returned, if it's negative we
> > should ignore this program and move on, or maybe add some error handling
> > mechanism here.
> >
> > Signed-off-by: I Hsin Cheng <richard120310@gmail.com>
> > ---
> >  tools/lib/bpf/libbpf.c | 3 +++
> >  1 file changed, 3 insertions(+)
> >
> > diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> > index a3be6f8fac09..95fb5e48e79e 100644
> > --- a/tools/lib/bpf/libbpf.c
> > +++ b/tools/lib/bpf/libbpf.c
> > @@ -8458,6 +8458,9 @@ static void bpf_map_prepare_vdata(const struct bpf_map *map)
> >                       continue;
> >
> >               prog_fd = bpf_program__fd(prog);
> > +             if (prog_fd < 0)
> > +                     continue;
> > +
>
> The 'progs' variable comes from 'st_ops->progs' array.
> Elements of this array are set in two places:
> a. bpf_object__collect_st_ops_relos() called from
>    bpf_object__collect_relos() called from
>    bpf_object_open().
>    This handles relocations pointing to programs in global struct ops
>    maps definitions, e.g.:
>
>     SEC(".struct_ops.link")
>     struct bpf_testmod_ops testmod_1 = {
>            .test_1 = (void *)test_1,     // <--- this one
>            ...
>     };
>
> b. bpf_map__init_kern_struct_ops() called from
>    bpf_object__init_kern_struct_ops_maps() called from
>    bpf_object_load().
>    This copies values set from "shadow types", e.g.:
>
>     skel->struct_ops.testmod_1->test_1 = skel->some_other_prog
>
> The bpf_map_prepare_vdata() itself is called from
> bpf_object_prepare_struct_ops() called from
> bpf_object_load().
>
> The call to bpf_object_prepare_struct_ops() is preceded by a call to
> bpf_object_adjust_struct_ops_autoload() (c), which in turn is preceded
> by both (a) and (b). Meaning that autoload decisions are final at the
> time of bpf_map_prepare_vdata() call. The (c) enables autoload for
> programs referenced from any struct ops map.
>
> Hence, I think that situation when 'prog_fd < 0' should not be
> possible here => we need an error log before skipping prog_fd
> (or aborting?).
>

Not sure what Eduard is suggesting here, tbh. But I think if this
actually can happen that we have a non-loaded BPF program in one of
those struct_ops slots, then let's add a test demonstrating that.

Worst case of what can happen right now is the kernel rejecting
struct_ops loading due to -22 as a program FD.

pw-bot: cr

> (Also, please double-check what Song Liu suggests about
>  obj->gen_loader, I am not familiar with that part).
>
> >               kern_data = st_ops->kern_vdata + st_ops->kern_func_off[i];
> >               *(unsigned long *)kern_data = prog_fd;
> >       }
>
>
Eduard Zingerman Oct. 8, 2024, 9:48 a.m. UTC | #5
On Mon, 2024-10-07 at 20:42 -0700, Andrii Nakryiko wrote:

[...]

> Not sure what Eduard is suggesting here, tbh. But I think if this
> actually can happen that we have a non-loaded BPF program in one of
> those struct_ops slots, then let's add a test demonstrating that.

Given the call chain listed in a previous email I think that such
situation is not possible (modulo obj->gen_loader, which I know
nothing about).

Thus I suggest to add a pr_warn() and return -EINVAL or something like
that here.

> Worst case of what can happen right now is the kernel rejecting
> struct_ops loading due to -22 as a program FD.
> 
> pw-bot: cr

[...]
Andrii Nakryiko Oct. 8, 2024, 5:21 p.m. UTC | #6
On Tue, Oct 8, 2024 at 2:49 AM Eduard Zingerman <eddyz87@gmail.com> wrote:
>
> On Mon, 2024-10-07 at 20:42 -0700, Andrii Nakryiko wrote:
>
> [...]
>
> > Not sure what Eduard is suggesting here, tbh. But I think if this
> > actually can happen that we have a non-loaded BPF program in one of
> > those struct_ops slots, then let's add a test demonstrating that.
>
> Given the call chain listed in a previous email I think that such
> situation is not possible (modulo obj->gen_loader, which I know
> nothing about).
>
> Thus I suggest to add a pr_warn() and return -EINVAL or something like
> that here.
>

That's what confused me :) If it's impossible, there is no need to
handle it, we know the FD has to be there. So I'd just not change
anything.

> > Worst case of what can happen right now is the kernel rejecting
> > struct_ops loading due to -22 as a program FD.
> >
> > pw-bot: cr
>
> [...]
>
Eduard Zingerman Oct. 8, 2024, 5:34 p.m. UTC | #7
On Tue, 2024-10-08 at 10:21 -0700, Andrii Nakryiko wrote:
> On Tue, Oct 8, 2024 at 2:49 AM Eduard Zingerman <eddyz87@gmail.com> wrote:
> > 
> > On Mon, 2024-10-07 at 20:42 -0700, Andrii Nakryiko wrote:
> > 
> > [...]
> > 
> > > Not sure what Eduard is suggesting here, tbh. But I think if this
> > > actually can happen that we have a non-loaded BPF program in one of
> > > those struct_ops slots, then let's add a test demonstrating that.
> > 
> > Given the call chain listed in a previous email I think that such
> > situation is not possible (modulo obj->gen_loader, which I know
> > nothing about).
> > 
> > Thus I suggest to add a pr_warn() and return -EINVAL or something like
> > that here.
> > 
> 
> That's what confused me :) If it's impossible, there is no need to
> handle it, we know the FD has to be there. So I'd just not change
> anything.

Granted I have a memory of a fruit fly, but it took me like half an
hour to figure out if it is possible or not, and I wrote a part of
that code. At the very least a comment is needed.
Also, adding an explicit cast should silence the tool warning.

> 
> > > Worst case of what can happen right now is the kernel rejecting
> > > struct_ops loading due to -22 as a program FD.
> > > 
> > > pw-bot: cr
> > 
> > [...]
> >
diff mbox series

Patch

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index a3be6f8fac09..95fb5e48e79e 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -8458,6 +8458,9 @@  static void bpf_map_prepare_vdata(const struct bpf_map *map)
 			continue;
 
 		prog_fd = bpf_program__fd(prog);
+		if (prog_fd < 0)
+			continue;
+
 		kern_data = st_ops->kern_vdata + st_ops->kern_func_off[i];
 		*(unsigned long *)kern_data = prog_fd;
 	}