diff mbox series

[1/3] ext4: Fix an error handling path in ext4_mb_init_cache()

Message ID 3921e725586edaca611fd3de388f917e959dc85d.1735912719.git.christophe.jaillet@wanadoo.fr
State New
Headers show
Series [1/3] ext4: Fix an error handling path in ext4_mb_init_cache() | expand

Commit Message

Christophe JAILLET Jan. 3, 2025, 1:59 p.m. UTC
'bhs' is an un-initialized pointer.
If 'groups_per_page' == 1, 'bh' is assigned its address.

Then, in the for loop below, if we early exit, either because
"group >= ngroups" or if ext4_get_group_info() fails, then it is still left
un-initialized.

It can then be used.
NULL tests could fail and lead to unexpected behavior. Also, should the
error handling path be called, brelse() would be passed a potentially
invalid value.

Better safe than sorry, just make sure it is correctly initialized to NULL.

Fixes: c9de560ded61 ("ext4: Add multi block allocator for ext4")
Signed-off-by: Christophe JAILLET <christophe.jaillet@wanadoo.fr>
---
Compile tested only.

The scenario looks possible, but I don't know if it can really happen...
---
 fs/ext4/mballoc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Comments

Dan Carpenter Jan. 6, 2025, 11:35 a.m. UTC | #1
On Fri, Jan 03, 2025 at 02:59:16PM +0100, Christophe JAILLET wrote:
> 'bhs' is an un-initialized pointer.
> If 'groups_per_page' == 1, 'bh' is assigned its address.
> 
> Then, in the for loop below, if we early exit, either because
> "group >= ngroups" or if ext4_get_group_info() fails, then it is still left
> un-initialized.
> 
> It can then be used.
> NULL tests could fail and lead to unexpected behavior. Also, should the
> error handling path be called, brelse() would be passed a potentially
> invalid value.
> 
> Better safe than sorry, just make sure it is correctly initialized to NULL.
> 
> Fixes: c9de560ded61 ("ext4: Add multi block allocator for ext4")
> Signed-off-by: Christophe JAILLET <christophe.jaillet@wanadoo.fr>
> ---
> Compile tested only.
> 
> The scenario looks possible, but I don't know if it can really happen...

A pointer to the stack can't ever equal the address of the heap so this
can't happen and it should not have a Fixes tag.

Setting the pointer to NULL probably silences a static checker warning
and these days everyone automatically zeroes stack data so it doesn't
affect the compiled code.  However generally we generally say that we
should fix the checker instead.

I've thought about this in Smatch for a while, and I think what I would
do is say that kmalloc() returns memory that is unique.  Smatch tracks if
variables are equal to each other and unique variables wouldn't be equal
to anything that came earlier.  But I haven't actually tried to implement
this.

regards,
dan carpenter
Christophe JAILLET Jan. 6, 2025, 7:16 p.m. UTC | #2
Le 06/01/2025 à 12:35, Dan Carpenter a écrit :
> On Fri, Jan 03, 2025 at 02:59:16PM +0100, Christophe JAILLET wrote:
>> 'bhs' is an un-initialized pointer.
>> If 'groups_per_page' == 1, 'bh' is assigned its address.
>>
>> Then, in the for loop below, if we early exit, either because
>> "group >= ngroups" or if ext4_get_group_info() fails, then it is still left
>> un-initialized.
>>
>> It can then be used.
>> NULL tests could fail and lead to unexpected behavior. Also, should the
>> error handling path be called, brelse() would be passed a potentially
>> invalid value.
>>
>> Better safe than sorry, just make sure it is correctly initialized to NULL.
>>
>> Fixes: c9de560ded61 ("ext4: Add multi block allocator for ext4")
>> Signed-off-by: Christophe JAILLET <christophe.jaillet@wanadoo.fr>
>> ---
>> Compile tested only.
>>
>> The scenario looks possible, but I don't know if it can really happen...
> 

Hi Dan,

> A pointer to the stack can't ever equal the address of the heap so this
> can't happen and it should not have a Fixes tag.

Not sure to understand what you mean.

I agree with your statement, but my point is that a pointer in the stack 
(and not *to* the stack) (i.e. 'bhs'), if not initialized, could in 
theory be anything. Let consider its value is 0xdeadbeef.

Then, if groups_per_page == 1, 'bh' points to the stack. Its value is 
"&bhs". And "bh[0]" is 0xdeadbeef.


Should ext4_get_group_info() fail on the first (and only) iteration of 
the for loop, then we 'continue'.
So the loop is done, and bh[0] is never updated, so still points to a 
memory holding 0xdeadbeef.

On the next for loop, on the first (and only) iteration, bh[0] is not 
NULL (it is 0xdeadbeef), so we call:
	ext4_wait_block_bitmap(..., 0xdeadbeef);

If we branch to the error handling path, it would also lead to calling
	brelse(bh[0]), that is to say brelse(0xdeadbeef);


Hoping my analysis is correct, I hope my reasoning is clearer.


That's the theory.
In practice, see below. Certainly harmless thanks to compilers, but 
still a UB for me, so should need a Fixes and a backport (it can't hurt 
anyway) to fix the theory.

> Setting the pointer to NULL probably silences a static checker warning
> and these days everyone automatically zeroes stack data so it doesn't
> affect the compiled code.

Agreed, but unless we have a explicit gcc flag to ask for that behavior 
(I've not checked if it is already the case), it looks like an UB for me.

> However generally we generally say that we
> should fix the checker instead.

In this particular case, the checker is just me, not an static analysis 
tool :).

I looked at this place because one of my coccinelle script spotted:

	/* allocate buffer_heads to read bitmaps */
	if (groups_per_page > 1) {
		i = sizeof(struct buffer_head *) * groups_per_page;
		bh = kzalloc(i, gfp);

as a candidate for kcalloc().

The rest of the story is just by reading the code around it.

> 
> I've thought about this in Smatch for a while, and I think what I would
> do is say that kmalloc() returns memory that is unique.  Smatch tracks if
> variables are equal to each other and unique variables wouldn't be equal
> to anything that came earlier.  But I haven't actually tried to implement
> this.
> 
> regards,
> dan carpenter
>
Dan Carpenter Jan. 7, 2025, 8:09 a.m. UTC | #3
Ah.  Sorry, I misunderstood the issue.  I can't say for sure whether the
bug is possible but it seems like a reasonable concern.

regards,
dan carpenter
diff mbox series

Patch

diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index b25a27c86696..ff9a124f439b 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -1285,7 +1285,7 @@  static int ext4_mb_init_cache(struct folio *folio, char *incore, gfp_t gfp)
 	ext4_group_t first_group, group;
 	int first_block;
 	struct super_block *sb;
-	struct buffer_head *bhs;
+	struct buffer_head *bhs = NULL;
 	struct buffer_head **bh = NULL;
 	struct inode *inode;
 	char *data;