diff mbox series

[bpf,1/2] libbpf: fix removal of inner map in bpf_object__create_map

Message ID 20210714165440.472566-2-m@lambda.lt (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series libbpf: fix inner map removal in bpf_object__create_map | expand

Checks

Context Check Description
netdev/cover_letter success Link
netdev/fixes_present success Link
netdev/patch_count success Link
netdev/tree_selection success Clearly marked for bpf
netdev/subject_prefix success Link
netdev/cc_maintainers fail 1 blamed authors not CCed: toke@redhat.com; 7 maintainers not CCed: netdev@vger.kernel.org yhs@fb.com kpsingh@kernel.org kafai@fb.com john.fastabend@gmail.com songliubraving@fb.com toke@redhat.com
netdev/source_inline success Was 0 now: 0
netdev/verify_signedoff success Link
netdev/module_param success Was 0 now: 0
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/verify_fixes success Link
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 23 lines checked
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/header_inline success Link

Commit Message

Martynas Pumputis July 14, 2021, 4:54 p.m. UTC
If creating an outer map of a BTF-defined map-in-map fails (via
bpf_object__create_map()), then the previously created its inner map
won't be destroyed.

Fix this by ensuring that the destroy routines are not bypassed in the
case of a failure.

Fixes: 646f02ffdd49c ("libbpf: Add BTF-defined map-in-map support")
Reported-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Martynas Pumputis <m@lambda.lt>
---
 tools/lib/bpf/libbpf.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

Comments

John Fastabend July 15, 2021, 12:30 a.m. UTC | #1
Martynas Pumputis wrote:
> If creating an outer map of a BTF-defined map-in-map fails (via
> bpf_object__create_map()), then the previously created its inner map
> won't be destroyed.
> 
> Fix this by ensuring that the destroy routines are not bypassed in the
> case of a failure.
> 
> Fixes: 646f02ffdd49c ("libbpf: Add BTF-defined map-in-map support")
> Reported-by: Andrii Nakryiko <andrii@kernel.org>
> Signed-off-by: Martynas Pumputis <m@lambda.lt>
> ---
>  tools/lib/bpf/libbpf.c | 5 +++--
>  1 file changed, 3 insertions(+), 2 deletions(-)
> 
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 6f5e2757bb3c..1a840e81ea0a 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -4479,6 +4479,7 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
>  {
>  	struct bpf_create_map_attr create_attr;
>  	struct bpf_map_def *def = &map->def;
> +	int ret = 0;
>  
>  	memset(&create_attr, 0, sizeof(create_attr));
>  
> @@ -4561,7 +4562,7 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
>  	}
>  
>  	if (map->fd < 0)
> -		return -errno;
> +		ret = -errno;
>  

I'm trying to track this down, not being overly familiar with this bit of
code.

We entered bpf_object__create_map with map->inner_map potentially set and
then here we are going to zfree(&map->inner_map). I'm trying to track
down if this is problematic, I guess not? But seems like we could
also free a map here that we didn't create from this call in the above
logic.

>  	if (bpf_map_type__is_map_in_map(def->type) && map->inner_map) {

        if (bpf_map_type__is_map_in_map(def->type) && map->inner_map) {
                if (obj->gen_loader)
                        map->inner_map->fd = -1;
                bpf_map__destroy(map->inner_map);
                zfree(&map->inner_map);
        }


Also not sure here, sorry didn't have time to follow too thoroughly
will check again later. But, the 'map->inner_map->fd = -1' is going to
cause bpf_map__destroy to bypass the close(fd) as I understand it.
So are we leaking an fd if the inner_map->fd is coming from above
create? Or maybe never happens because obj->gen_loader is NULL?

Thanks!


>  		if (obj->gen_loader)
> @@ -4570,7 +4571,7 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
>  		zfree(&map->inner_map);
>  	}
>  
> -	return 0;
> +	return ret;
>  }
>  
>  static int init_map_slots(struct bpf_object *obj, struct bpf_map *map)
> -- 
> 2.32.0
>
Martynas Pumputis July 15, 2021, 10:06 a.m. UTC | #2
On 7/15/21 2:30 AM, John Fastabend wrote:
> Martynas Pumputis wrote:
>> If creating an outer map of a BTF-defined map-in-map fails (via
>> bpf_object__create_map()), then the previously created its inner map
>> won't be destroyed.
>>
>> Fix this by ensuring that the destroy routines are not bypassed in the
>> case of a failure.
>>
>> Fixes: 646f02ffdd49c ("libbpf: Add BTF-defined map-in-map support")
>> Reported-by: Andrii Nakryiko <andrii@kernel.org>
>> Signed-off-by: Martynas Pumputis <m@lambda.lt>
>> ---
>>   tools/lib/bpf/libbpf.c | 5 +++--
>>   1 file changed, 3 insertions(+), 2 deletions(-)
>>
>> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
>> index 6f5e2757bb3c..1a840e81ea0a 100644
>> --- a/tools/lib/bpf/libbpf.c
>> +++ b/tools/lib/bpf/libbpf.c
>> @@ -4479,6 +4479,7 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
>>   {
>>   	struct bpf_create_map_attr create_attr;
>>   	struct bpf_map_def *def = &map->def;
>> +	int ret = 0;
>>   
>>   	memset(&create_attr, 0, sizeof(create_attr));
>>   
>> @@ -4561,7 +4562,7 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
>>   	}
>>   
>>   	if (map->fd < 0)
>> -		return -errno;
>> +		ret = -errno;
>>   
> 
> I'm trying to track this down, not being overly familiar with this bit of
> code.
> 
> We entered bpf_object__create_map with map->inner_map potentially set and
> then here we are going to zfree(&map->inner_map). I'm trying to track
> down if this is problematic, I guess not? But seems like we could
> also free a map here that we didn't create from this call in the above
> logic.
> 

Keep in mind that we free the inner map anyway if the creation of the 
outer map was successful. Also, we don't try to recreate the map if any 
of the steps has failed. So I think it should not be problematic.


>>   	if (bpf_map_type__is_map_in_map(def->type) && map->inner_map) {
> 
>          if (bpf_map_type__is_map_in_map(def->type) && map->inner_map) {
>                  if (obj->gen_loader)
>                          map->inner_map->fd = -1;
>                  bpf_map__destroy(map->inner_map);
>                  zfree(&map->inner_map);
>          }
> 
> 
> Also not sure here, sorry didn't have time to follow too thoroughly
> will check again later. But, the 'map->inner_map->fd = -1' is going to
> cause bpf_map__destroy to bypass the close(fd) as I understand it.
> So are we leaking an fd if the inner_map->fd is coming from above
> create? Or maybe never happens because obj->gen_loader is NULL?

I think in the case of obj->gen_loader, we don't need to close the FD of 
any map, as the creation of maps will happen at a later stage in the 
kernel: 
https://lore.kernel.org/bpf/20210514003623.28033-15-alexei.starovoitov@gmail.com/.

> 
> Thanks!
> 
> 
>>   		if (obj->gen_loader)
>> @@ -4570,7 +4571,7 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
>>   		zfree(&map->inner_map);
>>   	}
>>   
>> -	return 0;
>> +	return ret;
>>   }
>>   
>>   static int init_map_slots(struct bpf_object *obj, struct bpf_map *map)
>> -- 
>> 2.32.0
>>
> 
>
John Fastabend July 15, 2021, 7:59 p.m. UTC | #3
Martynas Pumputis wrote:
> 
> 
> On 7/15/21 2:30 AM, John Fastabend wrote:
> > Martynas Pumputis wrote:
> >> If creating an outer map of a BTF-defined map-in-map fails (via
> >> bpf_object__create_map()), then the previously created its inner map
> >> won't be destroyed.
> >>
> >> Fix this by ensuring that the destroy routines are not bypassed in the
> >> case of a failure.
> >>
> >> Fixes: 646f02ffdd49c ("libbpf: Add BTF-defined map-in-map support")
> >> Reported-by: Andrii Nakryiko <andrii@kernel.org>
> >> Signed-off-by: Martynas Pumputis <m@lambda.lt>
> >> ---
> >>   tools/lib/bpf/libbpf.c | 5 +++--
> >>   1 file changed, 3 insertions(+), 2 deletions(-)
> >>
> >> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> >> index 6f5e2757bb3c..1a840e81ea0a 100644
> >> --- a/tools/lib/bpf/libbpf.c
> >> +++ b/tools/lib/bpf/libbpf.c
> >> @@ -4479,6 +4479,7 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
> >>   {
> >>   	struct bpf_create_map_attr create_attr;
> >>   	struct bpf_map_def *def = &map->def;
> >> +	int ret = 0;
> >>   
> >>   	memset(&create_attr, 0, sizeof(create_attr));
> >>   
> >> @@ -4561,7 +4562,7 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
> >>   	}
> >>   
> >>   	if (map->fd < 0)
> >> -		return -errno;
> >> +		ret = -errno;
> >>   
> > 
> > I'm trying to track this down, not being overly familiar with this bit of
> > code.
> > 
> > We entered bpf_object__create_map with map->inner_map potentially set and
> > then here we are going to zfree(&map->inner_map). I'm trying to track
> > down if this is problematic, I guess not? But seems like we could
> > also free a map here that we didn't create from this call in the above
> > logic.
> > 
> 
> Keep in mind that we free the inner map anyway if the creation of the 
> outer map was successful. Also, we don't try to recreate the map if any 
> of the steps has failed. So I think it should not be problematic.

Maybe not problematic, but I'm still missing a bit of detail here.
We create an inner map then immediately destroy it? I'll need to
reread to understand the why part.

> 
> 
> >>   	if (bpf_map_type__is_map_in_map(def->type) && map->inner_map) {
> > 
> >          if (bpf_map_type__is_map_in_map(def->type) && map->inner_map) {
> >                  if (obj->gen_loader)
> >                          map->inner_map->fd = -1;
> >                  bpf_map__destroy(map->inner_map);
> >                  zfree(&map->inner_map);
> >          }
> > 
> > 
> > Also not sure here, sorry didn't have time to follow too thoroughly
> > will check again later. But, the 'map->inner_map->fd = -1' is going to
> > cause bpf_map__destroy to bypass the close(fd) as I understand it.
> > So are we leaking an fd if the inner_map->fd is coming from above
> > create? Or maybe never happens because obj->gen_loader is NULL?
> 
> I think in the case of obj->gen_loader, we don't need to close the FD of 
> any map, as the creation of maps will happen at a later stage in the 
> kernel: 
> https://lore.kernel.org/bpf/20210514003623.28033-15-alexei.starovoitov@gmail.com/.

+1

> 
> > 
> > Thanks!
> > 
> >
John Fastabend July 16, 2021, 3:06 a.m. UTC | #4
John Fastabend wrote:
> Martynas Pumputis wrote:
> > 
> > 
> > On 7/15/21 2:30 AM, John Fastabend wrote:
> > > Martynas Pumputis wrote:
> > >> If creating an outer map of a BTF-defined map-in-map fails (via
> > >> bpf_object__create_map()), then the previously created its inner map
> > >> won't be destroyed.
> > >>
> > >> Fix this by ensuring that the destroy routines are not bypassed in the
> > >> case of a failure.
> > >>
> > >> Fixes: 646f02ffdd49c ("libbpf: Add BTF-defined map-in-map support")
> > >> Reported-by: Andrii Nakryiko <andrii@kernel.org>
> > >> Signed-off-by: Martynas Pumputis <m@lambda.lt>
> > >> ---
> > >>   tools/lib/bpf/libbpf.c | 5 +++--
> > >>   1 file changed, 3 insertions(+), 2 deletions(-)
> > >>
> > >> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> > >> index 6f5e2757bb3c..1a840e81ea0a 100644
> > >> --- a/tools/lib/bpf/libbpf.c
> > >> +++ b/tools/lib/bpf/libbpf.c
> > >> @@ -4479,6 +4479,7 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
> > >>   {
> > >>   	struct bpf_create_map_attr create_attr;
> > >>   	struct bpf_map_def *def = &map->def;
> > >> +	int ret = 0;
> > >>   
> > >>   	memset(&create_attr, 0, sizeof(create_attr));
> > >>   
> > >> @@ -4561,7 +4562,7 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
> > >>   	}
> > >>   
> > >>   	if (map->fd < 0)
> > >> -		return -errno;
> > >> +		ret = -errno;
> > >>   
> > > 
> > > I'm trying to track this down, not being overly familiar with this bit of
> > > code.
> > > 
> > > We entered bpf_object__create_map with map->inner_map potentially set and
> > > then here we are going to zfree(&map->inner_map). I'm trying to track
> > > down if this is problematic, I guess not? But seems like we could
> > > also free a map here that we didn't create from this call in the above
> > > logic.
> > > 
> > 
> > Keep in mind that we free the inner map anyway if the creation of the 
> > outer map was successful. Also, we don't try to recreate the map if any 
> > of the steps has failed. So I think it should not be problematic.
> 
> Maybe not problematic, but I'm still missing a bit of detail here.
> We create an inner map then immediately destroy it? I'll need to
> reread to understand the why part.

OK understand now ;)
John Fastabend July 16, 2021, 3:09 a.m. UTC | #5
Martynas Pumputis wrote:
> If creating an outer map of a BTF-defined map-in-map fails (via
> bpf_object__create_map()), then the previously created its inner map
> won't be destroyed.
> 
> Fix this by ensuring that the destroy routines are not bypassed in the
> case of a failure.
> 
> Fixes: 646f02ffdd49c ("libbpf: Add BTF-defined map-in-map support")
> Reported-by: Andrii Nakryiko <andrii@kernel.org>
> Signed-off-by: Martynas Pumputis <m@lambda.lt>
> ---

LGTM.

Acked-by: John Fastabend <john.fastabend@gmail.com>
Andrii Nakryiko July 16, 2021, 5:27 a.m. UTC | #6
On Wed, Jul 14, 2021 at 9:52 AM Martynas Pumputis <m@lambda.lt> wrote:
>
> If creating an outer map of a BTF-defined map-in-map fails (via
> bpf_object__create_map()), then the previously created its inner map
> won't be destroyed.
>
> Fix this by ensuring that the destroy routines are not bypassed in the
> case of a failure.
>
> Fixes: 646f02ffdd49c ("libbpf: Add BTF-defined map-in-map support")
> Reported-by: Andrii Nakryiko <andrii@kernel.org>
> Signed-off-by: Martynas Pumputis <m@lambda.lt>
> ---
>  tools/lib/bpf/libbpf.c | 5 +++--
>  1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 6f5e2757bb3c..1a840e81ea0a 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -4479,6 +4479,7 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
>  {
>         struct bpf_create_map_attr create_attr;
>         struct bpf_map_def *def = &map->def;
> +       int ret = 0;
>
>         memset(&create_attr, 0, sizeof(create_attr));
>
> @@ -4561,7 +4562,7 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
>         }
>
>         if (map->fd < 0)
> -               return -errno;
> +               ret = -errno;

Oh, isn't this a complicated function, eh? I stared at the code for a
while until I understood the whole idea with map->inner_map handling
there.

I think your change is correct, I'd just love you to consolidate all
those "int err" definitions, and use just one throughout this
function. It will clean up two other if() blocks, and in this case
"err" name is more appropriate, because it always is <= 0.

>
>         if (bpf_map_type__is_map_in_map(def->type) && map->inner_map) {
>                 if (obj->gen_loader)
> @@ -4570,7 +4571,7 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
>                 zfree(&map->inner_map);
>         }
>
> -       return 0;
> +       return ret;
>  }
>
>  static int init_map_slots(struct bpf_object *obj, struct bpf_map *map)
> --
> 2.32.0
>
Martynas Pumputis July 16, 2021, 3:24 p.m. UTC | #7
On 7/16/21 7:27 AM, Andrii Nakryiko wrote:
> On Wed, Jul 14, 2021 at 9:52 AM Martynas Pumputis <m@lambda.lt> wrote:
>>
>> If creating an outer map of a BTF-defined map-in-map fails (via
>> bpf_object__create_map()), then the previously created its inner map
>> won't be destroyed.
>>
>> Fix this by ensuring that the destroy routines are not bypassed in the
>> case of a failure.
>>
>> Fixes: 646f02ffdd49c ("libbpf: Add BTF-defined map-in-map support")
>> Reported-by: Andrii Nakryiko <andrii@kernel.org>
>> Signed-off-by: Martynas Pumputis <m@lambda.lt>
>> ---
>>   tools/lib/bpf/libbpf.c | 5 +++--
>>   1 file changed, 3 insertions(+), 2 deletions(-)
>>
>> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
>> index 6f5e2757bb3c..1a840e81ea0a 100644
>> --- a/tools/lib/bpf/libbpf.c
>> +++ b/tools/lib/bpf/libbpf.c
>> @@ -4479,6 +4479,7 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
>>   {
>>          struct bpf_create_map_attr create_attr;
>>          struct bpf_map_def *def = &map->def;
>> +       int ret = 0;
>>
>>          memset(&create_attr, 0, sizeof(create_attr));
>>
>> @@ -4561,7 +4562,7 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
>>          }
>>
>>          if (map->fd < 0)
>> -               return -errno;
>> +               ret = -errno;
> 
> Oh, isn't this a complicated function, eh? I stared at the code for a
> while until I understood the whole idea with map->inner_map handling
> there.
> 
> I think your change is correct, I'd just love you to consolidate all
> those "int err" definitions, and use just one throughout this
> function. It will clean up two other if() blocks, and in this case
> "err" name is more appropriate, because it always is <= 0.

Good idea. I will send v2 once we have agreed on the selftesting issue.

> 
>>
>>          if (bpf_map_type__is_map_in_map(def->type) && map->inner_map) {
>>                  if (obj->gen_loader)
>> @@ -4570,7 +4571,7 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
>>                  zfree(&map->inner_map);
>>          }
>>
>> -       return 0;
>> +       return ret;
>>   }
>>
>>   static int init_map_slots(struct bpf_object *obj, struct bpf_map *map)
>> --
>> 2.32.0
>>
diff mbox series

Patch

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 6f5e2757bb3c..1a840e81ea0a 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -4479,6 +4479,7 @@  static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
 {
 	struct bpf_create_map_attr create_attr;
 	struct bpf_map_def *def = &map->def;
+	int ret = 0;
 
 	memset(&create_attr, 0, sizeof(create_attr));
 
@@ -4561,7 +4562,7 @@  static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
 	}
 
 	if (map->fd < 0)
-		return -errno;
+		ret = -errno;
 
 	if (bpf_map_type__is_map_in_map(def->type) && map->inner_map) {
 		if (obj->gen_loader)
@@ -4570,7 +4571,7 @@  static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
 		zfree(&map->inner_map);
 	}
 
-	return 0;
+	return ret;
 }
 
 static int init_map_slots(struct bpf_object *obj, struct bpf_map *map)