The encode_dma() function has some validation on in_trans->size but it's not complete and it would be more clear to move those checks to find_and_map_user_pages(). The encode_dma() had two checks: if (in_trans->addr + in_trans->size < in_trans->addr || !in_trans->size) return -EINVAL; It's not sufficeint to just check if in_trans->size is zero. The resources->xferred_dma_size variable represent the number of bytes already transferred. If we have already transferred more bytes than in_trans->size then there are negative bytes remaining which doesn't make sense. Check for that as well. I introduced a new variable "remaining" which represents the amount we want to transfer (in_trans->size) minus the ammount we have already transferred (resources->xferred_dma_size). The check in encode_dma() checked that "addr + size" could not overflow however we may already have transferred some bytes so the real starting address is "xfer_start_addr" so check that "xfer_start_addr + size" cannot overflow instead. Also check that "addr + resources->xferred_dma_size cannot overflow. My other concern was that we are dealing with u64 values but on 32bit systems the kmalloc() function will truncate the sizes to 32 bits. So I calculated "total = in_trans->size + offset_in_page(xfer_start_addr);" and returned -EINVAL if it were >= SIZE_MAX. This will not affect 64bit systems. Fixes: 129776ac2e38 ("accel/qaic: Add control path") Signed-off-by: Dan Carpenter <dan.carpenter@xxxxxxxxxx> --- This is re-write re-write of the previous version. I am not necessarily sure it is correct. Please review carefully. In particular, please check how "total" is calculated. Maybe it would make more sense to write that as: total = remaining + offset_in_page(xfer_start_addr); The other question I had is should we add a check: if (remaining == 0) return 0; drivers/accel/qaic/qaic_control.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c index cfbc92da426f..d64505bcf4ae 100644 --- a/drivers/accel/qaic/qaic_control.c +++ b/drivers/accel/qaic/qaic_control.c @@ -392,18 +392,28 @@ static int find_and_map_user_pages(struct qaic_device *qdev, struct qaic_manage_trans_dma_xfer *in_trans, struct ioctl_resources *resources, struct dma_xfer *xfer) { + u64 xfer_start_addr, remaining, end, total; unsigned long need_pages; struct page **page_list; unsigned long nr_pages; struct sg_table *sgt; - u64 xfer_start_addr; int ret; int i; - xfer_start_addr = in_trans->addr + resources->xferred_dma_size; + if (check_add_overflow(in_trans->addr, resources->xferred_dma_size, &xfer_start_addr)) + return -EINVAL; + + if (in_trans->size == 0 || + in_trans->size < resources->xferred_dma_size || + check_add_overflow(xfer_start_addr, in_trans->size, &end)) + return -EINVAL; - need_pages = DIV_ROUND_UP(in_trans->size + offset_in_page(xfer_start_addr) - - resources->xferred_dma_size, PAGE_SIZE); + remaining = in_trans->size - resources->xferred_dma_size; + total = in_trans->size + offset_in_page(xfer_start_addr); + if (total >= SIZE_MAX) + return -EINVAL; + + need_pages = DIV_ROUND_UP(total - resources->xferred_dma_size, PAGE_SIZE); nr_pages = need_pages; @@ -435,7 +445,7 @@ static int find_and_map_user_pages(struct qaic_device *qdev, ret = sg_alloc_table_from_pages(sgt, page_list, nr_pages, offset_in_page(xfer_start_addr), - in_trans->size - resources->xferred_dma_size, GFP_KERNEL); + remaining, GFP_KERNEL); if (ret) { ret = -ENOMEM; goto free_sgt; @@ -566,9 +576,6 @@ static int encode_dma(struct qaic_device *qdev, void *trans, struct wrapper_list QAIC_MANAGE_EXT_MSG_LENGTH) return -ENOMEM; - if (in_trans->addr + in_trans->size < in_trans->addr || !in_trans->size) - return -EINVAL; - xfer = kmalloc(sizeof(*xfer), GFP_KERNEL); if (!xfer) return -ENOMEM; -- 2.39.2