Patchwork physmap: Fix leak of memory returned by parse_mtd_partitions

login
register
mail settings
Submitter Sascha Hauer
Date Feb. 24, 2009, 1:35 p.m.
Message ID <20090224133505.GJ21900@pengutronix.de>
Download mbox | patch
Permalink /patch/23617/
State New
Headers show

Comments

Sascha Hauer - Feb. 24, 2009, 1:35 p.m.
Hi,

Sorry to reply to such an old thread, but...

On Wed, Nov 12, 2008 at 11:57:33PM +0900, Atsushi Nemoto wrote:
> The mtd partition parser returns an allocated pointer array of
> mtd_partition.  The caller must free it.  The array is used only for
> add_mtd_partitions(), so free it just after the call.

This patch breaks command line parsing support. With command line
partition parsing the struct mtd_partition array is allocated, but only
once. On my board with NAND and NOR (both with command line partition
parsing) It fails badly in parse_cmdline_partitions() when the second
device gets parsed.

The following patch fixes it, but I don't know if this is
the correct solution. Does anybody have more insights on this?

Sascha


> 
> Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp>
> ---
>  drivers/mtd/maps/physmap.c |   17 ++++++++---------
>  1 files changed, 8 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c
> index 7ca048d..cc26b41 100644
> --- a/drivers/mtd/maps/physmap.c
> +++ b/drivers/mtd/maps/physmap.c
> @@ -29,7 +29,6 @@ struct physmap_flash_info {
>  	struct map_info		map[MAX_RESOURCES];
>  #ifdef CONFIG_MTD_PARTITIONS
>  	int			nr_parts;
> -	struct mtd_partition	*parts;
>  #endif
>  };
>  
> @@ -56,14 +55,10 @@ static int physmap_flash_remove(struct platform_device *dev)
>  	for (i = 0; i < MAX_RESOURCES; i++) {
>  		if (info->mtd[i] != NULL) {
>  #ifdef CONFIG_MTD_PARTITIONS
> -			if (info->nr_parts) {
> +			if (info->nr_parts || physmap_data->nr_parts)
>  				del_mtd_partitions(info->mtd[i]);
> -				kfree(info->parts);
> -			} else if (physmap_data->nr_parts) {
> -				del_mtd_partitions(info->mtd[i]);
> -			} else {
> +			else
>  				del_mtd_device(info->mtd[i]);
> -			}
>  #else
>  			del_mtd_device(info->mtd[i]);
>  #endif
> @@ -86,6 +81,9 @@ static int physmap_flash_probe(struct platform_device *dev)
>  	int err = 0;
>  	int i;
>  	int devices_found = 0;
> +#ifdef CONFIG_MTD_PARTITIONS
> +	struct mtd_partition *parts;
> +#endif
>  
>  	physmap_data = dev->dev.platform_data;
>  	if (physmap_data == NULL)
> @@ -166,9 +164,10 @@ static int physmap_flash_probe(struct platform_device *dev)
>  		goto err_out;
>  
>  #ifdef CONFIG_MTD_PARTITIONS
> -	err = parse_mtd_partitions(info->cmtd, part_probe_types, &info->parts, 0);
> +	err = parse_mtd_partitions(info->cmtd, part_probe_types, &parts, 0);
>  	if (err > 0) {
> -		add_mtd_partitions(info->cmtd, info->parts, err);
> +		add_mtd_partitions(info->cmtd, parts, err);
> +		kfree(parts);
>  		return 0;
>  	}
>  
> -- 
> 1.5.6.3
> 
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
>
Atsushi Nemoto - Feb. 24, 2009, 2:36 p.m.
On Tue, 24 Feb 2009 14:35:05 +0100, Sascha Hauer <s.hauer@pengutronix.de> wrote:
> On Wed, Nov 12, 2008 at 11:57:33PM +0900, Atsushi Nemoto wrote:
> > The mtd partition parser returns an allocated pointer array of
> > mtd_partition.  The caller must free it.  The array is used only for
> > add_mtd_partitions(), so free it just after the call.
> 
> This patch breaks command line parsing support. With command line
> partition parsing the struct mtd_partition array is allocated, but only
> once. On my board with NAND and NOR (both with command line partition
> parsing) It fails badly in parse_cmdline_partitions() when the second
> device gets parsed.
> 
> The following patch fixes it, but I don't know if this is
> the correct solution. Does anybody have more insights on this?

Do your NAND and NOR have same mtd-id?  The cmdlinepart allocates
mtd_partition aray for each mtd-id.  So usually another array will be
returned for NAND and NOR.

The physmap patch has another bug and fixes are on the way mainline:

http://git.infradead.org/mtd-2.6.git?a=commit;h=e480814f138cd5d78a8efe397756ba6b6518fdb6

But this seems not enough, as you wrote.  If multiple mtd have same
mtd-id, bad things can happen.  And more seriously, if I load physmap
driver _again_ after unload, cmdlinepart will return a freed pointer
on the second time.

Hmm, little memory leak is less serious than crash.  Now I start
thinking reverting the commit 176bf2e0 will be best for 2.6.29
release.

I'm not sure for long term solutions.

A) make all parsers return kmalloc-ed mtd_partition array each time
   and fix memory leak in each driver

B) make all parsers return mtd_partition array allocated only once,
   and fix drivers which free the mtd_partition array.

David, how do you think?

---
Atsushi Nemoto
Sascha Hauer - Feb. 24, 2009, 3:29 p.m.
On Tue, Feb 24, 2009 at 11:36:00PM +0900, Atsushi Nemoto wrote:
> On Tue, 24 Feb 2009 14:35:05 +0100, Sascha Hauer <s.hauer@pengutronix.de> wrote:
> > On Wed, Nov 12, 2008 at 11:57:33PM +0900, Atsushi Nemoto wrote:
> > > The mtd partition parser returns an allocated pointer array of
> > > mtd_partition.  The caller must free it.  The array is used only for
> > > add_mtd_partitions(), so free it just after the call.
> > 
> > This patch breaks command line parsing support. With command line
> > partition parsing the struct mtd_partition array is allocated, but only
> > once. On my board with NAND and NOR (both with command line partition
> > parsing) It fails badly in parse_cmdline_partitions() when the second
> > device gets parsed.
> > 
> > The following patch fixes it, but I don't know if this is
> > the correct solution. Does anybody have more insights on this?
> 
> Do your NAND and NOR have same mtd-id?  The cmdlinepart allocates
> mtd_partition aray for each mtd-id.  So usually another array will be
> returned for NAND and NOR.

Yes, it allocates one array for NAND and one for NOR. The problem starts
in mtdpart_setup_real():

>		struct cmdline_mtd_partition *this_mtd;
> 		struct mtd_partition *parts;
> 
> 		[...]
> 
> 		/*
> 		 * parse one mtd. have it reserve memory for the
> 		 * struct cmdline_mtd_partition and the mtd-id string.
> 		 */
> 		parts = newpart(p + 1,		/* cmdline */
> 				&s,		/* out: updated cmdline ptr */
> 				&num_parts,	/* out: number of parts */
> 				0,		/* first partition */
> 				(unsigned char**)&this_mtd, /* out: extra mem */
> 				mtd_id_len + 1 + sizeof(*this_mtd) +
> 				sizeof(void*)-1 /*alignment*/);
>

Here we allocate the array containing not only the struct mtd_partition,
but also the names and the struct cmdline_mtd_partition...

> 	for(part = partitions; part; part = part->next)
> 	{
> 		if ((!mtd_id) || (!strcmp(part->mtd_id, mtd_id)))
> 		{
> 			for(i = 0, offset = 0; i < part->num_parts; i++)
> 			{
> 				if (part->parts[i].offset == OFFSET_CONTINUOUS)
> 				  part->parts[i].offset = offset;
> 				else
> 				  offset = part->parts[i].offset;
> 				if (part->parts[i].size == SIZE_REMAINING)
> 				  part->parts[i].size = master->size - offset;
> 				if (offset + part->parts[i].size > master->size)
> 				{
> 					printk(KERN_WARNING ERRP
> 					       "%s: partitioning exceeds flash size, truncating\n",
> 					       part->mtd_id);
> 					part->parts[i].size = master->size - offset;
> 					part->num_parts = i;
> 				}
> 				offset += part->parts[i].size;
> 			}
> 			*pparts = part->parts;

...here this array is returned and kfreed in the physmap flash driver. The
next time we enter this loop 'part' points to already freed memory.

*pparts is not meant to be freed, cmdlinepart.c needs it for the whole
kernel life. We can only return a copy of the partition array.

 
> 
> The physmap patch has another bug and fixes are on the way mainline:
> 
> http://git.infradead.org/mtd-2.6.git?a=commit;h=e480814f138cd5d78a8efe397756ba6b6518fdb6
> 
> But this seems not enough, as you wrote.  If multiple mtd have same
> mtd-id, bad things can happen.  And more seriously, if I load physmap
> driver _again_ after unload, cmdlinepart will return a freed pointer
> on the second time.
> 
> Hmm, little memory leak is less serious than crash.  Now I start
> thinking reverting the commit 176bf2e0 will be best for 2.6.29
> release.

Even when reverting the commit the same problem still exists because the
array then gets freed in physmap_flash_remove(). This won't hurt me
though because I never use mtd drivers as modules.

Sascha
Atsushi Nemoto - Feb. 25, 2009, 1:31 a.m.
On Tue, 24 Feb 2009 16:29:58 +0100, Sascha Hauer <s.hauer@pengutronix.de> wrote:
> > The physmap patch has another bug and fixes are on the way mainline:
> > 
> > http://git.infradead.org/mtd-2.6.git?a=commit;h=e480814f138cd5d78a8efe397756ba6b6518fdb6
> > 
> > But this seems not enough, as you wrote.  If multiple mtd have same
> > mtd-id, bad things can happen.  And more seriously, if I load physmap
> > driver _again_ after unload, cmdlinepart will return a freed pointer
> > on the second time.
> > 
> > Hmm, little memory leak is less serious than crash.  Now I start
> > thinking reverting the commit 176bf2e0 will be best for 2.6.29
> > release.
> 
> Even when reverting the commit the same problem still exists because the
> array then gets freed in physmap_flash_remove(). This won't hurt me
> though because I never use mtd drivers as modules.

If the commit reverted, kfree() in physmap_flash_remove never be
called due to another bug (info->nr_parts is not set properly).  But
unloading the physmap module will lead crash anyway since master mtd
device will be freed without deleting slave mtd devices if cmdlinepart
was used.

So I think either reverting the commit or applying the above fix in
mtd-2.6 git tree can fix regression from 2.6.28.  Both work well
unless unloading the physmap module after booting with mtdparts=
option.

---
Atsushi Nemoto

Patch

diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c
index 50a3403..14c00dd 100644
--- a/drivers/mtd/cmdlinepart.c
+++ b/drivers/mtd/cmdlinepart.c
@@ -335,7 +335,13 @@  static int parse_cmdline_partitions(struct mtd_info *master,
 				}
 				offset += part->parts[i].size;
 			}
-			*pparts = part->parts;
+
+			*pparts = kmalloc(sizeof (struct mtd_partition) * part->num_parts, GFP_KERNEL);
+			if (!*pparts)
+				return -ENOMEM;
+
+			memcpy(*pparts, part->parts, sizeof (struct mtd_partition) * part->num_parts);
+
 			return part->num_parts;
 		}
 	}