diff options
Diffstat (limited to 'mm')
-rw-r--r-- | mm/filemap_xip.c | 30 | ||||
-rw-r--r-- | mm/memory.c | 1 |
2 files changed, 29 insertions, 2 deletions
diff --git a/mm/filemap_xip.c b/mm/filemap_xip.c index c175f9f25210..59e1c5585748 100644 --- a/mm/filemap_xip.c +++ b/mm/filemap_xip.c @@ -256,8 +256,20 @@ again: __xip_unmap(mapping, vmf->pgoff); found: + /* + * We must recheck i_size under i_mmap_rwsem to prevent races + * with truncation + */ + i_mmap_lock_read(mapping); + size = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> + PAGE_CACHE_SHIFT; + if (unlikely(vmf->pgoff >= size)) { + i_mmap_unlock_read(mapping); + return VM_FAULT_SIGBUS; + } err = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, xip_pfn); + i_mmap_unlock_read(mapping); if (err == -ENOMEM) return VM_FAULT_OOM; /* @@ -281,16 +293,30 @@ found: } if (error != -ENODATA) goto out; + + /* + * We must recheck i_size under i_mmap_rwsem to prevent races + * with truncation + */ + i_mmap_lock_read(mapping); + size = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> + PAGE_CACHE_SHIFT; + if (unlikely(vmf->pgoff >= size)) { + ret = VM_FAULT_SIGBUS; + goto unlock; + } /* not shared and writable, use xip_sparse_page() */ page = xip_sparse_page(); if (!page) - goto out; + goto unlock; err = vm_insert_page(vma, (unsigned long)vmf->virtual_address, page); if (err == -ENOMEM) - goto out; + goto unlock; ret = VM_FAULT_NOPAGE; +unlock: + i_mmap_unlock_read(mapping); out: write_seqcount_end(&xip_sparse_seq); mutex_unlock(&xip_sparse_mutex); diff --git a/mm/memory.c b/mm/memory.c index 99275325f303..1b04e13b9993 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2329,6 +2329,7 @@ void unmap_mapping_range(struct address_space *mapping, details.last_index = ULONG_MAX; + /* DAX uses i_mmap_lock to serialise file truncate vs page fault */ i_mmap_lock_write(mapping); if (unlikely(!RB_EMPTY_ROOT(&mapping->i_mmap))) unmap_mapping_range_tree(&mapping->i_mmap, &details); |