MFC: r258387,r258425

Split raw reading/programming into smaller chunks to avoid allocating too
big chunk of kernel memory. Validate size of data. Add error handling to
avoid calling copyout() when data has not been read correctly. Also MFC of
change r258425 which fixes problem introduced by r258387.

Reviewed by:    zbb
Reported by:    x90c <geinblues@gmail.com>
Approved by:    re
This commit is contained in:
gber 2013-11-25 15:34:57 +00:00
parent 484ab7255a
commit 476fa15d40
2 changed files with 102 additions and 18 deletions

View File

@ -294,19 +294,40 @@ nand_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
struct thread *td)
{
struct nand_chip *chip;
struct chip_geom *cg;
struct nand_oob_rw *oob_rw = NULL;
struct nand_raw_rw *raw_rw = NULL;
device_t nandbus;
size_t bufsize = 0, len = 0;
size_t raw_size;
off_t off;
uint8_t *buf = NULL;
int ret = 0;
uint8_t status;
chip = (struct nand_chip *)dev->si_drv1;
cg = &chip->chip_geom;
nandbus = device_get_parent(chip->dev);
if ((cmd == NAND_IO_RAW_READ) || (cmd == NAND_IO_RAW_PROG)) {
raw_rw = (struct nand_raw_rw *)data;
buf = malloc(raw_rw->len, M_NAND, M_WAITOK);
raw_size = cg->pgs_per_blk * (cg->page_size + cg->oob_size);
/* Check if len is not bigger than chip size */
if (raw_rw->len > raw_size)
return (EFBIG);
/*
* Do not ask for too much memory, in case of large transfers
* read/write in 16-pages chunks
*/
bufsize = 16 * (cg->page_size + cg->oob_size);
if (raw_rw->len < bufsize)
bufsize = raw_rw->len;
buf = malloc(bufsize, M_NAND, M_WAITOK);
len = raw_rw->len;
off = 0;
}
switch(cmd) {
case NAND_IO_ERASE:
@ -335,19 +356,37 @@ nand_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
break;
case NAND_IO_RAW_PROG:
ret = copyin(raw_rw->data, buf, raw_rw->len);
if (ret)
break;
ret = nand_prog_pages_raw(chip, raw_rw->off, buf,
raw_rw->len);
while (len > 0) {
if (len < bufsize)
bufsize = len;
ret = copyin(raw_rw->data + off, buf, bufsize);
if (ret)
break;
ret = nand_prog_pages_raw(chip, raw_rw->off + off, buf,
bufsize);
if (ret)
break;
len -= bufsize;
off += bufsize;
}
break;
case NAND_IO_RAW_READ:
ret = nand_read_pages_raw(chip, raw_rw->off, buf,
raw_rw->len);
if (ret)
break;
ret = copyout(buf, raw_rw->data, raw_rw->len);
while (len > 0) {
if (len < bufsize)
bufsize = len;
ret = nand_read_pages_raw(chip, raw_rw->off + off, buf,
bufsize);
if (ret)
break;
ret = copyout(buf, raw_rw->data + off, bufsize);
if (ret)
break;
len -= bufsize;
off += bufsize;
}
break;
case NAND_IO_PAGE_STAT:

View File

@ -193,20 +193,42 @@ nand_ioctl(struct disk *ndisk, u_long cmd, void *data, int fflag,
struct thread *td)
{
struct nand_chip *chip;
struct chip_geom *cg;
struct nand_oob_rw *oob_rw = NULL;
struct nand_raw_rw *raw_rw = NULL;
device_t nandbus;
size_t bufsize = 0, len = 0;
size_t raw_size;
off_t off;
uint8_t *buf = NULL;
int ret = 0;
uint8_t status;
chip = (struct nand_chip *)ndisk->d_drv1;
cg = &chip->chip_geom;
nandbus = device_get_parent(chip->dev);
if ((cmd == NAND_IO_RAW_READ) || (cmd == NAND_IO_RAW_PROG)) {
raw_rw = (struct nand_raw_rw *)data;
buf = malloc(raw_rw->len, M_NAND, M_WAITOK);
raw_size = cg->pgs_per_blk * (cg->page_size + cg->oob_size);
/* Check if len is not bigger than chip size */
if (raw_rw->len > raw_size)
return (EFBIG);
/*
* Do not ask for too much memory, in case of large transfers
* read/write in 16-pages chunks
*/
bufsize = 16 * (cg->page_size + cg->oob_size);
if (raw_rw->len < bufsize)
bufsize = raw_rw->len;
buf = malloc(bufsize, M_NAND, M_WAITOK);
len = raw_rw->len;
off = 0;
}
switch (cmd) {
case NAND_IO_ERASE:
ret = nand_erase_blocks(chip, ((off_t *)data)[0],
@ -234,15 +256,38 @@ nand_ioctl(struct disk *ndisk, u_long cmd, void *data, int fflag,
break;
case NAND_IO_RAW_PROG:
copyin(raw_rw->data, buf, raw_rw->len);
ret = nand_prog_pages_raw(chip, raw_rw->off, buf,
raw_rw->len);
while (len > 0) {
if (len < bufsize)
bufsize = len;
ret = copyin(raw_rw->data + off, buf, bufsize);
if (ret)
break;
ret = nand_prog_pages_raw(chip, raw_rw->off + off, buf,
bufsize);
if (ret)
break;
len -= bufsize;
off += bufsize;
}
break;
case NAND_IO_RAW_READ:
ret = nand_read_pages_raw(chip, raw_rw->off, buf,
raw_rw->len);
copyout(buf, raw_rw->data, raw_rw->len);
while (len > 0) {
if (len < bufsize)
bufsize = len;
ret = nand_read_pages_raw(chip, raw_rw->off + off, buf,
bufsize);
if (ret)
break;
ret = copyout(buf, raw_rw->data + off, bufsize);
if (ret)
break;
len -= bufsize;
off += bufsize;
}
break;
case NAND_IO_GET_CHIP_PARAM: