Patchwork Fix corruption error in rh_alloc_fixed()

login
register
mail settings
Submitter Guillaume Knispel
Date Dec. 9, 2008, 2:28 p.m.
Message ID <20081209152834.1d6ff291@xilun.lan.proformatique.com>
Download mbox | patch
Permalink /patch/12983/
State Accepted
Commit af4d3643864ee5fcba0c97d77a424fa0b0346f8e
Delegated to: Kumar Gala
Headers show

Comments

Guillaume Knispel - Dec. 9, 2008, 2:28 p.m.
There is an error in rh_alloc_fixed() of the Remote Heap code:
If there is at least one free block blk won't be NULL at the end of the
search loop, so -ENOMEM won't be returned and the else branch of
"if (bs == s || be == e)" will be taken, corrupting the management
structures.

Signed-off-by: Guillaume Knispel <gknispel@proformatique.com>
---
Fix an error in rh_alloc_fixed() that made allocations succeed when
they should fail, and corrupted management structures.
Timur Tabi - Dec. 9, 2008, 3:03 p.m.
Guillaume Knispel wrote:
> There is an error in rh_alloc_fixed() of the Remote Heap code:
> If there is at least one free block blk won't be NULL at the end of the
> search loop, so -ENOMEM won't be returned and the else branch of
> "if (bs == s || be == e)" will be taken, corrupting the management
> structures.
> 
> Signed-off-by: Guillaume Knispel <gknispel@proformatique.com>
> ---
> Fix an error in rh_alloc_fixed() that made allocations succeed when
> they should fail, and corrupted management structures.
> 
> diff --git a/arch/powerpc/lib/rheap.c b/arch/powerpc/lib/rheap.c
> index 29b2941..45907c1 100644
> --- a/arch/powerpc/lib/rheap.c
> +++ b/arch/powerpc/lib/rheap.c
> @@ -556,6 +556,7 @@ unsigned long rh_alloc_fixed(rh_info_t * info, unsigned long start, int size, co
>  		be = blk->start + blk->size;
>  		if (s >= bs && e <= be)
>  			break;
> +		blk = NULL;
>  	}
>  
>  	if (blk == NULL)

This is a good catch, however, wouldn't it be better to do this:

	list_for_each(l, &info->free_list) {
		blk = list_entry(l, rh_block_t, list);
		/* The range must lie entirely inside one free block */
		bs = blk->start;
		be = blk->start + blk->size;
		if (s >= bs && e <= be)
			break;
	}

-	if (blk == NULL)
+	if (blk == &info->free_list)
		return (unsigned long) -ENOMEM;

I haven't tested this, but the if-statement at the end of the loop is meant to
check whether the list_for_each() loop got to the end or not.

What do you think?
Guillaume Knispel - Dec. 9, 2008, 3:14 p.m.
On Tue, 09 Dec 2008 09:03:19 -0600
Timur Tabi <timur@freescale.com> wrote:

> Guillaume Knispel wrote:
> > There is an error in rh_alloc_fixed() of the Remote Heap code:
> > If there is at least one free block blk won't be NULL at the end of the
> > search loop, so -ENOMEM won't be returned and the else branch of
> > "if (bs == s || be == e)" will be taken, corrupting the management
> > structures.
> > 
> > Signed-off-by: Guillaume Knispel <gknispel@proformatique.com>
> > ---
> > Fix an error in rh_alloc_fixed() that made allocations succeed when
> > they should fail, and corrupted management structures.
> > 
> > diff --git a/arch/powerpc/lib/rheap.c b/arch/powerpc/lib/rheap.c
> > index 29b2941..45907c1 100644
> > --- a/arch/powerpc/lib/rheap.c
> > +++ b/arch/powerpc/lib/rheap.c
> > @@ -556,6 +556,7 @@ unsigned long rh_alloc_fixed(rh_info_t * info, unsigned long start, int size, co
> >  		be = blk->start + blk->size;
> >  		if (s >= bs && e <= be)
> >  			break;
> > +		blk = NULL;
> >  	}
> >  
> >  	if (blk == NULL)
> 
> This is a good catch, however, wouldn't it be better to do this:
> 
> 	list_for_each(l, &info->free_list) {
> 		blk = list_entry(l, rh_block_t, list);
> 		/* The range must lie entirely inside one free block */
> 		bs = blk->start;
> 		be = blk->start + blk->size;
> 		if (s >= bs && e <= be)
> 			break;
> 	}
> 
> -	if (blk == NULL)
> +	if (blk == &info->free_list)
> 		return (unsigned long) -ENOMEM;
> 
> I haven't tested this, but the if-statement at the end of the loop is meant to
> check whether the list_for_each() loop got to the end or not.
> 
> What do you think?

blk = NULL; at the end of the loop is what is done in the more used
rh_alloc_align(), so for consistency either we change both or we use
the same construction here.
I also think that testing for &info->free_list is harder to understand
because you must have the linked list implementation in your head
(which a kernel developer should anyway so this is not so important)

Guillaume Knispel
Timur Tabi - Dec. 9, 2008, 3:16 p.m.
Guillaume Knispel wrote:

> blk = NULL; at the end of the loop is what is done in the more used
> rh_alloc_align(), so for consistency either we change both or we use
> the same construction here.
> I also think that testing for &info->free_list is harder to understand
> because you must have the linked list implementation in your head
> (which a kernel developer should anyway so this is not so important)

Fair enough.

Acked-by: Timur Tabi <timur@freescale.com>
Kumar Gala - Dec. 17, 2008, 4:11 p.m.
On Dec 9, 2008, at 8:28 AM, Guillaume Knispel wrote:

> There is an error in rh_alloc_fixed() of the Remote Heap code:
> If there is at least one free block blk won't be NULL at the end of  
> the
> search loop, so -ENOMEM won't be returned and the else branch of
> "if (bs == s || be == e)" will be taken, corrupting the management
> structures.
>
> Signed-off-by: Guillaume Knispel <gknispel@proformatique.com>
> ---
> Fix an error in rh_alloc_fixed() that made allocations succeed when
> they should fail, and corrupted management structures.
>
> diff --git a/arch/powerpc/lib/rheap.c b/arch/powerpc/lib/rheap.c
> index 29b2941..45907c1 100644
> --- a/arch/powerpc/lib/rheap.c
> +++ b/arch/powerpc/lib/rheap.c
> @@ -556,6 +556,7 @@ unsigned long rh_alloc_fixed(rh_info_t * info,  
> unsigned long start, int size, co
> 		be = blk->start + blk->size;
> 		if (s >= bs && e <= be)
> 			break;
> +		blk = NULL;
> 	}
>
> 	if (blk == NULL)

applied

- k

Patch

diff --git a/arch/powerpc/lib/rheap.c b/arch/powerpc/lib/rheap.c
index 29b2941..45907c1 100644
--- a/arch/powerpc/lib/rheap.c
+++ b/arch/powerpc/lib/rheap.c
@@ -556,6 +556,7 @@  unsigned long rh_alloc_fixed(rh_info_t * info, unsigned long start, int size, co
 		be = blk->start + blk->size;
 		if (s >= bs && e <= be)
 			break;
+		blk = NULL;
 	}
 
 	if (blk == NULL)