Patchwork [v3] nand: nand_base: Always initialise oob_poi before writing OOB data

login
register
mail settings
Submitter THOMSON, Adam (Adam)
Date June 14, 2011, 2:51 p.m.
Message ID <E14CAA9D00C5044EACE0C59FC2A1C7F5059AACE12B@FRMRSSXCHMBSC1.dc-m.alcatel-lucent.com>
Download mbox | patch
Permalink /patch/100352/
State New
Headers show

Comments

THOMSON, Adam (Adam) - June 14, 2011, 2:51 p.m.
In nand_do_write_ops() code it is possible for a caller to provide
ops.oobbuf populated and ops.mode == MTD_OOB_AUTO, which currently
means that the chip->oob_poi buffer isn't initialised to all 0xFF.
The nand_fill_oob() method then carries out the task of copying
the provided OOB data to oob_poi, but with MTD_OOB_AUTO it skips
areas marked as unavailable by the layout struct, including the
bad block marker bytes.

An example of this causing issues is when the last OOB data read
was from the start of a bad block where the markers are not 0xFF,
and the caller wishes to write new OOB data at the beginning of
another block. In this scenario the caller would provide OOB data,
but nand_fill_oob() would skip the bad block marker bytes in
oob_poi before copying the OOB data provided by the caller.
This means that when the OOB data is written back to NAND,
the block is inadvertently marked as bad without the caller knowing.
This has been witnessed when using YAFFS2 where tags are stored
in the OOB.

To avoid this oob_poi is always initialised to 0xFF to make sure
no left over data is inadvertently written back to the OOB area.

Signed-off-by: Adam Thomson <adam.thomson@alcatel-lucent.com>
Cc: stable@kernel.org [2.6.20+]
---

Applies to: 2.6.20+ stable releases
Tested on: ARM based board with Hynix 256MB NAND chip

 drivers/mtd/nand/nand_base.c |   20 +++++++++++---------
 1 files changed, 11 insertions(+), 9 deletions(-)

Patch

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index dfe56e0..137dcb9 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1545,14 +1545,21 @@  static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
 /**
  * nand_fill_oob - [Internal] Transfer client buffer to oob
  * @chip:	nand chip structure
+ * @mtd:	MTD device structure
  * @oob:	oob data buffer
  * @ops:	oob ops structure
  */
-static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,
-				  struct mtd_oob_ops *ops)
+static uint8_t *nand_fill_oob(struct nand_chip *chip, struct mtd_info *mtd,
+			      uint8_t *oob, struct mtd_oob_ops *ops)
 {
 	size_t len = ops->ooblen;
 
+	/*
+	 * Initialise to all 0xFF, to avoid the possibility of
+	 * left over OOB data from a previous OOB read.
+	 */
+	memset(chip->oob_poi, 0xff, mtd->oobsize);
+
 	switch(ops->mode) {
 
 	case MTD_OOB_PLACE:
@@ -1644,10 +1651,6 @@  static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
 	    (chip->pagebuf << chip->page_shift) < (to + ops->len))
 		chip->pagebuf = -1;
 
-	/* If we're not given explicit OOB data, let it be 0xFF */
-	if (likely(!oob))
-		memset(chip->oob_poi, 0xff, mtd->oobsize);
-
 	while(1) {
 		int bytes = mtd->writesize;
 		int cached = writelen > bytes && page != blockmask;
@@ -1664,7 +1667,7 @@  static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
 		}
 
 		if (unlikely(oob))
-			oob = nand_fill_oob(chip, oob, ops);
+			oob = nand_fill_oob(chip, mtd, oob, ops);
 
 		ret = chip->write_page(mtd, chip, wbuf, page, cached,
 				       (ops->mode == MTD_OOB_RAW));
@@ -1777,8 +1780,7 @@  static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
 	if (page == chip->pagebuf)
 		chip->pagebuf = -1;
 
-	memset(chip->oob_poi, 0xff, mtd->oobsize);
-	nand_fill_oob(chip, ops->oobbuf, ops);
+	nand_fill_oob(chip, mtd, ops->oobbuf, ops);
 	status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
 	memset(chip->oob_poi, 0xff, mtd->oobsize);