diff mbox series

[bpf-next,v5,3/6] libbpf: Convert st_ops->data to shadow type.

Message ID 20240227010432.714127-4-thinker.li@gmail.com (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series Create shadow types for struct_ops maps in skeletons | expand

Checks

Context Check Description
bpf/vmtest-bpf-next-PR success PR summary
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for bpf-next, async
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 8 this patch: 8
netdev/build_tools success Errors and warnings before: 1 this patch: 1
netdev/cc_maintainers warning 8 maintainers not CCed: jolsa@kernel.org daniel@iogearbox.net john.fastabend@gmail.com yonghong.song@linux.dev sdf@google.com eddyz87@gmail.com kpsingh@kernel.org haoluo@google.com
netdev/build_clang success Errors and warnings before: 8 this patch: 8
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 8 this patch: 8
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 45 lines checked
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next-VM_Test-2 success Logs for Unittests
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-next-VM_Test-3 success Logs for Validate matrix.py
bpf/vmtest-bpf-next-VM_Test-5 success Logs for aarch64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-4 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-10 success Logs for aarch64-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-12 success Logs for s390x-gcc / build-release
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-11 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-17 success Logs for s390x-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-19 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-18 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-20 success Logs for x86_64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-28 success Logs for x86_64-llvm-17 / build / build for x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-35 success Logs for x86_64-llvm-18 / build / build for x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-34 success Logs for x86_64-llvm-17 / veristat
bpf/vmtest-bpf-next-VM_Test-42 success Logs for x86_64-llvm-18 / veristat
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-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_maps, false, 360) / test_maps on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-16 success Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-30 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-33 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-15 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-21 success Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-22 success Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 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-24 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-25 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-26 success Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-27 success Logs for x86_64-gcc / veristat / veristat on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-31 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-32 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-36 success Logs for x86_64-llvm-18 / build-release / build for x86_64 with llvm-18 and -O2 optimization
bpf/vmtest-bpf-next-VM_Test-37 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-38 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-39 success Logs for x86_64-llvm-18 / test (test_progs_cpuv4, false, 360) / test_progs_cpuv4 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-40 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-41 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-29 success Logs for x86_64-llvm-17 / build-release / build for x86_64 with llvm-17 and -O2 optimization
bpf/vmtest-bpf-next-VM_Test-14 success Logs for s390x-gcc / test (test_progs, false, 360) / test_progs on s390x with gcc

Commit Message

Kui-Feng Lee Feb. 27, 2024, 1:04 a.m. UTC
Convert st_ops->data to the shadow type of the struct_ops map. The shadow
type of a struct_ops type is a variant of the original struct type
providing a way to access/change the values in the maps of the struct_ops
type.

bpf_map__initial_value() will return st_ops->data for struct_ops types. The
skeleton is going to use it as the pointer to the shadow type of the
original struct type.

One of the main differences between the original struct type and the shadow
type is that all function pointers of the shadow type are converted to
pointers of struct bpf_program. Users can replace these bpf_program
pointers with other BPF programs. The st_ops->progs[] will be updated
before updating the value of a map to reflect the changes made by users.

Signed-off-by: Kui-Feng Lee <thinker.li@gmail.com>
---
 tools/lib/bpf/libbpf.c | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

Comments

Andrii Nakryiko Feb. 28, 2024, 5:58 p.m. UTC | #1
On Mon, Feb 26, 2024 at 5:04 PM Kui-Feng Lee <thinker.li@gmail.com> wrote:
>
> Convert st_ops->data to the shadow type of the struct_ops map. The shadow
> type of a struct_ops type is a variant of the original struct type
> providing a way to access/change the values in the maps of the struct_ops
> type.
>
> bpf_map__initial_value() will return st_ops->data for struct_ops types. The
> skeleton is going to use it as the pointer to the shadow type of the
> original struct type.
>
> One of the main differences between the original struct type and the shadow
> type is that all function pointers of the shadow type are converted to
> pointers of struct bpf_program. Users can replace these bpf_program
> pointers with other BPF programs. The st_ops->progs[] will be updated
> before updating the value of a map to reflect the changes made by users.
>
> Signed-off-by: Kui-Feng Lee <thinker.li@gmail.com>
> ---
>  tools/lib/bpf/libbpf.c | 21 ++++++++++++++++++++-
>  1 file changed, 20 insertions(+), 1 deletion(-)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 465b50235a01..2d22344fb127 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -1102,6 +1102,9 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)
>                 if (btf_is_ptr(mtype)) {
>                         struct bpf_program *prog;
>
> +                       /* Update the value from the shadow type */
> +                       st_ops->progs[i] = *(struct bpf_program **)mdata;
> +

it's unsettling to just cast a pointer like this without any
validation. It's too easy for users to set either some garbage there
or struct bpf_program * pointer from some other skeleton.

Luckily, validation is pretty simple, we can just iterate over all
bpf_object's programs and check if any of them matches the value of
the mdata pointer. If not, error out with meaningful error.

Also, even if the bpf_program pointer is correct, it could be a
program of the wrong type, so I think we should add a bit more
validation here, given these pointers are set by users directly after
bpf_object is opened.

>                         prog = st_ops->progs[i];
>                         if (!prog)
>                                 continue;
> @@ -9308,7 +9311,9 @@ static struct bpf_map *find_struct_ops_map_by_offset(struct bpf_object *obj,
>         return NULL;
>  }
>
> -/* Collect the reloc from ELF and populate the st_ops->progs[] */
> +/* Collect the reloc from ELF, populate the st_ops->progs[], and update
> + * st_ops->data for shadow type.
> + */
>  static int bpf_object__collect_st_ops_relos(struct bpf_object *obj,
>                                             Elf64_Shdr *shdr, Elf_Data *data)
>  {
> @@ -9422,6 +9427,14 @@ static int bpf_object__collect_st_ops_relos(struct bpf_object *obj,
>                 }
>
>                 st_ops->progs[member_idx] = prog;
> +
> +               /* st_ops->data will be expose to users, being returned by

typo: exposed

> +                * bpf_map__initial_value() as a pointer to the shadow
> +                * type. All function pointers in the original struct type
> +                * should be converted to a pointer to struct bpf_program
> +                * in the shadow type.
> +                */
> +               *((struct bpf_program **)(st_ops->data + moff)) = prog;
>         }
>
>         return 0;
> @@ -9880,6 +9893,12 @@ int bpf_map__set_initial_value(struct bpf_map *map,
>
>  void *bpf_map__initial_value(struct bpf_map *map, size_t *psize)
>  {
> +       if (bpf_map__is_struct_ops(map)) {
> +               if (psize)
> +                       *psize = map->def.value_size;
> +               return map->st_ops->data;
> +       }
> +
>         if (!map->mmaped)
>                 return NULL;
>         *psize = map->def.value_size;
> --
> 2.34.1
>
Martin KaFai Lau Feb. 28, 2024, 6:18 p.m. UTC | #2
On 2/28/24 9:58 AM, Andrii Nakryiko wrote:
> Also, even if the bpf_program pointer is correct, it could be a
> program of the wrong type, so I think we should add a bit more
> validation here, given these pointers are set by users directly after
> bpf_object is opened.

+1. The checking that is done at open time (collect_st_ops_relos) should have 
been moved here (init_kern_struct_ops, i.e. load time). I saw Eduard (thanks!) 
has already done that in his set: 
https://lore.kernel.org/bpf/20240227204556.17524-3-eddyz87@gmail.com/
Kui-Feng Lee Feb. 28, 2024, 7:27 p.m. UTC | #3
On 2/28/24 09:58, Andrii Nakryiko wrote:
> On Mon, Feb 26, 2024 at 5:04 PM Kui-Feng Lee <thinker.li@gmail.com> wrote:
>>
>> Convert st_ops->data to the shadow type of the struct_ops map. The shadow
>> type of a struct_ops type is a variant of the original struct type
>> providing a way to access/change the values in the maps of the struct_ops
>> type.
>>
>> bpf_map__initial_value() will return st_ops->data for struct_ops types. The
>> skeleton is going to use it as the pointer to the shadow type of the
>> original struct type.
>>
>> One of the main differences between the original struct type and the shadow
>> type is that all function pointers of the shadow type are converted to
>> pointers of struct bpf_program. Users can replace these bpf_program
>> pointers with other BPF programs. The st_ops->progs[] will be updated
>> before updating the value of a map to reflect the changes made by users.
>>
>> Signed-off-by: Kui-Feng Lee <thinker.li@gmail.com>
>> ---
>>   tools/lib/bpf/libbpf.c | 21 ++++++++++++++++++++-
>>   1 file changed, 20 insertions(+), 1 deletion(-)
>>
>> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
>> index 465b50235a01..2d22344fb127 100644
>> --- a/tools/lib/bpf/libbpf.c
>> +++ b/tools/lib/bpf/libbpf.c
>> @@ -1102,6 +1102,9 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)
>>                  if (btf_is_ptr(mtype)) {
>>                          struct bpf_program *prog;
>>
>> +                       /* Update the value from the shadow type */
>> +                       st_ops->progs[i] = *(struct bpf_program **)mdata;
>> +
> 
> it's unsettling to just cast a pointer like this without any
> validation. It's too easy for users to set either some garbage there
> or struct bpf_program * pointer from some other skeleton.
> 
> Luckily, validation is pretty simple, we can just iterate over all
> bpf_object's programs and check if any of them matches the value of
> the mdata pointer. If not, error out with meaningful error.

Make sense to me.

> 
> Also, even if the bpf_program pointer is correct, it could be a
> program of the wrong type, so I think we should add a bit more
> validation here, given these pointers are set by users directly after
> bpf_object is opened.


Agree!
Although this will be checked by the kernel, it makes sense to check at
the user space to provide a more meaningful error.

> 
>>                          prog = st_ops->progs[i];
>>                          if (!prog)
>>                                  continue;
>> @@ -9308,7 +9311,9 @@ static struct bpf_map *find_struct_ops_map_by_offset(struct bpf_object *obj,
>>          return NULL;
>>   }
>>
>> -/* Collect the reloc from ELF and populate the st_ops->progs[] */
>> +/* Collect the reloc from ELF, populate the st_ops->progs[], and update
>> + * st_ops->data for shadow type.
>> + */
>>   static int bpf_object__collect_st_ops_relos(struct bpf_object *obj,
>>                                              Elf64_Shdr *shdr, Elf_Data *data)
>>   {
>> @@ -9422,6 +9427,14 @@ static int bpf_object__collect_st_ops_relos(struct bpf_object *obj,
>>                  }
>>
>>                  st_ops->progs[member_idx] = prog;
>> +
>> +               /* st_ops->data will be expose to users, being returned by
> 
> typo: exposed
> 
>> +                * bpf_map__initial_value() as a pointer to the shadow
>> +                * type. All function pointers in the original struct type
>> +                * should be converted to a pointer to struct bpf_program
>> +                * in the shadow type.
>> +                */
>> +               *((struct bpf_program **)(st_ops->data + moff)) = prog;
>>          }
>>
>>          return 0;
>> @@ -9880,6 +9893,12 @@ int bpf_map__set_initial_value(struct bpf_map *map,
>>
>>   void *bpf_map__initial_value(struct bpf_map *map, size_t *psize)
>>   {
>> +       if (bpf_map__is_struct_ops(map)) {
>> +               if (psize)
>> +                       *psize = map->def.value_size;
>> +               return map->st_ops->data;
>> +       }
>> +
>>          if (!map->mmaped)
>>                  return NULL;
>>          *psize = map->def.value_size;
>> --
>> 2.34.1
>>
diff mbox series

Patch

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 465b50235a01..2d22344fb127 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -1102,6 +1102,9 @@  static int bpf_map__init_kern_struct_ops(struct bpf_map *map)
 		if (btf_is_ptr(mtype)) {
 			struct bpf_program *prog;
 
+			/* Update the value from the shadow type */
+			st_ops->progs[i] = *(struct bpf_program **)mdata;
+
 			prog = st_ops->progs[i];
 			if (!prog)
 				continue;
@@ -9308,7 +9311,9 @@  static struct bpf_map *find_struct_ops_map_by_offset(struct bpf_object *obj,
 	return NULL;
 }
 
-/* Collect the reloc from ELF and populate the st_ops->progs[] */
+/* Collect the reloc from ELF, populate the st_ops->progs[], and update
+ * st_ops->data for shadow type.
+ */
 static int bpf_object__collect_st_ops_relos(struct bpf_object *obj,
 					    Elf64_Shdr *shdr, Elf_Data *data)
 {
@@ -9422,6 +9427,14 @@  static int bpf_object__collect_st_ops_relos(struct bpf_object *obj,
 		}
 
 		st_ops->progs[member_idx] = prog;
+
+		/* st_ops->data will be expose to users, being returned by
+		 * bpf_map__initial_value() as a pointer to the shadow
+		 * type. All function pointers in the original struct type
+		 * should be converted to a pointer to struct bpf_program
+		 * in the shadow type.
+		 */
+		*((struct bpf_program **)(st_ops->data + moff)) = prog;
 	}
 
 	return 0;
@@ -9880,6 +9893,12 @@  int bpf_map__set_initial_value(struct bpf_map *map,
 
 void *bpf_map__initial_value(struct bpf_map *map, size_t *psize)
 {
+	if (bpf_map__is_struct_ops(map)) {
+		if (psize)
+			*psize = map->def.value_size;
+		return map->st_ops->data;
+	}
+
 	if (!map->mmaped)
 		return NULL;
 	*psize = map->def.value_size;