summaryrefslogtreecommitdiff
path: root/drivers/mtd
diff options
context:
space:
mode:
authorBoris Brezillon <boris.brezillon@free-electrons.com>2018-08-16 17:30:01 +0200
committerJagan Teki <jagan@amarulasolutions.com>2018-09-20 20:10:49 +0530
commit8fad769f1eab88a2bcf0b714694158968f58715a (patch)
tree1ba5bf66ac3327c1365de7844269ad4060217d56 /drivers/mtd
parent5f50d82d8918b711717b4bbd96c6f348eb6e2a2c (diff)
mtd: Add sanity checks in mtd_write/read_oob()
Unlike what's done in mtd_read/write(), there are no checks to make sure the parameters passed to mtd_read/write_oob() are consistent, which forces implementers of ->_read/write_oob() to do it, which in turn leads to code duplication and possibly errors in the logic. Do general sanity checks, like ops fields consistency and range checking. Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com> Cc: Peter Pan <peterpandong@micron.com> Signed-off-by: Richard Weinberger <richard@nod.at> [Miquel: squashed the fix about the chip's size check] Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/mtdcore.c45
1 files changed, 45 insertions, 0 deletions
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index ad61406dac..9a3efe95df 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -1010,12 +1010,50 @@ int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
}
EXPORT_SYMBOL_GPL(mtd_panic_write);
+static int mtd_check_oob_ops(struct mtd_info *mtd, loff_t offs,
+ struct mtd_oob_ops *ops)
+{
+ /*
+ * Some users are setting ->datbuf or ->oobbuf to NULL, but are leaving
+ * ->len or ->ooblen uninitialized. Force ->len and ->ooblen to 0 in
+ * this case.
+ */
+ if (!ops->datbuf)
+ ops->len = 0;
+
+ if (!ops->oobbuf)
+ ops->ooblen = 0;
+
+ if (offs < 0 || offs + ops->len > mtd->size)
+ return -EINVAL;
+
+ if (ops->ooblen) {
+ u64 maxooblen;
+
+ if (ops->ooboffs >= mtd_oobavail(mtd, ops))
+ return -EINVAL;
+
+ maxooblen = ((mtd_div_by_ws(mtd->size, mtd) -
+ mtd_div_by_ws(offs, mtd)) *
+ mtd_oobavail(mtd, ops)) - ops->ooboffs;
+ if (ops->ooblen > maxooblen)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
{
int ret_code;
ops->retlen = ops->oobretlen = 0;
if (!mtd->_read_oob)
return -EOPNOTSUPP;
+
+ ret_code = mtd_check_oob_ops(mtd, from, ops);
+ if (ret_code)
+ return ret_code;
+
/*
* In cases where ops->datbuf != NULL, mtd->_read_oob() has semantics
* similar to mtd->_read(), returning a non-negative integer
@@ -1034,11 +1072,18 @@ EXPORT_SYMBOL_GPL(mtd_read_oob);
int mtd_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
+ int ret;
+
ops->retlen = ops->oobretlen = 0;
if (!mtd->_write_oob)
return -EOPNOTSUPP;
if (!(mtd->flags & MTD_WRITEABLE))
return -EROFS;
+
+ ret = mtd_check_oob_ops(mtd, to, ops);
+ if (ret)
+ return ret;
+
return mtd->_write_oob(mtd, to, ops);
}
EXPORT_SYMBOL_GPL(mtd_write_oob);