diff mbox series

[06/17] fs/squashfs: sqfs_read_directory_table: fix memory leak

Message ID 20201014080622.14970-7-richard.genoud@posteo.net
State Superseded
Delegated to: Tom Rini
Headers show
Series fs/squashfs: fix memory leaks and introduce exists() function | expand

Commit Message

Richard Genoud Oct. 14, 2020, 8:06 a.m. UTC
pos_list wasn't freed on every error

Signed-off-by: Richard Genoud <richard.genoud@posteo.net>
---
 fs/squashfs/sqfs.c | 31 +++++++++++++++++--------------
 1 file changed, 17 insertions(+), 14 deletions(-)

Comments

Miquel Raynal Oct. 15, 2020, 1:54 p.m. UTC | #1
Hi Richard,

Richard Genoud <richard.genoud@posteo.net> wrote on Wed, 14 Oct 2020
10:06:11 +0200:

> pos_list wasn't freed on every error
> 
> Signed-off-by: Richard Genoud <richard.genoud@posteo.net>

Same comment here (and probably after as well) as in patch 05/17, not
sure this is actually relevant for the community but I prefer this:

	bar = malloc();
	...
	if (ret)
		goto free_bar;

	foo = malloc();
	...
	if (ret)
		goto free foo;

	...

	foo:
		kfree(foo);
	bar:
		kfree(bar);

than:

	foo = NULL;
	bar = NULL;

	...
	if (ret)
		goto out;
	...
	if (ret)
		goto out;
	...
		out:
	if (ret)
		kfree(...)



> ---
>  fs/squashfs/sqfs.c | 31 +++++++++++++++++--------------
>  1 file changed, 17 insertions(+), 14 deletions(-)
> 
> diff --git a/fs/squashfs/sqfs.c b/fs/squashfs/sqfs.c
> index 55d183663a8..c4d74fd4d6d 100644
> --- a/fs/squashfs/sqfs.c
> +++ b/fs/squashfs/sqfs.c
> @@ -722,6 +722,8 @@ static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
>  	unsigned long dest_len = 0;
>  	bool compressed;
>  
> +	*dir_table = NULL;
> +	*pos_list = NULL;
>  	/* DIRECTORY TABLE */
>  	table_size = get_unaligned_le64(&sblk->fragment_table_start) -
>  		get_unaligned_le64(&sblk->directory_table_start);
> @@ -736,35 +738,31 @@ static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
>  		return -ENOMEM;
>  
>  	if (sqfs_disk_read(start, n_blks, dtb) < 0)
> -		goto free_dtb;
> +		goto out;
>  
>  	/* Parse directory table (metadata block) header */
>  	ret = sqfs_read_metablock(dtb, table_offset, &compressed, &src_len);
>  	if (ret)
> -		goto free_dtb;
> +		goto out;
>  
>  	/* Calculate total size to store the whole decompressed table */
>  	metablks_count = sqfs_count_metablks(dtb, table_offset, table_size);
>  	if (metablks_count < 1)
> -		goto free_dtb;
> +		goto out;
>  
>  	*dir_table = malloc(metablks_count * SQFS_METADATA_BLOCK_SIZE);
>  	if (!*dir_table)
> -		goto free_dtb;
> +		goto out;
>  
>  	*pos_list = malloc(metablks_count * sizeof(u32));
> -	if (!*pos_list) {
> -		free(*dir_table);
> -		goto free_dtb;
> -	}
> +	if (!*pos_list)
> +		goto out;
>  
>  	ret = sqfs_get_metablk_pos(*pos_list, dtb, table_offset,
>  				   metablks_count);
>  	if (ret) {
>  		metablks_count = -1;
> -		free(*dir_table);
> -		free(*pos_list);
> -		goto free_dtb;
> +		goto out;
>  	}
>  
>  	src_table = dtb + table_offset + SQFS_HEADER_SIZE;
> @@ -780,8 +778,7 @@ static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
>  					      &dest_len, src_table, src_len);
>  			if (ret) {
>  				metablks_count = -1;
> -				free(*dir_table);
> -				goto free_dtb;
> +				goto out;
>  			}
>  
>  			if (dest_len < SQFS_METADATA_BLOCK_SIZE) {
> @@ -803,7 +800,13 @@ static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
>  		src_table += src_len + SQFS_HEADER_SIZE;
>  	}
>  
> -free_dtb:
> +out:
> +	if (metablks_count < 1) {
> +		free(*dir_table);
> +		free(*pos_list);
> +		*dir_table = NULL;
> +		*pos_list = NULL;
> +	}
>  	free(dtb);
>  
>  	return metablks_count;

Thanks,
Miquèl
Richard Genoud Oct. 15, 2020, 4:29 p.m. UTC | #2
Hi Miquel !
Thanks for your feedback.

Le 15/10/2020 à 15:54, Miquel Raynal a écrit :
> Hi Richard,
> 
> Richard Genoud <richard.genoud@posteo.net> wrote on Wed, 14 Oct 2020
> 10:06:11 +0200:
> 
>> pos_list wasn't freed on every error
>>
>> Signed-off-by: Richard Genoud <richard.genoud@posteo.net>
> 
> Same comment here (and probably after as well) as in patch 05/17, not
> sure this is actually relevant for the community but I prefer this:
> 
> 	bar = malloc();
> 	...
> 	if (ret)
> 		goto free_bar;
> 
> 	foo = malloc();
> 	...
> 	if (ret)
> 		goto free foo;
> 
> 	...
> 
> 	foo:
> 		kfree(foo);
> 	bar:
> 		kfree(bar);
> 
> than:
> 
> 	foo = NULL;
> 	bar = NULL;
> 
> 	...
> 	if (ret)
> 		goto out;
> 	...
> 	if (ret)
> 		goto out;
> 	...
> 		out:
> 	if (ret)
> 		kfree(...)

I guess it's a coding habit.
I personnaly prefer the later because I think it's less error-prone :
When moving code aroung, we don't have to move the labels and rename
the gotos.
Ex:
Let's say we have this code:
	bar = malloc();
	...
	if (ret)
		goto free_bar;

	foo = malloc();
	...
	if (ret)
		goto free_foo;
	ret = init_somthing();
	if (ret)
		goto free_foo;
	ret = dummy()
	if (ret)
		goto free_foo;

	...

	foo:
		kfree(foo);
	bar:
		kfree(bar);

And, we want to move, for whatever reason, init_something() and dummy()
before the foo allocation. We will have to change the code to:

	bar = malloc();
	...
	if (ret)
		goto free_bar;
	ret = init_somthing();
	if (ret)
		goto free_bar; // not free_foo anymore !
	ret = dummy()
	if (ret)
		goto free_bar; // ditto

	foo = malloc();
	...
	if (ret)
		goto free_foo;
	...

	foo:
		kfree(foo);
	bar:
		kfree(bar);

Worse, if we have to exchange bar and foo allocation, we'll also have
to exchange the deallocation of foo and bar and change all gotos beneath :
	foo = malloc();
	...
	if (ret)
		goto free_foo;

	bar = malloc();
	...
	if (ret)
		goto free_bar;

	ret = init_somthing();
	if (ret)
		goto free_foo; // not free_foo anymore
	ret = dummy()
	if (ret)
		goto free_foo; //ditto


	...

// oops ! we have to exchange that !
	foo:
		kfree(foo);
	bar:
		kfree(bar);


That's why I prefer only one label and setting NULL.
If I didn't convince you, I'll change it back to multiple labels :)

> 
>> ---
>>   fs/squashfs/sqfs.c | 31 +++++++++++++++++--------------
>>   1 file changed, 17 insertions(+), 14 deletions(-)
>>
>> diff --git a/fs/squashfs/sqfs.c b/fs/squashfs/sqfs.c
>> index 55d183663a8..c4d74fd4d6d 100644
>> --- a/fs/squashfs/sqfs.c
>> +++ b/fs/squashfs/sqfs.c
>> @@ -722,6 +722,8 @@ static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
>>   	unsigned long dest_len = 0;
>>   	bool compressed;
>>   
>> +	*dir_table = NULL;
>> +	*pos_list = NULL;
>>   	/* DIRECTORY TABLE */
>>   	table_size = get_unaligned_le64(&sblk->fragment_table_start) -
>>   		get_unaligned_le64(&sblk->directory_table_start);
>> @@ -736,35 +738,31 @@ static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
>>   		return -ENOMEM;
>>   
>>   	if (sqfs_disk_read(start, n_blks, dtb) < 0)
>> -		goto free_dtb;
>> +		goto out;
>>   
>>   	/* Parse directory table (metadata block) header */
>>   	ret = sqfs_read_metablock(dtb, table_offset, &compressed, &src_len);
>>   	if (ret)
>> -		goto free_dtb;
>> +		goto out;
>>   
>>   	/* Calculate total size to store the whole decompressed table */
>>   	metablks_count = sqfs_count_metablks(dtb, table_offset, table_size);
>>   	if (metablks_count < 1)
>> -		goto free_dtb;
>> +		goto out;
>>   
>>   	*dir_table = malloc(metablks_count * SQFS_METADATA_BLOCK_SIZE);
>>   	if (!*dir_table)
>> -		goto free_dtb;
>> +		goto out;
>>   
>>   	*pos_list = malloc(metablks_count * sizeof(u32));
>> -	if (!*pos_list) {
>> -		free(*dir_table);
>> -		goto free_dtb;
>> -	}
>> +	if (!*pos_list)
>> +		goto out;
>>   
>>   	ret = sqfs_get_metablk_pos(*pos_list, dtb, table_offset,
>>   				   metablks_count);
>>   	if (ret) {
>>   		metablks_count = -1;
>> -		free(*dir_table);
>> -		free(*pos_list);
>> -		goto free_dtb;
>> +		goto out;
>>   	}
>>   
>>   	src_table = dtb + table_offset + SQFS_HEADER_SIZE;
>> @@ -780,8 +778,7 @@ static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
>>   					      &dest_len, src_table, src_len);
>>   			if (ret) {
>>   				metablks_count = -1;
>> -				free(*dir_table);
>> -				goto free_dtb;
>> +				goto out;
>>   			}
>>   
>>   			if (dest_len < SQFS_METADATA_BLOCK_SIZE) {
>> @@ -803,7 +800,13 @@ static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
>>   		src_table += src_len + SQFS_HEADER_SIZE;
>>   	}
>>   
>> -free_dtb:
>> +out:
>> +	if (metablks_count < 1) {
>> +		free(*dir_table);
>> +		free(*pos_list);
>> +		*dir_table = NULL;
>> +		*pos_list = NULL;
>> +	}
>>   	free(dtb);
>>   
>>   	return metablks_count;
> 
> Thanks,
> Miquèl
> 
Thanks !
Richard
Miquel Raynal Oct. 15, 2020, 4:38 p.m. UTC | #3
Hi Richard,

Richard Genoud <richard.genoud@posteo.net> wrote on Thu, 15 Oct 2020
18:29:45 +0200:

> Hi Miquel !
> Thanks for your feedback.
> 
> Le 15/10/2020 à 15:54, Miquel Raynal a écrit :
> > Hi Richard,
> > 
> > Richard Genoud <richard.genoud@posteo.net> wrote on Wed, 14 Oct 2020
> > 10:06:11 +0200:
> >   
> >> pos_list wasn't freed on every error
> >>
> >> Signed-off-by: Richard Genoud <richard.genoud@posteo.net>  
> > 
> > Same comment here (and probably after as well) as in patch 05/17, not
> > sure this is actually relevant for the community but I prefer this:
> > 
> > 	bar = malloc();
> > 	...
> > 	if (ret)
> > 		goto free_bar;
> > 
> > 	foo = malloc();
> > 	...
> > 	if (ret)
> > 		goto free foo;
> > 
> > 	...
> > 
> > 	foo:
> > 		kfree(foo);
> > 	bar:
> > 		kfree(bar);
> > 
> > than:
> > 
> > 	foo = NULL;
> > 	bar = NULL;
> > 
> > 	...
> > 	if (ret)
> > 		goto out;
> > 	...
> > 	if (ret)
> > 		goto out;
> > 	...
> > 		out:
> > 	if (ret)
> > 		kfree(...)  
> 
> I guess it's a coding habit.
> I personnaly prefer the later because I think it's less error-prone :
> When moving code aroung, we don't have to move the labels and rename
> the gotos.
> Ex:
> Let's say we have this code:
> 	bar = malloc();
> 	...
> 	if (ret)
> 		goto free_bar;
> 
> 	foo = malloc();
> 	...
> 	if (ret)
> 		goto free_foo;
> 	ret = init_somthing();
> 	if (ret)
> 		goto free_foo;
> 	ret = dummy()
> 	if (ret)
> 		goto free_foo;
> 
> 	...
> 
> 	foo:
> 		kfree(foo);
> 	bar:
> 		kfree(bar);
> 
> And, we want to move, for whatever reason, init_something() and dummy()
> before the foo allocation. We will have to change the code to:
> 
> 	bar = malloc();
> 	...
> 	if (ret)
> 		goto free_bar;
> 	ret = init_somthing();
> 	if (ret)
> 		goto free_bar; // not free_foo anymore !
> 	ret = dummy()
> 	if (ret)
> 		goto free_bar; // ditto
> 
> 	foo = malloc();
> 	...
> 	if (ret)
> 		goto free_foo;
> 	...
> 
> 	foo:
> 		kfree(foo);
> 	bar:
> 		kfree(bar);
> 
> Worse, if we have to exchange bar and foo allocation, we'll also have
> to exchange the deallocation of foo and bar and change all gotos beneath :
> 	foo = malloc();
> 	...
> 	if (ret)
> 		goto free_foo;
> 
> 	bar = malloc();
> 	...
> 	if (ret)
> 		goto free_bar;
> 
> 	ret = init_somthing();
> 	if (ret)
> 		goto free_foo; // not free_foo anymore
> 	ret = dummy()
> 	if (ret)
> 		goto free_foo; //ditto
> 
> 
> 	...
> 
> // oops ! we have to exchange that !
> 	foo:
> 		kfree(foo);
> 	bar:
> 		kfree(bar);
> 
> 
> That's why I prefer only one label and setting NULL.
> If I didn't convince you, I'll change it back to multiple labels :)

You are right it involves less changes when editing the code. But
on the other hand it is so often written like [my proposal], that it
almost becomes a coding style choice I guess. Anyway, I don't have a
strong opinion on this so I'll let you choose the best approach from
your point of view, unless you get other comments sharing my thoughts.

Thanks anyway for the cleanup :)

Cheers,
Miquèl
Richard Genoud Oct. 16, 2020, 12:31 p.m. UTC | #4
Le 15/10/2020 à 18:38, Miquel Raynal a écrit :
> Hi Richard,

>> That's why I prefer only one label and setting NULL.
>> If I didn't convince you, I'll change it back to multiple labels :)
> 
> You are right it involves less changes when editing the code. But
> on the other hand it is so often written like [my proposal], that it
> almost becomes a coding style choice I guess. Anyway, I don't have a
> strong opinion on this so I'll let you choose the best approach from
> your point of view, unless you get other comments sharing my thoughts.
> 
> Thanks anyway for the cleanup :)
> 
> Cheers,
> Miquèl
> 
Hum. You're right, consistency is a good thing.
I looked around in other files, but I don't think a standard emerges.
How about I resend the series changing all the remaining functions
(there's only 3 remaining) to the "NULL/goto out" scheme ?

Regards,
Richard
Miquel Raynal Oct. 16, 2020, 12:34 p.m. UTC | #5
Hi Richard,

Richard Genoud <richard.genoud@posteo.net> wrote on Fri, 16 Oct 2020
14:31:53 +0200:

> Le 15/10/2020 à 18:38, Miquel Raynal a écrit :
> > Hi Richard,  
> 
> >> That's why I prefer only one label and setting NULL.
> >> If I didn't convince you, I'll change it back to multiple labels :)  
> > 
> > You are right it involves less changes when editing the code. But
> > on the other hand it is so often written like [my proposal], that it
> > almost becomes a coding style choice I guess. Anyway, I don't have a
> > strong opinion on this so I'll let you choose the best approach from
> > your point of view, unless you get other comments sharing my thoughts.
> > 
> > Thanks anyway for the cleanup :)
> > 
> > Cheers,
> > Miquèl
> >   
> Hum. You're right, consistency is a good thing.
> I looked around in other files, but I don't think a standard emerges.
> How about I resend the series changing all the remaining functions
> (there's only 3 remaining) to the "NULL/goto out" scheme ?
> 

Sure :)  Maybe you can wait a bit for more feedback before resending
the 18-patch series though.

Cheers,
Miquèl
diff mbox series

Patch

diff --git a/fs/squashfs/sqfs.c b/fs/squashfs/sqfs.c
index 55d183663a8..c4d74fd4d6d 100644
--- a/fs/squashfs/sqfs.c
+++ b/fs/squashfs/sqfs.c
@@ -722,6 +722,8 @@  static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
 	unsigned long dest_len = 0;
 	bool compressed;
 
+	*dir_table = NULL;
+	*pos_list = NULL;
 	/* DIRECTORY TABLE */
 	table_size = get_unaligned_le64(&sblk->fragment_table_start) -
 		get_unaligned_le64(&sblk->directory_table_start);
@@ -736,35 +738,31 @@  static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
 		return -ENOMEM;
 
 	if (sqfs_disk_read(start, n_blks, dtb) < 0)
-		goto free_dtb;
+		goto out;
 
 	/* Parse directory table (metadata block) header */
 	ret = sqfs_read_metablock(dtb, table_offset, &compressed, &src_len);
 	if (ret)
-		goto free_dtb;
+		goto out;
 
 	/* Calculate total size to store the whole decompressed table */
 	metablks_count = sqfs_count_metablks(dtb, table_offset, table_size);
 	if (metablks_count < 1)
-		goto free_dtb;
+		goto out;
 
 	*dir_table = malloc(metablks_count * SQFS_METADATA_BLOCK_SIZE);
 	if (!*dir_table)
-		goto free_dtb;
+		goto out;
 
 	*pos_list = malloc(metablks_count * sizeof(u32));
-	if (!*pos_list) {
-		free(*dir_table);
-		goto free_dtb;
-	}
+	if (!*pos_list)
+		goto out;
 
 	ret = sqfs_get_metablk_pos(*pos_list, dtb, table_offset,
 				   metablks_count);
 	if (ret) {
 		metablks_count = -1;
-		free(*dir_table);
-		free(*pos_list);
-		goto free_dtb;
+		goto out;
 	}
 
 	src_table = dtb + table_offset + SQFS_HEADER_SIZE;
@@ -780,8 +778,7 @@  static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
 					      &dest_len, src_table, src_len);
 			if (ret) {
 				metablks_count = -1;
-				free(*dir_table);
-				goto free_dtb;
+				goto out;
 			}
 
 			if (dest_len < SQFS_METADATA_BLOCK_SIZE) {
@@ -803,7 +800,13 @@  static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
 		src_table += src_len + SQFS_HEADER_SIZE;
 	}
 
-free_dtb:
+out:
+	if (metablks_count < 1) {
+		free(*dir_table);
+		free(*pos_list);
+		*dir_table = NULL;
+		*pos_list = NULL;
+	}
 	free(dtb);
 
 	return metablks_count;