generic_file_splice_read() makes a couple of preliminary checks (for s_maxbytes and zero len), but shmem_file_splice_read() is called without those: so check them inside it. (But shmem does not support O_DIRECT, so no need for that one here - and even if O_DIRECT support were stubbed in, it would still just be using the page cache.) HWPoison: my reading of folio_test_hwpoison() is that it only tests the head page of a large folio, whereas splice_folio_into_pipe() will splice as much of the folio as it can: so for safety we should also check the has_hwpoisoned flag, set if any of the folio's pages are hwpoisoned. (Perhaps that ugliness can be improved at the mm end later.) The call to splice_zeropage_into_pipe() risked overrunning past EOF: ask it for "part" not "len". Fixes: b81d7b89becc ("shmem: Implement splice-read") Signed-off-by: Hugh Dickins <hughd@xxxxxxxxxx> --- Thank you, David, for attending to tmpfs in your splice update: yes, I too wish it could have just used the generic, but I'm sure you're right that there's a number of reasons it needs its own. mm/shmem.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2902,6 +2902,11 @@ static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos, loff_t isize; int error = 0; + if (unlikely(*ppos >= MAX_LFS_FILESIZE)) + return 0; + if (unlikely(!len)) + return 0; + /* Work out how much data we can actually add into the pipe */ used = pipe_occupancy(pipe->head, pipe->tail); npages = max_t(ssize_t, pipe->max_usage - used, 0); @@ -2911,7 +2916,8 @@ static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos, if (*ppos >= i_size_read(inode)) break; - error = shmem_get_folio(inode, *ppos / PAGE_SIZE, &folio, SGP_READ); + error = shmem_get_folio(inode, *ppos / PAGE_SIZE, &folio, + SGP_READ); if (error) { if (error == -EINVAL) error = 0; @@ -2920,7 +2926,9 @@ static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos, if (folio) { folio_unlock(folio); - if (folio_test_hwpoison(folio)) { + if (folio_test_hwpoison(folio) || + (folio_test_large(folio) && + folio_test_has_hwpoisoned(folio))) { error = -EIO; break; } @@ -2956,7 +2964,7 @@ static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos, folio_put(folio); folio = NULL; } else { - n = splice_zeropage_into_pipe(pipe, *ppos, len); + n = splice_zeropage_into_pipe(pipe, *ppos, part); } if (!n)