From: yahia ahmed shmem_fault and shmem_writeout access inode->i_private without holding a lock, while shmem_fallocate is modifying it while holding a lock, thus a data-race is created. Fix this by using READ_ONCE and WRITE_ONCE, which provides an atomic, lockless read of inode->i_private which prevents compiler optimizations such as caching in registers and add writing to inode->i_private with WRITE_ONCE to prevent the compiler from writing in registers. Reported-by: syzbot+76cc716982cf0254f302@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=76cc716982cf0254f302 Signed-off-by: yahia ahmed --- v3: - Fix lockless read in shmem_writeout - Apply WRITE_ONCE and READ_ONCE across all locked instances of inode->i_private v2: - Add WRITE_ONCE to shmem_fallocate() --- mm/shmem.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mm/shmem.c b/mm/shmem.c index b51f83c970bb..1c39e9dfc524 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1657,10 +1657,10 @@ int shmem_writeout(struct folio *folio, struct swap_iocb **plug, * reactivate the folio, and let shmem_fallocate() quit when too many. */ if (!folio_test_uptodate(folio)) { - if (inode->i_private) { + if (READ_ONCE(inode->i_private)) { struct shmem_falloc *shmem_falloc; spin_lock(&inode->i_lock); - shmem_falloc = inode->i_private; + shmem_falloc = READ_ONCE(inode->i_private); if (shmem_falloc && !shmem_falloc->waitq && index >= shmem_falloc->start && @@ -2647,7 +2647,7 @@ static vm_fault_t shmem_falloc_wait(struct vm_fault *vmf, struct inode *inode) vm_fault_t ret = 0; spin_lock(&inode->i_lock); - shmem_falloc = inode->i_private; + shmem_falloc = READ_ONCE(inode->i_private); if (shmem_falloc && shmem_falloc->waitq && vmf->pgoff >= shmem_falloc->start && @@ -2693,7 +2693,7 @@ static vm_fault_t shmem_fault(struct vm_fault *vmf) * Trinity finds that probing a hole which tmpfs is punching can * prevent the hole-punch from ever completing: noted in i_private. */ - if (unlikely(inode->i_private)) { + if (unlikely(READ_ONCE(inode->i_private))) { ret = shmem_falloc_wait(vmf, inode); if (ret) return ret; @@ -3630,7 +3630,7 @@ static long shmem_fallocate(struct file *file, int mode, loff_t offset, shmem_falloc.start = (u64)unmap_start >> PAGE_SHIFT; shmem_falloc.next = (unmap_end + 1) >> PAGE_SHIFT; spin_lock(&inode->i_lock); - inode->i_private = &shmem_falloc; + WRITE_ONCE(inode->i_private, &shmem_falloc); spin_unlock(&inode->i_lock); if ((u64)unmap_end > (u64)unmap_start) @@ -3640,7 +3640,7 @@ static long shmem_fallocate(struct file *file, int mode, loff_t offset, /* No need to unmap again: hole-punching leaves COWed pages */ spin_lock(&inode->i_lock); - inode->i_private = NULL; + WRITE_ONCE(inode->i_private, NULL); wake_up_all(&shmem_falloc_waitq); WARN_ON_ONCE(!list_empty(&shmem_falloc_waitq.head)); spin_unlock(&inode->i_lock); @@ -3672,7 +3672,7 @@ static long shmem_fallocate(struct file *file, int mode, loff_t offset, shmem_falloc.nr_falloced = 0; shmem_falloc.nr_unswapped = 0; spin_lock(&inode->i_lock); - inode->i_private = &shmem_falloc; + WRITE_ONCE(inode->i_private, &shmem_falloc); spin_unlock(&inode->i_lock); /* @@ -3747,7 +3747,7 @@ static long shmem_fallocate(struct file *file, int mode, loff_t offset, i_size_write(inode, offset + len); undone: spin_lock(&inode->i_lock); - inode->i_private = NULL; + WRITE_ONCE(inode->i_private, NULL); spin_unlock(&inode->i_lock); out: if (!error) -- 2.54.0